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,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Error returned from [`Wallet::new_no_persist`]
|
/// Creates a wallet that does not persist data, with a custom genesis hash.
|
||||||
#[derive(Debug)]
|
pub fn new_no_persist_with_genesis_hash<E: IntoWalletDescriptor>(
|
||||||
pub enum NewNoPersistError {
|
descriptor: E,
|
||||||
/// There was problem with the descriptors passed in
|
change_descriptor: Option<E>,
|
||||||
Descriptor(crate::descriptor::DescriptorError),
|
network: Network,
|
||||||
/// We cannot determine the genesis hash from the network.
|
genesis_hash: BlockHash,
|
||||||
UnknownNetwork,
|
) -> Result<Self, crate::descriptor::DescriptorError> {
|
||||||
}
|
Self::new_with_genesis_hash(descriptor, change_descriptor, (), network, genesis_hash)
|
||||||
|
.map_err(|e| match e {
|
||||||
impl fmt::Display for NewNoPersistError {
|
NewError::Descriptor(e) => e,
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
NewError::Write(_) => unreachable!("mock-write must always succeed"),
|
||||||
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()? {
|
||||||
external_descriptor,
|
Wallet::new(
|
||||||
Some(internal_descriptor),
|
external_descriptor,
|
||||||
db,
|
Some(internal_descriptor),
|
||||||
Network::Testnet,
|
db,
|
||||||
)?;
|
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()? {
|
||||||
external_descriptor,
|
Wallet::new(
|
||||||
Some(internal_descriptor),
|
external_descriptor,
|
||||||
db,
|
Some(internal_descriptor),
|
||||||
Network::Testnet,
|
db,
|
||||||
)?;
|
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()? {
|
||||||
external_descriptor,
|
Wallet::new(
|
||||||
Some(internal_descriptor),
|
external_descriptor,
|
||||||
db,
|
Some(internal_descriptor),
|
||||||
Network::Testnet,
|
db,
|
||||||
)?;
|
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