-
Notifications
You must be signed in to change notification settings - Fork 320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Slight rethinking of Wallet
constructors (breaking change)
#1500
Slight rethinking of Wallet
constructors (breaking change)
#1500
Conversation
This PR removes the new_or_load Wallet constructor and requires users to know whether they intend to load a wallet or create a new one.
…ad_no_priv This new name makes it explicit that the wallet is being loaded without private keys. The old name implied there was another way to load a wallet than with changesets, which is not the case.
89b4b1a
to
24fa925
Compare
I agree this API doesn't make sense anymore after the persistence backend was remove. IMO there should just be a single You can have methods on I'd do this in a PR that moves |
Ok this is quite a departure from what we have. My initial reaction was to think that it might be odd to have to users dig into the idea of changesets before they've even made a first wallet ("darn, surely this Wallet::new method could create what I need internally?" says the newbie), but after sitting on it for 30 minutes I think you can't get away from changesets anyway, so might as well use them to their fullest. It's more refactoring than I have here so before I get going on this it might be nice to get a Concept ACK from a few other devs on the team, but I'm up for it if that appears to be the cleanest and better way to move forward. A few questions
pub indexed_tx_graph: crate::indexed_tx_graph::ChangeSet<A, crate::keychain::ChangeSet<K>>, with the keychain::ChangeSet pub struct ChangeSet<K> {
/// Contains the keychains that have been added and their respective descriptor
pub keychains_added: BTreeMap<K, Descriptor<DescriptorPublicKey>>,
/// Contains for each descriptor_id the last revealed index of derivation
pub last_revealed: BTreeMap<DescriptorId, u32>,
} Did I misunderstand your idea or maybe misunderstand the type and it does in fact have the descriptors somewhere in there that the wallet can then use to populate signers?
|
I'm warming up to this idea for simplifying the |
I tend to agree it seems onerous to place handling the changeset structure before creating a wallet. But I also don't hate the idea of having one fn new(desc: E, change_desc: E, network: Network) {}
fn new_with_genesis_hash(desc: E, change_desc: E, network: Network, gen: BlockHash) {}
fn load(cs: ChangeSet) {}
fn load_with_descriptors(cs: ChangeSet, desc: E, change_desc: E) {} With |
@ValuedMammal your approach is simpler for users and with docs and example should be easy to explain which |
I like @ValuedMammal's solution. However, I would have What if you want to create custom signers? You can still add signers later on with |
I'd rather not make another big change to an API that I'd be happy to see a complete |
In #1514, I've introduced a builder API for constructing a // Load a wallet that is persisted to SQLite database.
let mut conn = bdk_wallet::rusqlite::Connection::open(file_path)?;
let mut wallet = Wallet::load()
// check loaded descriptors matches these values and extract private keys
.descriptors(EXTERNAL_DESC, INTERNAL_DESC)
// you can also manually add private keys
.keymap(KeychainKind::External, external_keymap)
.keymap(KeychainKind::Internal, internal_keymap)
// ensure loaded wallet's genesis hash matches this value
.genesis_hash(genesis_hash)
// set a lookahead for our indexer
.lookahead(101)
.load_wallet(&mut conn)?
.expect("must have data to load wallet");
// Create a non-persisted wallet.
let wallet = Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
.network(Network::Testnet)
.create_wallet_no_persist()?; |
closing in favor of #1514 |
…nstruction 64eb576 chore(wallet): Fix ChangeSet::merge (LLFourn) 8875c92 chore(wallet): Fix descriptor mismatch error keychain (LLFourn) 2cf07d6 refactor(chain,wallet)!: move rusqlite things into it's own file (志宇) 93f9b83 chore(chain): rm unused `sqlite` types (志宇) 892b97d feat(chain,wallet)!: Change persist-traits to be "safer" (志宇) 3aed4cf test(wallet): ensure checks work when loading wallet (志宇) af4ee0f refactor(wallet)!: Make `bdk_wallet::ChangeSet` non-exhaustive (志宇) 22d02ed feat!: improve wallet building methods (志宇) eb73f06 refactor!: move `WalletChangeSet` to `bdk_wallet` and fix import paths (志宇) 6b43001 feat!: Rework sqlite, changesets, persistence and wallet-construction (志宇) Pull request description: Closes #1496 Closes #1498 Closes #1500 ### Description Rework sqlite: Instead of only supported one schema (defined in `bdk_sqlite`), we have a schema per changeset type for more flexiblity. * rm `bdk_sqlite` crate (as we don't need `bdk_sqlite::Store` anymore). * add `sqlite` feature on `bdk_chain` which adds methods on each changeset type for initializing tables, loading the changeset and writing. Rework changesets: Some callers may want to use `KeychainTxOutIndex` where `K` may change per descriptor on every run. So we only want to persist the last revealed indices by `DescriptorId` (which uniquely-ish identifies the descriptor). * rm `keychain_added` field from `keychain_txout`'s changeset. * Add `keychain_added` to `CombinedChangeSet` (which is renamed to `WalletChangeSet`). Rework persistence: add back some safety and convenience when persisting our types. Working with changeset directly (as we were doing before) can be cumbersome. * Intoduce `struct Persisted<T>` which wraps a type `T` which stores staged changes to it. This adds safety when creating and or loading `T` from db. * `struct Persisted<T>` methods, `create`, `load` and `persist`, are available if `trait PersistWith<Db>` is implemented for `T`. `Db` represents the database connection and `PersistWith` should be implemented per database-type. * For async, we have `trait PersistedAsyncWith<Db>`. * `Wallet` has impls of `PersistedWith<rusqlite::Connection>`, `PersistedWith<rusqlite::Transaction>` and `PersistedWith<bdk_file_store::Store>` by default. Rework wallet-construction: Before, we had multiple methods for loading and creating with different input-counts so it would be unwieldly to add more parameters in the future. This also makes it difficult to impl `PersistWith` (which has a single method for `load` that takes in `PersistWith::LoadParams` and a single method for `create` that takes in `PersistWith::CreateParams`). * Introduce a builder pattern when constructing a `Wallet`. For loading from persistence or `ChangeSet`, we have `LoadParams`. For creating a new wallet, we have `CreateParams`. ### Notes to the reviewers TODO ### Changelog notice ``` ### Added - Add `sqlite` feature to `bdk_chain` which introduces methods on changeset types that encode/decode changesets to SQLite database. * Introduce `PersistWith<Db>` and `PersistAsyncWith<Db>` traits and a `Persisted` wrapper. This ergonomically makes sure user inits the db before reading/writing to it. ### Changed - Moved `bdk_chain::CombinedChangeSet` to `bdk_wallet::ChangeSet` and added `keychain_added` field. - `bdk_wallet::Wallet` construction now uses a builder API using the newly introduced `CreateParams` and `LoadParams`. ### Removed - Remove `keychains_added` field from `bdk_chain::keychain_txout::ChangeSet`. ``` ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [ ] I've added tests for the new feature * [x] I've added docs for the new feature ACKs for top commit: LLFourn: ACK: 64eb576 notmandatory: Re ACK 64eb576 Tree-SHA512: b8a1d48aea26d9fa293a8387a3533cd16c8ddae890f94d61fb91efa492fb05ac5e0a66200d64d7c857774368d5f0f8991a98684307029c25f50a1d8fceee8e67
Why This PR
Our current "constructors" on the
Wallet
type are as follow:This PR proposes a revised set of constructors offering clearer boundaries between them and their intended use:
A few observations on our current set of constructors and what led me to propose this PR:
Wallet.new_or_load()
. So... do you want to create a new wallet or load an existing one? It feels like you should know. If you don't know whether you're about to create a new wallet or load one from persistence, to me your issue lies upstream of this line in your code (but maybe there is a use case I'm not thinking of? let me know).new_or_load_with_genesis_hash()
is sort of named backwards (you don't load with a specific genesis hash, you can only do that on a new wallet). The clearer name for it would benew_with_genesis_hash_or_load()
.Wallet.load_from_changeset()
is not optimally named. In fact, the name is also missing the core feature of the constructor: your wallet will not have private keys. An extra commit on this PR renames this method toload_no_priv()
; feel free to bikeshed on the name I don't know that it's a particular good one yet and we can also just take that commit out if people like the load_from_changeset name.Note that this PR doesn't touch the inner logic of the constructors. I simply renamed the methods, removed the
Option
on the changeset when calling the load methods, and fixed up the tests to break apart the behaviour tested. It did require removing theNewOrLoadError
and adding the required variants to theLoadError
.Changelog notice
Checklists
All Submissions:
cargo fmt
andcargo clippy
before committingNew Features: