@@ -1,30 +1,23 @@
use bdk ::bitcoin ::hashes ::hex ::ToHex ;
use bdk ::bitcoin ::secp256k1 ::Secp256k1 ;
use bdk ::bitcoin ::util ::bip32 ::DerivationPath as BdkDerivationPath ;
use bdk ::bitcoin ::util ::psbt ::PartiallySignedTransaction ;
use bdk ::bitcoin ::{ Address , Network , OutPoint as BdkOutPoint , Script , Txid } ;
use bdk ::blockchain ::any ::{ AnyBlockchain , AnyBlockchainConfig } ;
use bdk ::blockchain ::GetBlockHash ;
use bdk ::blockchain ::GetHeight ;
use bdk ::blockchain ::{
electrum ::ElectrumBlockchainConfig , esplora ::EsploraBlockchainConfig , ConfigurableBlockchain ,
} ;
use bdk ::blockchain ::{ Blockchain as BdkBlockchain , Progress as BdkProgress } ;
use bdk ::database ::any ::{ AnyDatabase , SledDbConfiguration , SqliteDbConfiguration } ;
use bdk ::database ::{ AnyDatabaseConfig , ConfigurableDatabase } ;
use bdk ::descriptor ::DescriptorXKey ;
use bdk ::keys ::bip39 ::{ Language , Mnemonic , WordCount } ;
use bdk ::keys ::{
DerivableKey , DescriptorPublicKey as BdkDescriptorPublicKey ,
DescriptorSecretKey as BdkDescriptorSecretKey , ExtendedKey , GeneratableKey , GeneratedKey ,
} ;
use bdk ::keys ::{ DerivableKey , ExtendedKey , GeneratableKey , GeneratedKey } ;
use bdk ::miniscript ::BareCtx ;
use bdk ::wallet ::tx_builder ::ChangeSpendPolicy ;
use bdk ::wallet ::AddressIndex as BdkAddressIndex ;
use bdk ::wallet ::AddressInfo as BdkAddressInfo ;
use bdk ::{
Balance as BdkBalance , BlockTime, Error , FeeRate , KeychainKind , SignOptions ,
SyncOptions as BdkSyncOptions , Wallet as BdkWallet ,
BlockTime , Error , FeeRate , KeychainKind , SignOptions , SyncOptions as BdkSyncOptions ,
Wallet as BdkWallet ,
} ;
use std ::collections ::HashSet ;
use std ::convert ::{ From , TryFrom } ;
@@ -37,16 +30,8 @@ uniffi_macros::include_scaffolding!("bdk");
type BdkError = Error ;
pub struct AddressAmount {
pub address : String ,
pub amount : u64 ,
}
/// A derived address and the index it was found at.
pub struct AddressInfo {
/// Child index of this address
pub index : u32 ,
/// Address
pub address : String ,
}
@@ -59,17 +44,8 @@ impl From<BdkAddressInfo> for AddressInfo {
}
}
/// The address index selection strategy to use to derived an address from the wallet's external
/// descriptor.
pub enum AddressIndex {
/// Return a new address after incrementing the current descriptor index.
New ,
/// Return the address for the current descriptor index if it has not been used in a received
/// transaction. Otherwise return a new address as with AddressIndex::New.
/// Use with caution, if the wallet has not yet detected an address has been used it could
/// return an already used address. This function is primarily meant for situations where the
/// caller is untrusted; for example when deriving donation addresses on-demand for a public
/// web page.
LastUnused ,
}
@@ -82,82 +58,50 @@ impl From<AddressIndex> for BdkAddressIndex {
}
}
/// Type that can contain any of the database configurations defined by the library
/// This allows storing a single configuration that can be loaded into an AnyDatabaseConfig
/// instance. Wallets that plan to offer users the ability to switch blockchain backend at runtime
/// will find this particularly useful.
pub enum DatabaseConfig {
/// Memory database has no config
Memory ,
/// Simple key-value embedded database based on sled
Sled { config : SledDbConfiguration } ,
/// Sqlite embedded database using rusqlite
Sqlite { config : SqliteDbConfiguration } ,
}
/// Configuration for an ElectrumBlockchain
pub struct ElectrumConfig {
/// URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with ssl:// or tcp:// and include a port
/// e.g. ssl://electrum.blockstream.info:60002
pub url : String ,
/// URL of the socks5 proxy server or a Tor service
pub socks5 : Option < String > ,
/// Request retry count
pub retry : u8 ,
/// Request timeout (seconds)
pub timeout : Option < u8 > ,
/// Stop searching addresses for transactions after finding an unused gap of this length
pub stop_gap : u64 ,
}
/// Configuration for an EsploraBlockchain
pub struct EsploraConfig {
/// Base URL of the esplora service
/// e.g. https://blockstream.info/api/
pub base_url : String ,
/// Optional URL of the proxy to use to make requests to the Esplora server
/// The string should be formatted as: <protocol>://<user>:<password>@host:<port>.
/// Note that the format of this value and the supported protocols change slightly between the
/// sync version of esplora (using ureq) and the async version (using reqwest). For more
/// details check with the documentation of the two crates. Both of them are compiled with
/// the socks feature enabled.
/// The proxy is ignored when targeting wasm32.
pub proxy : Option < String > ,
/// Number of parallel requests sent to the esplora service (default: 4)
pub concurrency : Option < u8 > ,
/// Stop searching addresses for transactions after finding an unused gap of this length.
pub stop_gap : u64 ,
/// Socket timeout.
pub timeout : Option < u64 > ,
}
/// Type that can contain any of the blockchain configurations defined by the library.
pub enum BlockchainConfig {
/// Electrum client
Electrum { config : ElectrumConfig } ,
/// Esplora client
Esplora { config : EsploraConfig } ,
}
/// A wallet transaction
#[ derive(Debug, Clone, PartialEq, Eq, Default) ]
pub struct TransactionDetails {
/// Transaction id.
pub txid : String ,
/// Received value (sats)
/// Sum of owned outputs of this transaction.
pub received : u64 ,
/// Sent value (sats)
/// Sum of owned inputs of this transaction.
pub sent : u64 ,
/// Fee value (sats) if confirmed.
/// The availability of the fee depends on the backend. It's never None with an Electrum
/// Server backend, but it could be None with a Bitcoin RPC node without txindex that receive
/// funds while offline.
pub fee : Option < u64 > ,
/// If the transaction is confirmed, contains height and timestamp of the block containing the
/// transaction, unconfirmed transaction contains `None`.
pub confirmation_time : Option < BlockTime > ,
pub received : u64 ,
pub sent : u64 ,
pub txid : String ,
}
#[ derive(Debug, Clone, PartialEq, Eq) ]
pub enum Transaction {
Unconfirmed {
details : TransactionDetails ,
} ,
Confirmed {
details : TransactionDetails ,
confirmation : BlockTime ,
} ,
}
impl From < & bdk ::TransactionDetails > for TransactionDetails {
@@ -167,7 +111,20 @@ impl From<&bdk::TransactionDetails> for TransactionDetails {
txid : x . txid . to_string ( ) ,
received : x . received ,
sent : x . sent ,
confirmation_time : x . confirmation_time . clone ( ) ,
}
}
}
impl From < & bdk ::TransactionDetails > for Transaction {
fn from ( x : & bdk ::TransactionDetails ) -> Transaction {
match x . confirmation_time . clone ( ) {
Some ( block_time ) = > Transaction ::Confirmed {
details : TransactionDetails ::from ( x ) ,
confirmation : block_time ,
} ,
None = > Transaction ::Unconfirmed {
details : TransactionDetails ::from ( x ) ,
} ,
}
}
}
@@ -212,28 +169,15 @@ impl Blockchain {
let tx = psbt . internal . lock ( ) . unwrap ( ) . clone ( ) . extract_tx ( ) ;
self . get_blockchain ( ) . broadcast ( & tx )
}
fn get_height ( & self ) -> Result < u32 , Error > {
self . get_blockchain ( ) . get_height ( )
}
fn get_block_hash ( & self , height : u32 ) -> Result < String , Error > {
self . get_blockchain ( )
. get_block_hash ( u64 ::from ( height ) )
. map ( | hash | hash . to_string ( ) )
}
}
struct Wallet {
wallet_mutex : Mutex < BdkWallet < AnyDatabase > > ,
}
/// A reference to a transaction output.
#[ derive(Clone, Debug, PartialEq, Eq, Hash) ]
pub struct OutPoint {
/// The referenced transaction's txid.
txid : String ,
/// The index of the referenced output in its transaction's vout.
vout : u32 ,
}
@@ -246,39 +190,8 @@ impl From<&OutPoint> for BdkOutPoint {
}
}
pub struct Balance {
// All coinbase outputs not yet matured
pub immature : u64 ,
/// Unconfirmed UTXOs generated by a wallet tx
pub trusted_pending : u64 ,
/// Unconfirmed UTXOs received from an external wallet
pub untrusted_pending : u64 ,
/// Confirmed and immediately spendable balance
pub confirmed : u64 ,
/// Get sum of trusted_pending and confirmed coins
pub spendable : u64 ,
/// Get the whole balance visible to the wallet
pub total : u64 ,
}
impl From < BdkBalance > for Balance {
fn from ( bdk_balance : BdkBalance ) -> Self {
Balance {
immature : bdk_balance . immature ,
trusted_pending : bdk_balance . trusted_pending ,
untrusted_pending : bdk_balance . untrusted_pending ,
confirmed : bdk_balance . confirmed ,
spendable : bdk_balance . get_spendable ( ) ,
total : bdk_balance . get_total ( ) ,
}
}
}
/// A transaction output, which defines new coins to be created from old ones.
pub struct TxOut {
/// The value of the output, in satoshis.
value : u64 ,
/// The address of the output.
address : String ,
}
@@ -317,10 +230,7 @@ impl NetworkLocalUtxo for LocalUtxo {
}
}
/// Trait that logs at level INFO every update received (if any).
pub trait Progress : Send + Sync + 'static {
/// Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an
/// optional text message that can be displayed to the user.
fn update ( & self , progress : f32 , message : Option < String > ) ;
}
@@ -366,11 +276,6 @@ impl PartiallySignedBitcoinTransaction {
}
}
/// A Bitcoin wallet.
/// The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
/// 1. Output descriptors from which it can derive addresses.
/// 2. A Database where it tracks transactions and utxos related to the descriptors.
/// 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
impl Wallet {
fn new (
descriptor : String ,
@@ -397,12 +302,10 @@ impl Wallet {
self . wallet_mutex . lock ( ) . expect ( " wallet " )
}
/// Get the Bitcoin network the wallet is using.
fn network ( & self ) -> Network {
fn get_network ( & self ) -> Network {
self . get_wallet ( ) . network ( )
}
/// Sync the internal database with the blockchain.
fn sync (
& self ,
blockchain : & Blockchain ,
@@ -419,47 +322,75 @@ impl Wallet {
self . get_wallet ( ) . sync ( blockchain . deref ( ) , bdk_sync_opts )
}
/// Return a derived address using the external descriptor, see AddressIndex for available address index selection
/// strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end with a * character)
/// then the same address will always be returned for any AddressIndex.
fn get_address ( & self , address_index : AddressIndex ) -> Result < AddressInfo , BdkError > {
self . get_wallet ( )
. get_address ( address_index . into ( ) )
. map ( AddressInfo ::from )
}
/// Return the balance, meaning the sum of this wallet’ s unspent outputs’ values. Note that this method only operates
/// on the internal database, which first needs to be Wallet.sync manually.
fn get_balance ( & self ) -> Result < Balance , Error > {
self . get_wallet ( ) . get_balance ( ) . map ( | b | b . into ( ) )
fn get_balance ( & self ) -> Result < u64 , Error > {
self . get_wallet ( ) . get_balance ( )
}
/// Sign a transaction with all the wallet’ s signers.
fn sign ( & self , psbt : & PartiallySignedBitcoinTransaction ) -> Result < bool , Error > {
let mut psbt = psbt . internal . lock ( ) . unwrap ( ) ;
self . get_wallet ( ) . sign ( & mut psbt , SignOptions ::default ( ) )
}
/// Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually.
fn list_transactions ( & self ) -> Result < Vec < TransactionDetails > , Error > {
let transaction_details = self . get_wallet ( ) . list_t ransactions ( true ) ? ;
Ok ( transaction_details
. iter ( )
. map ( TransactionDetails ::from )
. collect ( ) )
fn get_transactions ( & self ) -> Result < Vec < Transaction > , Error > {
let transactions = self . get_wallet ( ) . list_transactions ( true ) ? ;
Ok ( transactions . iter ( ) . map ( T ransaction ::from ) . collect ( ) )
}
/// Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database,
/// which first needs to be Wallet.sync manually.
fn list_unspent ( & self ) -> Result < Vec < LocalUtxo > , Error > {
let unspents = self . get_wallet ( ) . list_unspent ( ) ? ;
Ok ( unspents
. iter ( )
. map ( | u | LocalUtxo ::from_utxo ( u , self . network ( ) ) )
. map ( | u | LocalUtxo ::from_utxo ( u , self . get_ network( ) ) )
. collect ( ) )
}
}
pub struct ExtendedKeyInfo {
mnemonic : String ,
xprv : String ,
fingerprint : String ,
}
fn generate_extended_key (
network : Network ,
word_count : WordCount ,
password : Option < String > ,
) -> Result < ExtendedKeyInfo , Error > {
let mnemonic : GeneratedKey < _ , BareCtx > =
Mnemonic ::generate ( ( word_count , Language ::English ) ) . unwrap ( ) ;
let mnemonic = mnemonic . into_key ( ) ;
let xkey : ExtendedKey = ( mnemonic . clone ( ) , password ) . into_extended_key ( ) ? ;
let xprv = xkey . into_xprv ( network ) . unwrap ( ) ;
let fingerprint = xprv . fingerprint ( & Secp256k1 ::new ( ) ) ;
Ok ( ExtendedKeyInfo {
mnemonic : mnemonic . to_string ( ) ,
xprv : xprv . to_string ( ) ,
fingerprint : fingerprint . to_string ( ) ,
} )
}
fn restore_extended_key (
network : Network ,
mnemonic : String ,
password : Option < String > ,
) -> Result < ExtendedKeyInfo , Error > {
let mnemonic = Mnemonic ::parse_in ( Language ::English , mnemonic ) . unwrap ( ) ;
let xkey : ExtendedKey = ( mnemonic . clone ( ) , password ) . into_extended_key ( ) ? ;
let xprv = xkey . into_xprv ( network ) . unwrap ( ) ;
let fingerprint = xprv . fingerprint ( & Secp256k1 ::new ( ) ) ;
Ok ( ExtendedKeyInfo {
mnemonic : mnemonic . to_string ( ) ,
xprv : xprv . to_string ( ) ,
fingerprint : fingerprint . to_string ( ) ,
} )
}
fn to_script_pubkey ( address : & str ) -> Result < Script , BdkError > {
Address ::from_str ( address )
. map ( | x | x . script_pubkey ( ) )
@@ -472,9 +403,6 @@ enum RbfValue {
Value ( u32 ) ,
}
/// A transaction builder.
/// After creating the TxBuilder, you set options on it until finally calling finish to consume the builder and generate the transaction.
/// Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
#[ derive(Clone, Debug) ]
struct TxBuilder {
recipients : Vec < ( String , u64 ) > ,
@@ -507,7 +435,6 @@ impl TxBuilder {
}
}
/// Add a recipient to the internal list.
fn add_recipient ( & self , recipient : String , amount : u64 ) -> Arc < Self > {
let mut recipients = self . recipients . to_vec ( ) ;
recipients . append ( & mut vec! [ ( recipient , amount ) ] ) ;
@@ -517,19 +444,6 @@ impl TxBuilder {
} )
}
fn set_recipients ( & self , recipients : Vec < AddressAmount > ) -> Arc < Self > {
let recipients = recipients
. iter ( )
. map ( | address_amount | ( address_amount . address . clone ( ) , address_amount . amount ) )
. collect ( ) ;
Arc ::new ( TxBuilder {
recipients ,
.. self . clone ( )
} )
}
/// Add a utxo to the internal list of unspendable utxos. It’ s important to note that the "must-be-spent"
/// utxos added with [TxBuilder.addUtxo] have priority over this. See the Rust docs of the two linked methods for more details.
fn add_unspendable ( & self , unspendable : OutPoint ) -> Arc < Self > {
let mut unspendable_hash_set = self . unspendable . clone ( ) ;
unspendable_hash_set . insert ( unspendable ) ;
@@ -539,15 +453,10 @@ impl TxBuilder {
} )
}
/// Add an outpoint to the internal list of UTXOs that must be spent. These have priority over the "unspendable"
/// utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent.
fn add_utxo ( & self , outpoint : OutPoint ) -> Arc < Self > {
self . add_utxos ( vec! [ outpoint ] )
}
/// Add the list of outpoints to the internal list of UTXOs that must be spent. If an error occurs while adding
/// any of the UTXOs then none of them are added and the error is returned. These have priority over the "unspendable"
/// utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent.
fn add_utxos ( & self , mut outpoints : Vec < OutPoint > ) -> Arc < Self > {
let mut utxos = self . utxos . to_vec ( ) ;
utxos . append ( & mut outpoints ) ;
@@ -557,7 +466,6 @@ impl TxBuilder {
} )
}
/// Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See TxBuilder.unspendable.
fn do_not_spend_change ( & self ) -> Arc < Self > {
Arc ::new ( TxBuilder {
change_policy : ChangeSpendPolicy ::ChangeForbidden ,
@@ -565,8 +473,6 @@ impl TxBuilder {
} )
}
/// Only spend utxos added by [add_utxo]. The wallet will not add additional utxos to the transaction even if they are
/// needed to make the transaction valid.
fn manually_selected_only ( & self ) -> Arc < Self > {
Arc ::new ( TxBuilder {
manually_selected_only : true ,
@@ -574,7 +480,6 @@ impl TxBuilder {
} )
}
/// Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See TxBuilder.unspendable.
fn only_spend_change ( & self ) -> Arc < Self > {
Arc ::new ( TxBuilder {
change_policy : ChangeSpendPolicy ::OnlyChange ,
@@ -582,8 +487,6 @@ impl TxBuilder {
} )
}
/// Replace the internal list of unspendable utxos with a new list. It’ s important to note that the "must-be-spent" utxos added with
/// TxBuilder.addUtxo have priority over these. See the Rust docs of the two linked methods for more details.
fn unspendable ( & self , unspendable : Vec < OutPoint > ) -> Arc < Self > {
Arc ::new ( TxBuilder {
unspendable : unspendable . into_iter ( ) . collect ( ) ,
@@ -591,7 +494,6 @@ impl TxBuilder {
} )
}
/// Set a custom fee rate.
fn fee_rate ( & self , sat_per_vb : f32 ) -> Arc < Self > {
Arc ::new ( TxBuilder {
fee_rate : Some ( sat_per_vb ) ,
@@ -599,7 +501,6 @@ impl TxBuilder {
} )
}
/// Set an absolute fee.
fn fee_absolute ( & self , fee_amount : u64 ) -> Arc < Self > {
Arc ::new ( TxBuilder {
fee_absolute : Some ( fee_amount ) ,
@@ -607,7 +508,6 @@ impl TxBuilder {
} )
}
/// Spend all the available inputs. This respects filters like TxBuilder.unspendable and the change policy.
fn drain_wallet ( & self ) -> Arc < Self > {
Arc ::new ( TxBuilder {
drain_wallet : true ,
@@ -615,14 +515,6 @@ impl TxBuilder {
} )
}
/// Sets the address to drain excess coins to. Usually, when there are excess coins they are sent to a change address
/// generated by the wallet. This option replaces the usual change address with an arbitrary ScriptPubKey of your choosing.
/// Just as with a change output, if the drain output is not needed (the excess coins are too small) it will not be included
/// in the resulting transaction. The only difference is that it is valid to use drain_to without setting any ordinary recipients
/// with add_recipient (but it is perfectly fine to add recipients as well). If you choose not to set any recipients, you should
/// either provide the utxos that the transaction should spend via add_utxos, or set drain_wallet to spend all of them.
/// When bumping the fees of a transaction made with this option, you probably want to use BumpFeeTxBuilder.allow_shrinking
/// to allow this output to be reduced to pay for the extra fees.
fn drain_to ( & self , address : String ) -> Arc < Self > {
Arc ::new ( TxBuilder {
drain_to : Some ( address ) ,
@@ -630,7 +522,6 @@ impl TxBuilder {
} )
}
/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
fn enable_rbf ( & self ) -> Arc < Self > {
Arc ::new ( TxBuilder {
rbf : Some ( RbfValue ::Default ) ,
@@ -638,9 +529,6 @@ impl TxBuilder {
} )
}
/// Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an
/// "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD`
/// an error will be thrown, since it would not be a valid nSequence to signal RBF.
fn enable_rbf_with_sequence ( & self , nsequence : u32 ) -> Arc < Self > {
Arc ::new ( TxBuilder {
rbf : Some ( RbfValue ::Value ( nsequence ) ) ,
@@ -648,7 +536,6 @@ impl TxBuilder {
} )
}
/// Add data as an output using OP_RETURN.
fn add_data ( & self , data : Vec < u8 > ) -> Arc < Self > {
Arc ::new ( TxBuilder {
data ,
@@ -656,7 +543,6 @@ impl TxBuilder {
} )
}
/// Finish building the transaction. Returns the BIP174 PSBT.
fn finish ( & self , wallet : & Wallet ) -> Result < Arc < PartiallySignedBitcoinTransaction > , Error > {
let wallet = wallet . get_wallet ( ) ;
let mut tx_builder = wallet . build_tx ( ) ;
@@ -712,7 +598,6 @@ impl TxBuilder {
}
}
/// The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true.
#[ derive(Clone) ]
struct BumpFeeTxBuilder {
txid : String ,
@@ -731,11 +616,6 @@ impl BumpFeeTxBuilder {
}
}
/// Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this script_pubkey
/// in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output to
/// shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is preserved
/// then it is currently not guaranteed to be in the same position as it was originally. Returns an error if script_pubkey
/// can’ t be found among the recipients of the transaction we are bumping.
fn allow_shrinking ( & self , address : String ) -> Arc < Self > {
Arc ::new ( Self {
allow_shrinking : Some ( address ) ,
@@ -743,7 +623,6 @@ impl BumpFeeTxBuilder {
} )
}
/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
fn enable_rbf ( & self ) -> Arc < Self > {
Arc ::new ( Self {
rbf : Some ( RbfValue ::Default ) ,
@@ -751,9 +630,6 @@ impl BumpFeeTxBuilder {
} )
}
/// Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an
/// "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD`
/// an error will be thrown, since it would not be a valid nSequence to signal RBF.
fn enable_rbf_with_sequence ( & self , nsequence : u32 ) -> Arc < Self > {
Arc ::new ( Self {
rbf : Some ( RbfValue ::Value ( nsequence ) ) ,
@@ -761,7 +637,6 @@ impl BumpFeeTxBuilder {
} )
}
/// Finish building the transaction. Returns the BIP174 PSBT.
fn finish ( & self , wallet : & Wallet ) -> Result < Arc < PartiallySignedBitcoinTransaction > , Error > {
let wallet = wallet . get_wallet ( ) ;
let txid = Txid ::from_str ( self . txid . as_str ( ) ) ? ;
@@ -792,173 +667,6 @@ impl BumpFeeTxBuilder {
}
}
fn generate_mnemonic ( word_count : WordCount ) -> Result < String , BdkError > {
let mnemonic : GeneratedKey < _ , BareCtx > =
Mnemonic ::generate ( ( word_count , Language ::English ) ) . unwrap ( ) ;
Ok ( mnemonic . to_string ( ) )
}
struct DerivationPath {
derivation_path_mutex : Mutex < BdkDerivationPath > ,
}
impl DerivationPath {
fn new ( path : String ) -> Result < Self , BdkError > {
BdkDerivationPath ::from_str ( & path )
. map ( | x | DerivationPath {
derivation_path_mutex : Mutex ::new ( x ) ,
} )
. map_err ( | e | BdkError ::Generic ( e . to_string ( ) ) )
}
}
struct DescriptorSecretKey {
descriptor_secret_key_mutex : Mutex < BdkDescriptorSecretKey > ,
}
impl DescriptorSecretKey {
fn new ( network : Network , mnemonic : String , password : Option < String > ) -> Result < Self , BdkError > {
let mnemonic = Mnemonic ::parse_in ( Language ::English , mnemonic )
. map_err ( | e | BdkError ::Generic ( e . to_string ( ) ) ) ? ;
let xkey : ExtendedKey = ( mnemonic , password ) . into_extended_key ( ) ? ;
let descriptor_secret_key = BdkDescriptorSecretKey ::XPrv ( DescriptorXKey {
origin : None ,
xkey : xkey . into_xprv ( network ) . unwrap ( ) ,
derivation_path : BdkDerivationPath ::master ( ) ,
wildcard : bdk ::descriptor ::Wildcard ::Unhardened ,
} ) ;
Ok ( Self {
descriptor_secret_key_mutex : Mutex ::new ( descriptor_secret_key ) ,
} )
}
fn derive ( & self , path : Arc < DerivationPath > ) -> Result < Arc < Self > , BdkError > {
let secp = Secp256k1 ::new ( ) ;
let descriptor_secret_key = self . descriptor_secret_key_mutex . lock ( ) . unwrap ( ) ;
let path = path . derivation_path_mutex . lock ( ) . unwrap ( ) . deref ( ) . clone ( ) ;
match descriptor_secret_key . deref ( ) {
BdkDescriptorSecretKey ::XPrv ( descriptor_x_key ) = > {
let derived_xprv = descriptor_x_key . xkey . derive_priv ( & secp , & path ) ? ;
let key_source = match descriptor_x_key . origin . clone ( ) {
Some ( ( fingerprint , origin_path ) ) = > ( fingerprint , origin_path . extend ( path ) ) ,
None = > ( descriptor_x_key . xkey . fingerprint ( & secp ) , path ) ,
} ;
let derived_descriptor_secret_key = BdkDescriptorSecretKey ::XPrv ( DescriptorXKey {
origin : Some ( key_source ) ,
xkey : derived_xprv ,
derivation_path : BdkDerivationPath ::default ( ) ,
wildcard : descriptor_x_key . wildcard ,
} ) ;
Ok ( Arc ::new ( Self {
descriptor_secret_key_mutex : Mutex ::new ( derived_descriptor_secret_key ) ,
} ) )
}
BdkDescriptorSecretKey ::SinglePriv ( _ ) = > {
unreachable! ( )
}
}
}
fn extend ( & self , path : Arc < DerivationPath > ) -> Arc < Self > {
let descriptor_secret_key = self . descriptor_secret_key_mutex . lock ( ) . unwrap ( ) ;
let path = path . derivation_path_mutex . lock ( ) . unwrap ( ) . deref ( ) . clone ( ) ;
match descriptor_secret_key . deref ( ) {
BdkDescriptorSecretKey ::XPrv ( descriptor_x_key ) = > {
let extended_path = descriptor_x_key . derivation_path . extend ( path ) ;
let extended_descriptor_secret_key = BdkDescriptorSecretKey ::XPrv ( DescriptorXKey {
origin : descriptor_x_key . origin . clone ( ) ,
xkey : descriptor_x_key . xkey ,
derivation_path : extended_path ,
wildcard : descriptor_x_key . wildcard ,
} ) ;
Arc ::new ( Self {
descriptor_secret_key_mutex : Mutex ::new ( extended_descriptor_secret_key ) ,
} )
}
BdkDescriptorSecretKey ::SinglePriv ( _ ) = > {
unreachable! ( )
}
}
}
fn as_public ( & self ) -> Arc < DescriptorPublicKey > {
let secp = Secp256k1 ::new ( ) ;
let descriptor_public_key = self
. descriptor_secret_key_mutex
. lock ( )
. unwrap ( )
. as_public ( & secp )
. unwrap ( ) ;
Arc ::new ( DescriptorPublicKey {
descriptor_public_key_mutex : Mutex ::new ( descriptor_public_key ) ,
} )
}
fn as_string ( & self ) -> String {
self . descriptor_secret_key_mutex . lock ( ) . unwrap ( ) . to_string ( )
}
}
struct DescriptorPublicKey {
descriptor_public_key_mutex : Mutex < BdkDescriptorPublicKey > ,
}
impl DescriptorPublicKey {
fn derive ( & self , path : Arc < DerivationPath > ) -> Result < Arc < Self > , BdkError > {
let secp = Secp256k1 ::new ( ) ;
let descriptor_public_key = self . descriptor_public_key_mutex . lock ( ) . unwrap ( ) ;
let path = path . derivation_path_mutex . lock ( ) . unwrap ( ) . deref ( ) . clone ( ) ;
match descriptor_public_key . deref ( ) {
BdkDescriptorPublicKey ::XPub ( descriptor_x_key ) = > {
let derived_xpub = descriptor_x_key . xkey . derive_pub ( & secp , & path ) ? ;
let key_source = match descriptor_x_key . origin . clone ( ) {
Some ( ( fingerprint , origin_path ) ) = > ( fingerprint , origin_path . extend ( path ) ) ,
None = > ( descriptor_x_key . xkey . fingerprint ( ) , path ) ,
} ;
let derived_descriptor_public_key = BdkDescriptorPublicKey ::XPub ( DescriptorXKey {
origin : Some ( key_source ) ,
xkey : derived_xpub ,
derivation_path : BdkDerivationPath ::default ( ) ,
wildcard : descriptor_x_key . wildcard ,
} ) ;
Ok ( Arc ::new ( Self {
descriptor_public_key_mutex : Mutex ::new ( derived_descriptor_public_key ) ,
} ) )
}
BdkDescriptorPublicKey ::SinglePub ( _ ) = > {
unreachable! ( )
}
}
}
fn extend ( & self , path : Arc < DerivationPath > ) -> Arc < Self > {
let descriptor_public_key = self . descriptor_public_key_mutex . lock ( ) . unwrap ( ) ;
let path = path . derivation_path_mutex . lock ( ) . unwrap ( ) . deref ( ) . clone ( ) ;
match descriptor_public_key . deref ( ) {
BdkDescriptorPublicKey ::XPub ( descriptor_x_key ) = > {
let extended_path = descriptor_x_key . derivation_path . extend ( path ) ;
let extended_descriptor_public_key = BdkDescriptorPublicKey ::XPub ( DescriptorXKey {
origin : descriptor_x_key . origin . clone ( ) ,
xkey : descriptor_x_key . xkey ,
derivation_path : extended_path ,
wildcard : descriptor_x_key . wildcard ,
} ) ;
Arc ::new ( Self {
descriptor_public_key_mutex : Mutex ::new ( extended_descriptor_public_key ) ,
} )
}
BdkDescriptorPublicKey ::SinglePub ( _ ) = > {
unreachable! ( )
}
}
}
fn as_string ( & self ) -> String {
self . descriptor_public_key_mutex . lock ( ) . unwrap ( ) . to_string ( )
}
}
uniffi ::deps ::static_assertions ::assert_impl_all! ( Wallet : Sync , Send ) ;
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
@@ -966,7 +674,7 @@ uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send);
// crate.
#[ cfg(test) ]
mod test {
use crate ::* ;
use crate ::{ TxBuilder , Wallet } ;
use bdk ::bitcoin ::Address ;
use bdk ::bitcoin ::Network ::Testnet ;
use bdk ::wallet ::get_funded_wallet ;
@@ -1026,96 +734,4 @@ mod test {
let output_value = psbt . unsigned_tx . output . get ( 0 ) . cloned ( ) . unwrap ( ) . value ;
assert_eq! ( output_value , 49_890_ u64 ) ; // input - fee
}
fn get_descriptor_secret_key ( ) -> DescriptorSecretKey {
let mnemonic =
" chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect " . to_string ( ) ;
DescriptorSecretKey ::new ( Network ::Testnet , mnemonic , None ) . unwrap ( )
}
fn derive_dsk (
key : & DescriptorSecretKey ,
path : & str ,
) -> Result < Arc < DescriptorSecretKey > , BdkError > {
let path = Arc ::new ( DerivationPath ::new ( path . to_string ( ) ) . unwrap ( ) ) ;
key . derive ( path )
}
fn extend_dsk ( key : & DescriptorSecretKey , path : & str ) -> Arc < DescriptorSecretKey > {
let path = Arc ::new ( DerivationPath ::new ( path . to_string ( ) ) . unwrap ( ) ) ;
key . extend ( path )
}
fn derive_dpk (
key : & DescriptorPublicKey ,
path : & str ,
) -> Result < Arc < DescriptorPublicKey > , BdkError > {
let path = Arc ::new ( DerivationPath ::new ( path . to_string ( ) ) . unwrap ( ) ) ;
key . derive ( path )
}
fn extend_dpk ( key : & DescriptorPublicKey , path : & str ) -> Arc < DescriptorPublicKey > {
let path = Arc ::new ( DerivationPath ::new ( path . to_string ( ) ) . unwrap ( ) ) ;
key . extend ( path )
}
#[ test ]
fn test_generate_descriptor_secret_key ( ) {
let master_dsk = get_descriptor_secret_key ( ) ;
assert_eq! ( master_dsk . as_string ( ) , " tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/* " ) ;
assert_eq! ( master_dsk . as_public ( ) . as_string ( ) , " tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/* " ) ;
}
#[ test ]
fn test_derive_self ( ) {
let master_dsk = get_descriptor_secret_key ( ) ;
let derived_dsk : & DescriptorSecretKey = & derive_dsk ( & master_dsk , " m " ) . unwrap ( ) ;
assert_eq! ( derived_dsk . as_string ( ) , " [d1d04177]tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/* " ) ;
let master_dpk : & DescriptorPublicKey = & master_dsk . as_public ( ) ;
let derived_dpk : & DescriptorPublicKey = & derive_dpk ( master_dpk , " m " ) . unwrap ( ) ;
assert_eq! ( derived_dpk . as_string ( ) , " [d1d04177]tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/* " ) ;
}
#[ test ]
fn test_derive_descriptors_keys ( ) {
let master_dsk = get_descriptor_secret_key ( ) ;
let derived_dsk : & DescriptorSecretKey = & derive_dsk ( & master_dsk , " m/0 " ) . unwrap ( ) ;
assert_eq! ( derived_dsk . as_string ( ) , " [d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/* " ) ;
let master_dpk : & DescriptorPublicKey = & master_dsk . as_public ( ) ;
let derived_dpk : & DescriptorPublicKey = & derive_dpk ( master_dpk , " m/0 " ) . unwrap ( ) ;
assert_eq! ( derived_dpk . as_string ( ) , " [d1d04177/0]tpubD9oaCiP1MPmQdndm7DCD3D3QU34pWd6BbKSRedoZF1UJcNhEk3PJwkALNYkhxeTKL29oGNR7psqvT1KZydCGqUDEKXN6dVQJY2R8ooLPy8m/* " ) ;
}
#[ test ]
fn test_extend_descriptor_keys ( ) {
let master_dsk = get_descriptor_secret_key ( ) ;
let extended_dsk : & DescriptorSecretKey = & extend_dsk ( & master_dsk , " m/0 " ) ;
assert_eq! ( extended_dsk . as_string ( ) , " tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/0/* " ) ;
let master_dpk : & DescriptorPublicKey = & master_dsk . as_public ( ) ;
let extended_dpk : & DescriptorPublicKey = & extend_dpk ( master_dpk , " m/0 " ) ;
assert_eq! ( extended_dpk . as_string ( ) , " tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/0/* " ) ;
}
#[ test ]
fn test_derive_and_extend_descriptor_secret_key ( ) {
let master_dsk = get_descriptor_secret_key ( ) ;
// derive DescriptorSecretKey with path "m/0" from master
let derived_dsk : & DescriptorSecretKey = & derive_dsk ( & master_dsk , " m/0 " ) . unwrap ( ) ;
assert_eq! ( derived_dsk . as_string ( ) , " [d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/* " ) ;
// extend derived_dsk with path "m/0"
let extended_dsk : & DescriptorSecretKey = & extend_dsk ( derived_dsk , " m/0 " ) ;
assert_eq! ( extended_dsk . as_string ( ) , " [d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/0/* " ) ;
}
#[ test ]
fn test_derive_hardened_path_using_public ( ) {
let master_dpk = get_descriptor_secret_key ( ) . as_public ( ) ;
let derived_dpk = & derive_dpk ( & master_dpk , " m/84h/1h/0h " ) ;
assert! ( derived_dpk . is_err ( ) ) ;
}
}