feat(bdk)!: have separate methods for creating and loading Wallet
				
					
				
			`Wallet::new` now creates a new wallet. `Wallet::load` loads an existing wallet. The network type is now recoverable from persistence. Error types have been simplified.
This commit is contained in:
		
							parent
							
								
									7d5f31f6cc
								
							
						
					
					
						commit
						6cf3963c6c
					
				| @ -199,12 +199,3 @@ impl_error!(miniscript::Error, Miniscript); | |||||||
| impl_error!(MiniscriptPsbtError, MiniscriptPsbt); | impl_error!(MiniscriptPsbtError, MiniscriptPsbt); | ||||||
| impl_error!(bitcoin::bip32::Error, Bip32); | impl_error!(bitcoin::bip32::Error, Bip32); | ||||||
| impl_error!(bitcoin::psbt::Error, Psbt); | impl_error!(bitcoin::psbt::Error, Psbt); | ||||||
| 
 |  | ||||||
| impl From<crate::wallet::NewNoPersistError> for Error { |  | ||||||
|     fn from(e: crate::wallet::NewNoPersistError) -> Self { |  | ||||||
|         match e { |  | ||||||
|             wallet::NewNoPersistError::Descriptor(e) => Error::Descriptor(e), |  | ||||||
|             unknown_network_err => Error::Generic(format!("{}", unknown_network_err)), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -128,12 +128,18 @@ pub struct ChangeSet { | |||||||
|         ConfirmationTimeHeightAnchor, |         ConfirmationTimeHeightAnchor, | ||||||
|         keychain::ChangeSet<KeychainKind>, |         keychain::ChangeSet<KeychainKind>, | ||||||
|     >, |     >, | ||||||
|  | 
 | ||||||
|  |     /// Stores the network type of the wallet.
 | ||||||
|  |     pub network: Option<Network>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Append for ChangeSet { | impl Append for ChangeSet { | ||||||
|     fn append(&mut self, other: Self) { |     fn append(&mut self, other: Self) { | ||||||
|         Append::append(&mut self.chain, other.chain); |         Append::append(&mut self.chain, other.chain); | ||||||
|         Append::append(&mut self.indexed_tx_graph, other.indexed_tx_graph); |         Append::append(&mut self.indexed_tx_graph, other.indexed_tx_graph); | ||||||
|  |         if other.network.is_some() { | ||||||
|  |             self.network = other.network; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn is_empty(&self) -> bool { |     fn is_empty(&self) -> bool { | ||||||
| @ -225,75 +231,81 @@ impl Wallet { | |||||||
|         descriptor: E, |         descriptor: E, | ||||||
|         change_descriptor: Option<E>, |         change_descriptor: Option<E>, | ||||||
|         network: Network, |         network: Network, | ||||||
|     ) -> Result<Self, NewNoPersistError> { |     ) -> Result<Self, crate::descriptor::DescriptorError> { | ||||||
|         Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e { |         Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e { | ||||||
|             NewError::Descriptor(e) => NewNoPersistError::Descriptor(e), |             NewError::Descriptor(e) => e, | ||||||
|             NewError::Persist(_) | NewError::InvalidPersistenceGenesis => { |             NewError::Write(_) => unreachable!("mock-write must always succeed"), | ||||||
|                 unreachable!("no persistence so it can't fail") |         }) | ||||||
|     } |     } | ||||||
|             NewError::UnknownNetwork => NewNoPersistError::UnknownNetwork, | 
 | ||||||
|  |     /// Creates a wallet that does not persist data, with a custom genesis hash.
 | ||||||
|  |     pub fn new_no_persist_with_genesis_hash<E: IntoWalletDescriptor>( | ||||||
|  |         descriptor: E, | ||||||
|  |         change_descriptor: Option<E>, | ||||||
|  |         network: Network, | ||||||
|  |         genesis_hash: BlockHash, | ||||||
|  |     ) -> Result<Self, crate::descriptor::DescriptorError> { | ||||||
|  |         Self::new_with_genesis_hash(descriptor, change_descriptor, (), network, genesis_hash) | ||||||
|  |             .map_err(|e| match e { | ||||||
|  |                 NewError::Descriptor(e) => e, | ||||||
|  |                 NewError::Write(_) => unreachable!("mock-write must always succeed"), | ||||||
|             }) |             }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Error returned from [`Wallet::new_no_persist`]
 |  | ||||||
| #[derive(Debug)] |  | ||||||
| pub enum NewNoPersistError { |  | ||||||
|     /// There was problem with the descriptors passed in
 |  | ||||||
|     Descriptor(crate::descriptor::DescriptorError), |  | ||||||
|     /// We cannot determine the genesis hash from the network.
 |  | ||||||
|     UnknownNetwork, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl fmt::Display for NewNoPersistError { |  | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |  | ||||||
|         match self { |  | ||||||
|             NewNoPersistError::Descriptor(e) => e.fmt(f), |  | ||||||
|             NewNoPersistError::UnknownNetwork => write!( |  | ||||||
|                 f, |  | ||||||
|                 "unknown network - genesis block hash needs to be provided explicitly" |  | ||||||
|             ), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[cfg(feature = "std")] |  | ||||||
| impl std::error::Error for NewNoPersistError {} |  | ||||||
| 
 |  | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| /// Error returned from [`Wallet::new`]
 | /// Error returned from [`Wallet::new`]
 | ||||||
| pub enum NewError<PE> { | pub enum NewError<W> { | ||||||
|     /// There was problem with the descriptors passed in
 |     /// There was problem with the passed-in descriptor(s).
 | ||||||
|     Descriptor(crate::descriptor::DescriptorError), |     Descriptor(crate::descriptor::DescriptorError), | ||||||
|     /// We were unable to load the wallet's data from the persistence backend
 |     /// We were unable to write the wallet's data to the persistence backend.
 | ||||||
|     Persist(PE), |     Write(W), | ||||||
|     /// We cannot determine the genesis hash from the network
 |  | ||||||
|     UnknownNetwork, |  | ||||||
|     /// The genesis block hash is either missing from persistence or has an unexpected value
 |  | ||||||
|     InvalidPersistenceGenesis, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<PE> fmt::Display for NewError<PE> | impl<W> fmt::Display for NewError<W> | ||||||
| where | where | ||||||
|     PE: fmt::Display, |     W: fmt::Display, | ||||||
| { | { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         match self { |         match self { | ||||||
|             NewError::Descriptor(e) => e.fmt(f), |             NewError::Descriptor(e) => e.fmt(f), | ||||||
|             NewError::Persist(e) => { |             NewError::Write(e) => e.fmt(f), | ||||||
|                 write!(f, "failed to load wallet from persistence backend: {}", e) |  | ||||||
|             } |  | ||||||
|             NewError::UnknownNetwork => write!( |  | ||||||
|                 f, |  | ||||||
|                 "unknown network - genesis block hash needs to be provided explicitly" |  | ||||||
|             ), |  | ||||||
|             NewError::InvalidPersistenceGenesis => write!(f, "the genesis block hash is either missing from persistence or has an unexpected value"), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "std")] | #[cfg(feature = "std")] | ||||||
| impl<PE> std::error::Error for NewError<PE> where PE: core::fmt::Display + core::fmt::Debug {} | impl<W> std::error::Error for NewError<W> where W: core::fmt::Display + core::fmt::Debug {} | ||||||
|  | 
 | ||||||
|  | /// An error that may occur when loading a [`Wallet`] from persistence.
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub enum LoadError<L> { | ||||||
|  |     /// There was a problem with the passed-in descriptor(s).
 | ||||||
|  |     Descriptor(crate::descriptor::DescriptorError), | ||||||
|  |     /// Loading data from the persistence backend failed.
 | ||||||
|  |     Load(L), | ||||||
|  |     /// Data loaded from persistence is missing network type.
 | ||||||
|  |     MissingNetwork, | ||||||
|  |     /// Data loaded from persistence is missing genesis hash.
 | ||||||
|  |     MissingGenesis, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<L> fmt::Display for LoadError<L> | ||||||
|  | where | ||||||
|  |     L: fmt::Display, | ||||||
|  | { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             LoadError::Descriptor(e) => e.fmt(f), | ||||||
|  |             LoadError::Load(e) => e.fmt(f), | ||||||
|  |             LoadError::MissingNetwork => write!(f, "loaded data is missing network type"), | ||||||
|  |             LoadError::MissingGenesis => write!(f, "loaded data is missing genesis hash"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "std")] | ||||||
|  | impl<L> std::error::Error for LoadError<L> where L: core::fmt::Display + core::fmt::Debug {} | ||||||
| 
 | 
 | ||||||
| /// An error that may occur when inserting a transaction into [`Wallet`].
 | /// An error that may occur when inserting a transaction into [`Wallet`].
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| @ -316,30 +328,29 @@ impl<D> Wallet<D> { | |||||||
|         change_descriptor: Option<E>, |         change_descriptor: Option<E>, | ||||||
|         db: D, |         db: D, | ||||||
|         network: Network, |         network: Network, | ||||||
|     ) -> Result<Self, NewError<D::LoadError>> |     ) -> Result<Self, NewError<D::WriteError>> | ||||||
|     where |     where | ||||||
|         D: PersistBackend<ChangeSet>, |         D: PersistBackend<ChangeSet>, | ||||||
|     { |     { | ||||||
|         Self::with_custom_genesis_hash(descriptor, change_descriptor, db, network, None) |         let genesis_hash = genesis_block(network).block_hash(); | ||||||
|  |         Self::new_with_genesis_hash(descriptor, change_descriptor, db, network, genesis_hash) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Create a new [`Wallet`] with a custom genesis hash.
 |     /// Create a new [`Wallet`] with a custom genesis hash.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// This is like [`Wallet::new`] with an additional `custom_genesis_hash` parameter.
 |     /// This is like [`Wallet::new`] with an additional `custom_genesis_hash` parameter.
 | ||||||
|     pub fn with_custom_genesis_hash<E: IntoWalletDescriptor>( |     pub fn new_with_genesis_hash<E: IntoWalletDescriptor>( | ||||||
|         descriptor: E, |         descriptor: E, | ||||||
|         change_descriptor: Option<E>, |         change_descriptor: Option<E>, | ||||||
|         mut db: D, |         db: D, | ||||||
|         network: Network, |         network: Network, | ||||||
|         custom_genesis_hash: Option<BlockHash>, |         genesis_hash: BlockHash, | ||||||
|     ) -> Result<Self, NewError<D::LoadError>> |     ) -> Result<Self, NewError<D::WriteError>> | ||||||
|     where |     where | ||||||
|         D: PersistBackend<ChangeSet>, |         D: PersistBackend<ChangeSet>, | ||||||
|     { |     { | ||||||
|         let secp = Secp256k1::new(); |         let secp = Secp256k1::new(); | ||||||
|         let genesis_hash = |         let (chain, _) = LocalChain::from_genesis_hash(genesis_hash); | ||||||
|             custom_genesis_hash.unwrap_or_else(|| genesis_block(network).block_hash()); |  | ||||||
|         let (mut chain, _) = LocalChain::from_genesis_hash(genesis_hash); |  | ||||||
|         let mut indexed_graph = IndexedTxGraph::< |         let mut indexed_graph = IndexedTxGraph::< | ||||||
|             ConfirmationTimeHeightAnchor, |             ConfirmationTimeHeightAnchor, | ||||||
|             KeychainTxOutIndex<KeychainKind>, |             KeychainTxOutIndex<KeychainKind>, | ||||||
| @ -351,34 +362,27 @@ impl<D> Wallet<D> { | |||||||
|             .index |             .index | ||||||
|             .add_keychain(KeychainKind::External, descriptor.clone()); |             .add_keychain(KeychainKind::External, descriptor.clone()); | ||||||
|         let signers = Arc::new(SignersContainer::build(keymap, &descriptor, &secp)); |         let signers = Arc::new(SignersContainer::build(keymap, &descriptor, &secp)); | ||||||
|         let change_signers = match change_descriptor { | 
 | ||||||
|  |         let change_signers = Arc::new(match change_descriptor { | ||||||
|             Some(desc) => { |             Some(desc) => { | ||||||
|                 let (change_descriptor, change_keymap) = |                 let (descriptor, keymap) = into_wallet_descriptor_checked(desc, &secp, network) | ||||||
|                     into_wallet_descriptor_checked(desc, &secp, network) |  | ||||||
|                     .map_err(NewError::Descriptor)?; |                     .map_err(NewError::Descriptor)?; | ||||||
| 
 |                 let signers = SignersContainer::build(keymap, &descriptor, &secp); | ||||||
|                 let change_signers = Arc::new(SignersContainer::build( |  | ||||||
|                     change_keymap, |  | ||||||
|                     &change_descriptor, |  | ||||||
|                     &secp, |  | ||||||
|                 )); |  | ||||||
| 
 |  | ||||||
|                 indexed_graph |                 indexed_graph | ||||||
|                     .index |                     .index | ||||||
|                     .add_keychain(KeychainKind::Internal, change_descriptor); |                     .add_keychain(KeychainKind::Internal, descriptor); | ||||||
| 
 |                 signers | ||||||
|                 change_signers |  | ||||||
|             } |             } | ||||||
|             None => Arc::new(SignersContainer::new()), |             None => SignersContainer::new(), | ||||||
|         }; |         }); | ||||||
| 
 | 
 | ||||||
|         let changeset = db.load_from_persistence().map_err(NewError::Persist)?; |         let mut persist = Persist::new(db); | ||||||
|         chain |         persist.stage(ChangeSet { | ||||||
|             .apply_changeset(&changeset.chain) |             chain: chain.initial_changeset(), | ||||||
|             .map_err(|_| NewError::InvalidPersistenceGenesis)?; |             indexed_tx_graph: indexed_graph.initial_changeset(), | ||||||
|         indexed_graph.apply_changeset(changeset.indexed_tx_graph); |             network: Some(network), | ||||||
| 
 |         }); | ||||||
|         let persist = Persist::new(db); |         persist.commit().map_err(NewError::Write)?; | ||||||
| 
 | 
 | ||||||
|         Ok(Wallet { |         Ok(Wallet { | ||||||
|             signers, |             signers, | ||||||
| @ -391,6 +395,56 @@ impl<D> Wallet<D> { | |||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Load [`Wallet`] from persistence.
 | ||||||
|  |     pub fn load<E: IntoWalletDescriptor>( | ||||||
|  |         descriptor: E, | ||||||
|  |         change_descriptor: Option<E>, | ||||||
|  |         mut db: D, | ||||||
|  |     ) -> Result<Self, LoadError<D::LoadError>> | ||||||
|  |     where | ||||||
|  |         D: PersistBackend<ChangeSet>, | ||||||
|  |     { | ||||||
|  |         let secp = Secp256k1::new(); | ||||||
|  | 
 | ||||||
|  |         let changeset = db.load_from_persistence().map_err(LoadError::Load)?; | ||||||
|  |         let network = changeset.network.ok_or(LoadError::MissingNetwork)?; | ||||||
|  | 
 | ||||||
|  |         let chain = | ||||||
|  |             LocalChain::from_changeset(changeset.chain).map_err(|_| LoadError::MissingGenesis)?; | ||||||
|  | 
 | ||||||
|  |         let mut index = KeychainTxOutIndex::<KeychainKind>::default(); | ||||||
|  | 
 | ||||||
|  |         let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, &secp, network) | ||||||
|  |             .map_err(LoadError::Descriptor)?; | ||||||
|  |         let signers = Arc::new(SignersContainer::build(keymap, &descriptor, &secp)); | ||||||
|  |         index.add_keychain(KeychainKind::External, descriptor); | ||||||
|  | 
 | ||||||
|  |         let change_signers = Arc::new(match change_descriptor { | ||||||
|  |             Some(descriptor) => { | ||||||
|  |                 let (descriptor, keymap) = | ||||||
|  |                     into_wallet_descriptor_checked(descriptor, &secp, network) | ||||||
|  |                         .map_err(LoadError::Descriptor)?; | ||||||
|  |                 let signers = SignersContainer::build(keymap, &descriptor, &secp); | ||||||
|  |                 index.add_keychain(KeychainKind::Internal, descriptor); | ||||||
|  |                 signers | ||||||
|  |             } | ||||||
|  |             None => SignersContainer::new(), | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         let indexed_graph = IndexedTxGraph::new(index); | ||||||
|  |         let persist = Persist::new(db); | ||||||
|  | 
 | ||||||
|  |         Ok(Wallet { | ||||||
|  |             signers, | ||||||
|  |             change_signers, | ||||||
|  |             chain, | ||||||
|  |             indexed_graph, | ||||||
|  |             persist, | ||||||
|  |             network, | ||||||
|  |             secp, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Get the Bitcoin network the wallet is using.
 |     /// Get the Bitcoin network the wallet is using.
 | ||||||
|     pub fn network(&self) -> Network { |     pub fn network(&self) -> Network { | ||||||
|         self.network |         self.network | ||||||
|  | |||||||
| @ -18,16 +18,20 @@ use bdk_file_store::Store; | |||||||
| 
 | 
 | ||||||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||||
|     let db_path = std::env::temp_dir().join("bdk-electrum-example"); |     let db_path = std::env::temp_dir().join("bdk-electrum-example"); | ||||||
|     let db = Store::<bdk::wallet::ChangeSet>::new_from_path(DB_MAGIC.as_bytes(), db_path)?; |     let mut db = Store::<bdk::wallet::ChangeSet>::new_from_path(DB_MAGIC.as_bytes(), db_path)?; | ||||||
|     let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; |     let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; | ||||||
|     let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; |     let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; | ||||||
| 
 | 
 | ||||||
|     let mut wallet = Wallet::new( |     let mut wallet = if db.is_empty()? { | ||||||
|  |         Wallet::new( | ||||||
|             external_descriptor, |             external_descriptor, | ||||||
|             Some(internal_descriptor), |             Some(internal_descriptor), | ||||||
|             db, |             db, | ||||||
|             Network::Testnet, |             Network::Testnet, | ||||||
|     )?; |         )? | ||||||
|  |     } else { | ||||||
|  |         Wallet::load(external_descriptor, Some(internal_descriptor), db)? | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     let address = wallet.get_address(bdk::wallet::AddressIndex::New); |     let address = wallet.get_address(bdk::wallet::AddressIndex::New); | ||||||
|     println!("Generated Address: {}", address); |     println!("Generated Address: {}", address); | ||||||
|  | |||||||
| @ -16,16 +16,20 @@ const PARALLEL_REQUESTS: usize = 5; | |||||||
| #[tokio::main] | #[tokio::main] | ||||||
| async fn main() -> Result<(), Box<dyn std::error::Error>> { | async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||||
|     let db_path = std::env::temp_dir().join("bdk-esplora-async-example"); |     let db_path = std::env::temp_dir().join("bdk-esplora-async-example"); | ||||||
|     let db = Store::<bdk::wallet::ChangeSet>::new_from_path(DB_MAGIC.as_bytes(), db_path)?; |     let mut db = Store::<bdk::wallet::ChangeSet>::new_from_path(DB_MAGIC.as_bytes(), db_path)?; | ||||||
|     let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; |     let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; | ||||||
|     let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; |     let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; | ||||||
| 
 | 
 | ||||||
|     let mut wallet = Wallet::new( |     let mut wallet = if db.is_empty()? { | ||||||
|  |         Wallet::new( | ||||||
|             external_descriptor, |             external_descriptor, | ||||||
|             Some(internal_descriptor), |             Some(internal_descriptor), | ||||||
|             db, |             db, | ||||||
|             Network::Testnet, |             Network::Testnet, | ||||||
|     )?; |         )? | ||||||
|  |     } else { | ||||||
|  |         Wallet::load(external_descriptor, Some(internal_descriptor), db)? | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     let address = wallet.get_address(AddressIndex::New); |     let address = wallet.get_address(AddressIndex::New); | ||||||
|     println!("Generated Address: {}", address); |     println!("Generated Address: {}", address); | ||||||
|  | |||||||
| @ -15,16 +15,20 @@ use bdk_file_store::Store; | |||||||
| 
 | 
 | ||||||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||||
|     let db_path = std::env::temp_dir().join("bdk-esplora-example"); |     let db_path = std::env::temp_dir().join("bdk-esplora-example"); | ||||||
|     let db = Store::<bdk::wallet::ChangeSet>::new_from_path(DB_MAGIC.as_bytes(), db_path)?; |     let mut db = Store::<bdk::wallet::ChangeSet>::new_from_path(DB_MAGIC.as_bytes(), db_path)?; | ||||||
|     let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; |     let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; | ||||||
|     let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; |     let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; | ||||||
| 
 | 
 | ||||||
|     let mut wallet = Wallet::new( |     let mut wallet = if db.is_empty()? { | ||||||
|  |         Wallet::new( | ||||||
|             external_descriptor, |             external_descriptor, | ||||||
|             Some(internal_descriptor), |             Some(internal_descriptor), | ||||||
|             db, |             db, | ||||||
|             Network::Testnet, |             Network::Testnet, | ||||||
|     )?; |         )? | ||||||
|  |     } else { | ||||||
|  |         Wallet::load(external_descriptor, Some(internal_descriptor), db)? | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     let address = wallet.get_address(AddressIndex::New); |     let address = wallet.get_address(AddressIndex::New); | ||||||
|     println!("Generated Address: {}", address); |     println!("Generated Address: {}", address); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user