diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml index 4274563..43b21ab 100644 --- a/.github/workflows/cont_integration.yml +++ b/.github/workflows/cont_integration.yml @@ -39,8 +39,10 @@ jobs: if: matrix.rust == '1.63.0' run: | cargo update -p home:0.5.11 --precise 0.5.5 - cargo update -p tokio:1.43.0 --precise 1.38.1 cargo update -p cc --precise 1.0.105 + cargo update -p rustls:0.23.21 --precise 0.23.12 + cargo update -p time --precise 0.3.20 + cargo update -p zstd-sys --precise "2.0.7+zstd.1.5.4" - name: Build run: cargo build - name: Clippy diff --git a/Cargo.toml b/Cargo.toml index 86a22f1..5a0d5cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bdk-reserves" -version = "0.30.1" +version = "1.0.0" authors = ["Richard Ulrich "] edition = "2018" description = "Proof of reserves for bitcoin dev kit" @@ -10,12 +10,15 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/bitcoindevkit/bdk-reserves" [dependencies] -bdk = { version = "0.30.1", default-features = false, features = ["std"] } +bdk_wallet = { version = "1.0.0", default-features = false, features = ["std"] } bitcoinconsensus = "0.19.0-3" log = "^0.4" +# same version as in rust-bitcoin - I couldn't find a re-export +units = { package = "bitcoin-units", version = "0.1.0", default-features = false, features = ["alloc"] } + [dev-dependencies] rstest = "^0.11" -bdk-testutils = "^0.4" -bdk = { version = "0.30.1", default-features = true } -electrsd = { version = "0.24", features = ["bitcoind_22_0", "electrs_0_9_1"] } +bdk_wallet = { version = "1.0.0", default-features = true, features = ["test-utils"] } +bdk_electrum = "0.20.1" +electrsd = { version = "0.29", features = ["bitcoind_22_1", "electrs_0_9_1"] } diff --git a/Dockerfile b/Dockerfile index 00e33fe..fe398ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,4 @@ -<<<<<<< HEAD -FROM rust:1.70-bookworm -======= FROM rust:1.81-bookworm ->>>>>>> d2a9c38 (upgrading to match bdk v0.30) ARG http_proxy ENV http_proxy=$http_proxy ENV https_proxy=$http_proxy diff --git a/Makefile b/Makefile index 304ebea..ead51df 100644 --- a/Makefile +++ b/Makefile @@ -18,8 +18,10 @@ test_63: builder_63 rm -f Cargo.lock $(DOCKER_RUN) ${TAG_63} cargo test || true $(DOCKER_RUN) ${TAG_63} cargo update -p home:0.5.11 --precise 0.5.5 || true - $(DOCKER_RUN) ${TAG_63} cargo update -p tokio:1.43.0 --precise 1.38.1 || true $(DOCKER_RUN) ${TAG_63} cargo update -p cc --precise 1.0.105 || true + $(DOCKER_RUN) ${TAG_63} cargo update -p rustls:0.23.21 --precise 0.23.12 || true + $(DOCKER_RUN) ${TAG_63} cargo update -p time --precise 0.3.20 || true + $(DOCKER_RUN) ${TAG_63} cargo update -p zstd-sys --precise "2.0.7+zstd.1.5.4" || true $(DOCKER_RUN) ${TAG_63} cargo test run: builder diff --git a/README.md b/README.md index e69502f..f52d6b0 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,11 @@ This library should always compile with Rust **1.63.0**. To build with the MSRV you will need to pin the below dependencies: ```shell -cargo update -p home:0.5.9 --precise 0.5.5 -cargo update -p tokio:1.39.3 --precise 1.38.1 +cargo update -p home:0.5.11 --precise 0.5.5 cargo update -p cc --precise 1.0.105 +cargo update -p rustls:0.23.21 --precise 0.23.12 +cargo update -p time --precise 0.3.20 +cargo update -p zstd-sys --precise "2.0.7+zstd.1.5.4" ``` ## Contribution diff --git a/src/lib.rs b/src/lib.rs index 04703ff..6bc133a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,6 @@ // You may not use this file except in accordance with one or both of these // licenses. -pub extern crate bdk; +pub extern crate bdk_wallet; pub mod reserves; diff --git a/src/reserves.rs b/src/reserves.rs index a35d20d..c609a70 100644 --- a/src/reserves.rs +++ b/src/reserves.rs @@ -18,24 +18,25 @@ //! https://github.com/bitcoin/bips/blob/master/bip-0127.mediawiki //! https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki -use bdk::bitcoin::blockdata::opcodes; -use bdk::bitcoin::blockdata::script::{Builder, Script, ScriptBuf}; -use bdk::bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut}; -use bdk::bitcoin::consensus::encode::serialize; -use bdk::bitcoin::hash_types::{PubkeyHash, Txid}; -use bdk::bitcoin::hashes::{hash160, sha256d, Hash}; -use bdk::bitcoin::psbt::{Input, PartiallySignedTransaction as PSBT}; -use bdk::bitcoin::sighash::EcdsaSighashType; -use bdk::bitcoin::Sequence; -use bdk::database::BatchDatabase; -use bdk::wallet::tx_builder::TxOrdering; -use bdk::wallet::Wallet; -use bdk::Error; +use bdk_wallet::bitcoin::blockdata::opcodes; +use bdk_wallet::bitcoin::blockdata::script::{Builder, Script, ScriptBuf}; +use bdk_wallet::bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut}; +use bdk_wallet::bitcoin::consensus::encode::serialize; +use bdk_wallet::bitcoin::hash_types::Txid; +use bdk_wallet::bitcoin::hashes::{hash160, sha256d, Hash}; +use bdk_wallet::bitcoin::psbt::ExtractTxError; +use bdk_wallet::bitcoin::psbt::{Input, Psbt}; +use bdk_wallet::bitcoin::sighash::EcdsaSighashType; +use bdk_wallet::bitcoin::{Amount, PubkeyHash, Sequence}; +use bdk_wallet::chain::ChainPosition; +use bdk_wallet::{error::CreateTxError, signer::SignerError}; +use bdk_wallet::{AddForeignUtxoError, TxOrdering, Wallet}; +use units::weight::Weight; /// The API for proof of reserves pub trait ProofOfReserves { /// Create a proof for all spendable UTXOs in a wallet - fn create_proof(&self, message: &str) -> Result; + fn create_proof(&mut self, message: &str) -> Result; /// Make sure this is a proof, and not a spendable transaction. /// Make sure the proof is valid. @@ -47,10 +48,10 @@ pub trait ProofOfReserves { /// Returns the spendable amount of the proof. fn verify_proof( &self, - psbt: &PSBT, + psbt: &Psbt, message: &str, - max_block_height: Option, - ) -> Result; + max_block_height: Option, + ) -> Result; } /// Proof error @@ -80,41 +81,60 @@ pub enum ProofError { OutpointNotFound(usize), /// Failed to retrieve the block height of a Tx or UTXO MissingConfirmationInfo, - /// A wrapped BDK Error - BdkError(Error), + /// Error adding foreign UTXO + ForeignUtxo(AddForeignUtxoError), + /// Failed to create a transaction + TxError(CreateTxError), + /// Failed to extract TX from a PSBT + TxExtraction(ExtractTxError), + /// Failed to construct a Wallet + Wallet(bdk_wallet::descriptor::error::Error), + /// Failed to sign a transaction + Sign(SignerError), } -impl From for ProofError { - fn from(error: bdk::Error) -> Self { - ProofError::BdkError(error) +impl From for ProofError { + fn from(error: AddForeignUtxoError) -> Self { + ProofError::ForeignUtxo(error) } } -impl From for bdk::Error { - fn from(error: ProofError) -> Self { - if let ProofError::BdkError(err) = error { - err - } else { - bdk::Error::Generic(format!("{:?}", error)) - } +impl From for ProofError { + fn from(error: CreateTxError) -> Self { + ProofError::TxError(error) } } -impl ProofOfReserves for Wallet -where - D: BatchDatabase, -{ - fn create_proof(&self, message: &str) -> Result { +impl From for ProofError { + fn from(error: ExtractTxError) -> Self { + ProofError::TxExtraction(error) + } +} + +impl From for ProofError { + fn from(error: bdk_wallet::descriptor::error::Error) -> Self { + ProofError::Wallet(error) + } +} + +impl From for ProofError { + fn from(error: SignerError) -> Self { + ProofError::Sign(error) + } +} + +impl ProofOfReserves for Wallet { + fn create_proof(&mut self, message: &str) -> Result { if message.is_empty() { return Err(ProofError::ChallengeInputMismatch); } let challenge_txin = challenge_txin(message); let challenge_psbt_inp = Input { witness_utxo: Some(TxOut { - value: 0, + value: Amount::from_sat(0), script_pubkey: Builder::new().push_opcode(opcodes::OP_TRUE).into_script(), }), - final_script_sig: Some(Script::empty().into()), /* "finalize" the input with an empty scriptSig */ + final_script_sig: Some(Script::new().into()), /* "finalize" the input with an empty scriptSig */ ..Default::default() }; @@ -124,38 +144,43 @@ where let mut builder = self.build_tx(); builder .drain_wallet() - .add_foreign_utxo(challenge_txin.previous_output, challenge_psbt_inp, 42)? - .fee_absolute(0) + .add_foreign_utxo( + challenge_txin.previous_output, + challenge_psbt_inp, + Weight::from_wu(42), + )? + .fee_absolute(Amount::from_sat(0)) .only_witness_utxo() .current_height(0) .drain_to(out_script_unspendable) .ordering(TxOrdering::Untouched); - let (psbt, _details) = builder.finish().map_err(ProofError::BdkError)?; + let psbt = builder.finish()?; Ok(psbt) } fn verify_proof( &self, - psbt: &PSBT, + psbt: &Psbt, message: &str, - max_block_height: Option, - ) -> Result { + max_block_height: Option, + ) -> Result { // verify the proof UTXOs are still spendable - let unspents = match self.list_unspent() { - Ok(utxos) => utxos, - Err(err) => return Err(ProofError::BdkError(err)), - }; - let unspents = unspents - .iter() + let unspents = self + .list_unspent() .map(|utxo| { if max_block_height.is_none() { Ok((utxo, None)) } else { - let tx_details = self.get_tx(&utxo.outpoint.txid, false)?; + let tx_details = self.get_tx(utxo.outpoint.txid); + println!("{:?}", tx_details); if let Some(tx_details) = tx_details { - if let Some(conf_time) = tx_details.confirmation_time { - Ok((utxo, Some(conf_time.height))) + if let ChainPosition::<_>::Confirmed { + anchor, + transitively: _, + } = tx_details.chain_position + { + Ok((utxo, Some(anchor.block_id.height as usize))) } else { Ok((utxo, None)) } @@ -168,7 +193,7 @@ where let outpoints = unspents .iter() .filter(|(_utxo, block_height)| { - block_height.unwrap_or(u32::MAX) <= max_block_height.unwrap_or(u32::MAX) + block_height.unwrap_or(usize::MAX) <= max_block_height.unwrap_or(usize::MAX) }) .map(|(utxo, _)| (utxo.outpoint, utxo.txout.clone())) .collect(); @@ -185,11 +210,18 @@ where /// Since the caller provides the outpoints, he is also responsible to make sure they have enough confirmations. /// Returns the spendable amount of the proof. pub fn verify_proof( - psbt: &PSBT, + psbt: &Psbt, message: &str, outpoints: Vec<(OutPoint, TxOut)>, -) -> Result { - let tx = psbt.clone().extract_tx(); +) -> Result { + if psbt.inputs.len() != 1 && psbt.unsigned_tx.output.len() != 1 { + return Err(ProofError::WrongNumberOfOutputs); + } + if psbt.unsigned_tx.input.len() <= 1 && psbt.inputs.len() <= 1 { + return Err(ProofError::WrongNumberOfInputs); + } + + let tx = psbt.clone().extract_tx()?; if tx.output.len() != 1 { return Err(ProofError::WrongNumberOfOutputs); @@ -241,7 +273,7 @@ pub fn verify_proof( if let Some(op) = outpoints.iter().find(|op| op.0 == tx_in.previous_output) { op.1.value } else { - 0 + Amount::from_sat(0) } }) .sum(); @@ -279,7 +311,7 @@ pub fn verify_proof( i, bitcoinconsensus::verify( txout.script_pubkey.to_bytes().as_slice(), - txout.value, + txout.value.to_sat(), &serialized_tx, i, ) @@ -312,33 +344,48 @@ fn challenge_txin(message: &str) -> TxIn { #[cfg(test)] mod test { use super::*; - use bdk::bitcoin::secp256k1::ecdsa::{SerializedSignature, Signature}; - use bdk::bitcoin::{Address, Network, Witness}; - use bdk::wallet::get_funded_wallet; + use bdk_wallet::bitcoin::{Address, Network, Witness}; + use bdk_wallet::test_utils::get_funded_wallet_single; + use bdk_wallet::SignOptions; use std::str::FromStr; #[test] fn test_proof() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (mut wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; - let psbt = wallet.create_proof(message).unwrap(); + let mut psbt = wallet.create_proof(message).unwrap(); let psbt_b64 = psbt.to_string(); - let expected = r#"cHNidP8BAH4BAAAAAmw1RvG4UzfnSafpx62EPTyha6VslP0Er7n3TxjEpeBeAAAAAAD/////2johM0znoXIXT1lg+ySrvGrtq1IGXPJzpfi/emkV9iIAAAAAAP////8BUMMAAAAAAAAZdqkUn3/QltN+0sDj9/DPySS+70/862iIrAAAAAAAAQEKAAAAAAAAAAABUQEHAAABAR9QwwAAAAAAABYAFOzlJlcQU9qGRUyeBmd56vnRUC5qIgYDKwVYB4vsOGlKhJM9ZZMD4lddrn6RaFkRRUEVv9ZEh+ME7OUmVwAA"#; + let expected = r#"cHNidP8BAH4BAAAAAmw1RvG4UzfnSafpx62EPTyha6VslP0Er7n3TxjEpeBeAAAAAAD/////MQvsP2eDTCk3vWfQJ50IOFWLwuTHPsnYikR1hosdK0sAAAAAAP3///8BUMMAAAAAAAAZdqkUn3/QltN+0sDj9/DPySS+70/862iIrAAAAAAAAQEKAAAAAAAAAAABUQEHAAABAR9QwwAAAAAAABYAFOzlJlcQU9qGRUyeBmd56vnRUC5qIgYDKwVYB4vsOGlKhJM9ZZMD4lddrn6RaFkRRUEVv9ZEh+ME7OUmVwAA"#; + + assert_eq!(psbt_b64, expected); + + let signopts = SignOptions { + trust_witness_utxo: true, + ..Default::default() + }; + wallet.sign(&mut psbt, signopts).unwrap(); + + let spendable = wallet.verify_proof(&psbt, message, None).unwrap(); + assert_eq!(spendable, Amount::from_sat(50_000)); + + let psbt_b64 = psbt.to_string(); + + let expected = r#"cHNidP8BAH4BAAAAAmw1RvG4UzfnSafpx62EPTyha6VslP0Er7n3TxjEpeBeAAAAAAD/////MQvsP2eDTCk3vWfQJ50IOFWLwuTHPsnYikR1hosdK0sAAAAAAP3///8BUMMAAAAAAAAZdqkUn3/QltN+0sDj9/DPySS+70/862iIrAAAAAAAAQEKAAAAAAAAAAABUQEHAAABAR9QwwAAAAAAABYAFOzlJlcQU9qGRUyeBmd56vnRUC5qAQhrAkcwRAIgR9XtbnBY0jUe9zXI0kCEzEua5pHm6Bal6mCHNBmTTIECIE2NbjpRjD6/nZv72GQ7qPZ1Sdo3BPps0cBN1DOsB+S+ASEDKwVYB4vsOGlKhJM9ZZMD4lddrn6RaFkRRUEVv9ZEh+MAAA=="#; assert_eq!(psbt_b64, expected); } #[test] #[should_panic( - expected = "Descriptor(Miniscript(Unexpected(\"unexpected «Key too short (<66 char), doesn't match any format»\")))" + expected = "\"unexpected «Key too short (<66 char), doesn't match any format»\"" )] fn invalid_descriptor() { let descriptor = "wpkh(cVpPVqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (mut wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let _psbt = wallet.create_proof(message).unwrap(); @@ -348,73 +395,72 @@ mod test { #[should_panic(expected = "ChallengeInputMismatch")] fn empty_message() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (mut wallet, _) = get_funded_wallet_single(descriptor); let message = ""; let _psbt = wallet.create_proof(message).unwrap(); } - fn get_signed_proof() -> PSBT { - let psbt = "cHNidP8BAH4BAAAAAmw1RvG4UzfnSafpx62EPTyha6VslP0Er7n3TxjEpeBeAAAAAAD/////2johM0znoXIXT1lg+ySrvGrtq1IGXPJzpfi/emkV9iIAAAAAAP////8BUMMAAAAAAAAZdqkUn3/QltN+0sDj9/DPySS+70/862iIrAAAAAAAAQEKAAAAAAAAAAABUQEHAAABAR9QwwAAAAAAABYAFOzlJlcQU9qGRUyeBmd56vnRUC5qAQcAAQhrAkcwRAIgDSE4PQ57JDiZ7otGkTqz35bi/e1pexYaYKWaveuvRd4CIFzVB4sAmgtdEVz2vHzs1iXc9iRKJ+KQOQb+C2DtPyvzASEDKwVYB4vsOGlKhJM9ZZMD4lddrn6RaFkRRUEVv9ZEh+MAAA=="; - PSBT::from_str(psbt).unwrap() + fn get_signed_proof() -> Psbt { + let psbt = "cHNidP8BAH4BAAAAAmw1RvG4UzfnSafpx62EPTyha6VslP0Er7n3TxjEpeBeAAAAAAD/////MQvsP2eDTCk3vWfQJ50IOFWLwuTHPsnYikR1hosdK0sAAAAAAP3///8BUMMAAAAAAAAZdqkUn3/QltN+0sDj9/DPySS+70/862iIrAAAAAAAAQEKAAAAAAAAAAABUQEHAAABAR9QwwAAAAAAABYAFOzlJlcQU9qGRUyeBmd56vnRUC5qAQhrAkcwRAIgR9XtbnBY0jUe9zXI0kCEzEua5pHm6Bal6mCHNBmTTIECIE2NbjpRjD6/nZv72GQ7qPZ1Sdo3BPps0cBN1DOsB+S+ASEDKwVYB4vsOGlKhJM9ZZMD4lddrn6RaFkRRUEVv9ZEh+MAAA=="; + Psbt::from_str(psbt).unwrap() } #[test] fn verify_internal() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let psbt = get_signed_proof(); let spendable = wallet.verify_proof(&psbt, message, None).unwrap(); - assert_eq!(spendable, 50_000); + assert_eq!(spendable, Amount::from_sat(50_000)); } #[test] #[should_panic(expected = "NonSpendableInput")] - fn verify_internal_90() { + fn verify_internal_1990() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let psbt = get_signed_proof(); - let spendable = wallet.verify_proof(&psbt, message, Some(90)).unwrap(); - assert_eq!(spendable, 50_000); + let spendable = wallet.verify_proof(&psbt, message, Some(1990)).unwrap(); + assert_eq!(spendable, Amount::from_sat(0)); } #[test] - fn verify_internal_100() { + fn verify_internal_2000() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let psbt = get_signed_proof(); - let spendable = wallet.verify_proof(&psbt, message, Some(100)).unwrap(); - assert_eq!(spendable, 50_000); + let spendable = wallet.verify_proof(&psbt, message, Some(2000)).unwrap(); + assert_eq!(spendable, Amount::from_sat(50_000)); } #[test] fn verify_external() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let psbt = get_signed_proof(); - let unspents = wallet.list_unspent().unwrap(); - let outpoints = unspents - .iter() + let outpoints = wallet + .list_unspent() .map(|utxo| (utxo.outpoint, utxo.txout.clone())) .collect(); let spendable = verify_proof(&psbt, message, outpoints).unwrap(); - assert_eq!(spendable, 50_000); + assert_eq!(spendable, Amount::from_sat(50_000)); } #[test] #[should_panic(expected = "ChallengeInputMismatch")] fn wrong_message() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "Wrong message!"; let psbt = get_signed_proof(); @@ -425,7 +471,7 @@ mod test { #[should_panic(expected = "WrongNumberOfInputs")] fn too_few_inputs() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let mut psbt = get_signed_proof(); @@ -439,12 +485,12 @@ mod test { #[should_panic(expected = "WrongNumberOfOutputs")] fn no_output() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let mut psbt = get_signed_proof(); - psbt.unsigned_tx.output.clear(); psbt.inputs.clear(); + psbt.unsigned_tx.output.clear(); wallet.verify_proof(&psbt, message, None).unwrap(); } @@ -453,7 +499,7 @@ mod test { #[should_panic(expected = "NotSignedInput")] fn missing_signature() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let mut psbt = get_signed_proof(); @@ -467,17 +513,18 @@ mod test { #[should_panic(expected = "SignatureValidation")] fn invalid_signature() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let mut psbt = get_signed_proof(); psbt.inputs[1].final_script_sig = None; - let invalid_signature = Signature::from_str("3045022100f3b7b0b1400287766edfe8ba66bc0412984cdb97da6bb4092d5dc63a84e1da6f02204da10796361dbeaeead8f68a23157dffa23b356ec14ec2c0c384ad68d582bb14").unwrap(); - let invalid_signature = SerializedSignature::from_signature(&invalid_signature); + let invalid_signature = bdk_wallet::bitcoin::secp256k1::ecdsa::Signature::from_str("3045022100f3b7b0b1400287766edfe8ba66bc0412984cdb97da6bb4092d5dc63a84e1da6f02204da10796361dbeaeead8f68a23157dffa23b356ec14ec2c0c384ad68d582bb14").unwrap(); + let invalid_signature = + bdk_wallet::bitcoin::ecdsa::Signature::sighash_all(invalid_signature); let mut invalid_witness = Witness::new(); - invalid_witness.push_bitcoin_signature(&invalid_signature, EcdsaSighashType::All); + invalid_witness.push_ecdsa_signature(&invalid_signature); psbt.inputs[1].final_script_witness = Some(invalid_witness); @@ -488,7 +535,7 @@ mod test { #[should_panic(expected = "UnsupportedSighashType(1)")] fn wrong_sighash_type() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let mut psbt = get_signed_proof(); @@ -508,10 +555,7 @@ mod test { out_script_unspendable ); - let addr_unspendable = Address::new( - Network::Bitcoin, - bdk::bitcoin::address::Payload::PubkeyHash(pkh), - ); + let addr_unspendable = Address::p2pkh(pkh, Network::Bitcoin); assert_eq!( addr_unspendable.to_string(), "1FYMZEHnszCHKTBdFZ2DLrUuk3dGwYKQxh" @@ -519,10 +563,7 @@ mod test { // https://mempool.space/de/address/1FYMZEHnszCHKTBdFZ2DLrUuk3dGwYKQxh // https://bitcoin.stackexchange.com/questions/65969/invalid-public-key-was-spent-how-was-this-possible - let addr_unspendable_testnet = Address::new( - Network::Testnet, - bdk::bitcoin::address::Payload::PubkeyHash(pkh), - ); + let addr_unspendable_testnet = Address::p2pkh(pkh, Network::Testnet); assert_eq!( addr_unspendable_testnet.to_string(), "mv4JrHNmh1dY6ZfEy7zbAmhEc3Dyr8ULqX" @@ -534,7 +575,7 @@ mod test { #[should_panic(expected = "InvalidOutput")] fn invalid_output() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let mut psbt = get_signed_proof(); @@ -550,11 +591,11 @@ mod test { #[should_panic(expected = "InAndOutValueNotEqual")] fn sum_mismatch() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to me."; let mut psbt = get_signed_proof(); - psbt.unsigned_tx.output[0].value = 123; + psbt.unsigned_tx.output[0].value = Amount::from_sat(123); wallet.verify_proof(&psbt, message, None).unwrap(); } diff --git a/tests/mempool.rs b/tests/mempool.rs index 7e1d285..dad6329 100644 --- a/tests/mempool.rs +++ b/tests/mempool.rs @@ -1,40 +1,32 @@ mod regtestenv; -use bdk::bitcoin::Network; -use bdk::blockchain::{electrum::ElectrumBlockchain, Blockchain, GetHeight}; -use bdk::database::memory::MemoryDatabase; -use bdk::electrum_client::Client; -use bdk::wallet::{AddressIndex, SyncOptions, Wallet}; -use bdk::Error; -use bdk::SignOptions; +use bdk_electrum::electrum_client::{Client, ElectrumApi}; +use bdk_electrum::{electrum_client, BdkElectrumClient}; use bdk_reserves::reserves::*; +use bdk_wallet::bitcoin::{Amount, FeeRate, Network}; +use bdk_wallet::{KeychainKind, SignOptions, Wallet}; use regtestenv::RegTestEnv; -fn construct_wallet(desc: &str, network: Network) -> Result, Error> { - let wallet = Wallet::new(desc, None, network, MemoryDatabase::default())?; - - Ok(wallet) -} - #[test] fn unconfirmed() -> Result<(), ProofError> { - let wallet = construct_wallet( - "wpkh(cTTgG6x13nQjAeECaCaDrjrUdcjReZBGspcmNavsnSRyXq7zXT7r)", - Network::Regtest, - )?; + let mut wallet = + Wallet::create_single("wpkh(cTTgG6x13nQjAeECaCaDrjrUdcjReZBGspcmNavsnSRyXq7zXT7r)") + .network(Network::Regtest) + .create_wallet_no_persist()?; let regtestenv = RegTestEnv::new(); - regtestenv.generate(&[&wallet]); - let client = Client::new(regtestenv.electrum_url()).unwrap(); - let blockchain = ElectrumBlockchain::from(client); - wallet.sync(&blockchain, SyncOptions::default())?; + regtestenv.generate(&mut [&mut wallet]); - let balance = wallet.get_balance()?; + let client: BdkElectrumClient = + BdkElectrumClient::new(electrum_client::Client::new(regtestenv.electrum_url()).unwrap()); + + sync(&mut wallet, &client); + let balance = wallet.balance(); assert!( - balance.confirmed > 10_000, + balance.confirmed > Amount::from_sat(10_000), "insufficient balance: {}", balance.confirmed ); - let addr = wallet.get_address(AddressIndex::New).unwrap(); + let addr = wallet.reveal_next_address(KeychainKind::External).address; assert_eq!( addr.to_string(), "bcrt1qexxes4qzr3m6a6mcqrp0d4xexagw08fgy97gss" @@ -42,19 +34,21 @@ fn unconfirmed() -> Result<(), ProofError> { let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 1_000) - .fee_rate(bdk::FeeRate::from_sat_per_vb(2.0)); - let (mut psbt, _) = builder.finish().unwrap(); + .add_recipient(addr.script_pubkey(), Amount::from_sat(1_000)) + .fee_rate(FeeRate::from_sat_per_vb(2).unwrap()); + let mut psbt = builder.finish().unwrap(); let signopts = SignOptions { trust_witness_utxo: true, ..Default::default() }; let finalized = wallet.sign(&mut psbt, signopts.clone())?; assert!(finalized); - blockchain.broadcast(&psbt.extract_tx())?; - wallet.sync(&blockchain, SyncOptions::default())?; + client + .transaction_broadcast(&psbt.extract_tx().unwrap()) + .unwrap(); + sync(&mut wallet, &client); - let new_balance = wallet.get_balance()?; + let new_balance = wallet.balance(); assert_ne!(balance, new_balance); let message = "This belongs to me."; @@ -77,25 +71,26 @@ fn unconfirmed() -> Result<(), ProofError> { #[test] #[should_panic(expected = "NonSpendableInput")] fn confirmed() { - let wallet = construct_wallet( - "wpkh(cTTgG6x13nQjAeECaCaDrjrUdcjReZBGspcmNavsnSRyXq7zXT7r)", - Network::Regtest, - ) - .unwrap(); + let mut wallet = + Wallet::create_single("wpkh(cTTgG6x13nQjAeECaCaDrjrUdcjReZBGspcmNavsnSRyXq7zXT7r)") + .network(Network::Regtest) + .create_wallet_no_persist() + .unwrap(); let regtestenv = RegTestEnv::new(); - regtestenv.generate(&[&wallet]); - let client = Client::new(regtestenv.electrum_url()).unwrap(); - let blockchain = ElectrumBlockchain::from(client); - wallet.sync(&blockchain, SyncOptions::default()).unwrap(); + regtestenv.generate(&mut [&mut wallet]); - let balance = wallet.get_balance().unwrap(); + let client: BdkElectrumClient = + BdkElectrumClient::new(electrum_client::Client::new(regtestenv.electrum_url()).unwrap()); + + sync(&mut wallet, &client); + let balance = wallet.balance(); assert!( - balance.confirmed > 10_000, + balance.confirmed > Amount::from_sat(10_000), "insufficient balance: {}", balance ); - let addr = wallet.get_address(AddressIndex::New).unwrap(); + let addr = wallet.reveal_next_address(KeychainKind::External); assert_eq!( addr.to_string(), "bcrt1qexxes4qzr3m6a6mcqrp0d4xexagw08fgy97gss" @@ -103,19 +98,21 @@ fn confirmed() { let mut builder = wallet.build_tx(); builder - .add_recipient(addr.script_pubkey(), 1_000) - .fee_rate(bdk::FeeRate::from_sat_per_vb(2.0)); - let (mut psbt, _) = builder.finish().unwrap(); + .add_recipient(addr.script_pubkey(), Amount::from_sat(1_000)) + .fee_rate(FeeRate::from_sat_per_vb(2).unwrap()); + let mut psbt = builder.finish().unwrap(); let signopts = SignOptions { trust_witness_utxo: true, ..Default::default() }; let finalized = wallet.sign(&mut psbt, signopts.clone()).unwrap(); assert!(finalized); - blockchain.broadcast(&psbt.extract_tx()).unwrap(); - wallet.sync(&blockchain, SyncOptions::default()).unwrap(); + client + .transaction_broadcast(&psbt.extract_tx().unwrap()) + .unwrap(); + sync(&mut wallet, &client); - let new_balance = wallet.get_balance().unwrap(); + let new_balance = wallet.balance(); assert_ne!(balance, new_balance); let message = "This belongs to me."; @@ -123,8 +120,9 @@ fn confirmed() { let finalized = wallet.sign(&mut psbt, signopts).unwrap(); assert!(finalized); - const CONFIRMATIONS: u32 = 2; - let current_height = blockchain.get_height().unwrap(); + const CONFIRMATIONS: usize = 2; + let current_height = client.inner.block_headers_subscribe().unwrap().height; + assert_eq!(current_height, 117); let max_confirmation_height = current_height - CONFIRMATIONS; let spendable = wallet @@ -132,3 +130,11 @@ fn confirmed() { .unwrap(); assert_eq!(spendable, new_balance.confirmed); } + +fn sync(wallet: &mut Wallet, client: &BdkElectrumClient) { + const BATCH_SIZE: usize = 5; + + let sync_req = wallet.start_sync_with_revealed_spks().build(); + let update = client.sync(sync_req, BATCH_SIZE, true).unwrap(); + wallet.apply_update(update).unwrap(); +} diff --git a/tests/multi_sig.rs b/tests/multi_sig.rs index 2899e67..f1ab277 100644 --- a/tests/multi_sig.rs +++ b/tests/multi_sig.rs @@ -1,13 +1,10 @@ mod regtestenv; -use bdk::bitcoin::key::{PrivateKey, PublicKey}; -use bdk::bitcoin::psbt::PartiallySignedTransaction as PSBT; -use bdk::bitcoin::secp256k1::Secp256k1; -use bdk::bitcoin::Network; -use bdk::database::memory::MemoryDatabase; -use bdk::wallet::{AddressIndex, Wallet}; -use bdk::Error; -use bdk::SignOptions; use bdk_reserves::reserves::*; +use bdk_wallet::bitcoin::key::{PrivateKey, PublicKey}; +use bdk_wallet::bitcoin::psbt::Psbt; +use bdk_wallet::bitcoin::secp256k1::Secp256k1; +use bdk_wallet::bitcoin::Network; +use bdk_wallet::{KeychainKind, SignOptions, Wallet}; use regtestenv::RegTestEnv; use rstest::rstest; @@ -21,7 +18,7 @@ fn construct_multisig_wallet( signer: &PrivateKey, pubkeys: &[PublicKey], script_type: &MultisigType, -) -> Result, Error> { +) -> Result { let secp = Secp256k1::new(); let pub_derived = signer.public_key(&secp); @@ -45,7 +42,9 @@ fn construct_multisig_wallet( desc }) + &postfix; - let wallet = Wallet::new(&desc, None, Network::Regtest, MemoryDatabase::default())?; + let wallet = Wallet::create_single(desc) + .network(Network::Regtest) + .create_wallet_no_persist()?; Ok(wallet) } @@ -53,13 +52,14 @@ fn construct_multisig_wallet( #[rstest] #[case::wsh( MultisigType::Wsh, - "bcrt1qnmhmxkaqqz4lrruhew5mk6zqr0ezstn3stj6c3r2my6hgkescm0s9g276e" + "bcrt1qnmhmxkaqqz4lrruhew5mk6zqr0ezstn3stj6c3r2my6hgkescm0s9g276e", (0, 1) )] -#[case::shwsh(MultisigType::ShWsh, "2NDTiUegP4NwKMnxXm6KdCL1B1WHamhZHC1")] -#[case::p2sh(MultisigType::P2sh, "2N7yrzYXgQzNQQuHNTjcP3iwpzFVsqe6non")] +#[case::shwsh(MultisigType::ShWsh, "2NDTiUegP4NwKMnxXm6KdCL1B1WHamhZHC1", (1, 1))] +#[case::p2sh(MultisigType::P2sh, "2N7yrzYXgQzNQQuHNTjcP3iwpzFVsqe6non", (1, 0))] fn test_proof_multisig( #[case] script_type: MultisigType, #[case] expected_address: &'static str, + #[case] count_mult: (u32, usize), ) -> Result<(), ProofError> { let signer1 = PrivateKey::from_wif("cQCi6JdidZN5HeiHhjE7zZAJ1XJrZbj6MmpVPx8Ri3Kc8UjPgfbn").unwrap(); @@ -75,33 +75,38 @@ fn test_proof_multisig( ]; pubkeys.sort_by_key(|item| item.to_string()); - let wallets = [ - construct_multisig_wallet(&signer1, &pubkeys, &script_type)?, - construct_multisig_wallet(&signer2, &pubkeys, &script_type)?, - construct_multisig_wallet(&signer3, &pubkeys, &script_type)?, - ]; - - wallets.iter().enumerate().for_each(|(i, wallet)| { - let addr = wallet.get_address(AddressIndex::New).unwrap().to_string(); - assert!( - addr == expected_address, - "Wallet {} address is {} instead of {}", - i, - addr, - expected_address - ); - }); + let mut wallet0 = construct_multisig_wallet(&signer1, &pubkeys, &script_type)?; + let mut wallet1 = construct_multisig_wallet(&signer2, &pubkeys, &script_type)?; + let mut wallet2 = construct_multisig_wallet(&signer3, &pubkeys, &script_type)?; + let mut wallets = [&mut wallet0, &mut wallet1, &mut wallet2]; + + wallets + .iter_mut() + .enumerate() + .for_each(|(i, &mut ref mut wallet)| { + let addr = wallet + .reveal_next_address(KeychainKind::External) + .address + .to_string(); + assert!( + addr == expected_address, + "Wallet {} address is {} instead of {}", + i, + addr, + expected_address + ); + }); let regtestenv = RegTestEnv::new(); - regtestenv.generate(&[&wallets[0], &wallets[1], &wallets[2]]); + regtestenv.generate(&mut wallets); wallets.iter().enumerate().for_each(|(i, wallet)| { - let balance = wallet.get_balance().unwrap(); + let balance = wallet.balance(); assert!( - (49_999_999_256..=49_999_999_596).contains(&balance.confirmed), + (49_999_999_256..=49_999_999_598).contains(&balance.confirmed.to_sat()), "balance of wallet {} is {} but should be between 49_999_999_256 and 49_999_999_596", i, - balance + balance.confirmed.to_sat() ); }); @@ -115,7 +120,7 @@ fn test_proof_multisig( ); // returns a tuple with the counts of (partial_sigs, final_script_sig, final_script_witness) - let count_signatures = |psbt: &PSBT| { + let count_signatures = |psbt: &Psbt| { psbt.inputs.iter().fold((0usize, 0, 0), |acc, i| { ( acc.0 + i.partial_sigs.len(), @@ -127,7 +132,7 @@ fn test_proof_multisig( let signopts = SignOptions { trust_witness_utxo: true, - remove_partial_sigs: false, + //remove_partial_sigs: false, ..Default::default() }; let finalized = wallets[0].sign(&mut psbt, signopts.clone())?; @@ -137,7 +142,7 @@ fn test_proof_multisig( let finalized = wallets[1].sign(&mut psbt, signopts.clone())?; assert_eq!( count_signatures(&psbt), - ((num_inp - 1) * 2, num_inp, num_inp - 1) + (0, num_inp.pow(count_mult.0), (num_inp - 1) * count_mult.1) ); assert!(finalized); @@ -145,19 +150,19 @@ fn test_proof_multisig( let finalized = wallets[2].sign(&mut psbt, signopts.clone())?; assert_eq!( count_signatures(&psbt), - ((num_inp - 1) * 2, num_inp, num_inp - 1) + (0, num_inp.pow(count_mult.0), (num_inp - 1) * count_mult.1) ); assert!(finalized); let finalized = wallets[0].finalize_psbt(&mut psbt, signopts)?; assert_eq!( count_signatures(&psbt), - ((num_inp - 1) * 2, num_inp, num_inp - 1) + (0, num_inp.pow(count_mult.0), (num_inp - 1) * count_mult.1) ); assert!(finalized); let spendable = wallets[0].verify_proof(&psbt, message, None)?; - let balance = wallets[0].get_balance()?; + let balance = wallets[0].balance(); assert!( spendable <= balance.confirmed, "spendable ({}) <= balance.confirmed ({})", diff --git a/tests/regtestenv.rs b/tests/regtestenv.rs index 770a2ee..d666b62 100644 --- a/tests/regtestenv.rs +++ b/tests/regtestenv.rs @@ -1,10 +1,9 @@ -use bdk::blockchain::{electrum::ElectrumBlockchain, Blockchain}; -use bdk::database::memory::MemoryDatabase; -use bdk::electrum_client::Client; -use bdk::wallet::{AddressIndex, SyncOptions, Wallet}; -use bdk::SignOptions; +use bdk_electrum::electrum_client::Client; +use bdk_electrum::{electrum_client, BdkElectrumClient}; +use bdk_wallet::bitcoin::{Amount, FeeRate}; +use bdk_wallet::{KeychainKind, SignOptions, Wallet}; use electrsd::bitcoind::bitcoincore_rpc::{ - bitcoin::{network::constants::Network, Address}, + bitcoin::{Address, Network}, RpcApi, }; use electrsd::bitcoind::BitcoinD; @@ -45,10 +44,10 @@ impl RegTestEnv { &self.electrsd.electrum_url } - /// generates some blocks to have some coins to test with - pub fn generate(&self, wallets: &[&Wallet]) { - let addr2 = wallets[0].get_address(AddressIndex::Peek(1)).unwrap(); - let addr1 = wallets[0].get_address(AddressIndex::Peek(0)).unwrap(); + /// generates a couple of blocks to have some coins to test with + pub fn generate(&self, wallets: &mut [&mut Wallet]) { + let addr2 = wallets[0].peek_address(KeychainKind::External, 1); + let addr1 = wallets[0].peek_address(KeychainKind::External, 0); const MY_FOREIGN_ADDR: &str = "mpSFfNURcFTz2yJxBzRY9NhnozxeJ2AUC8"; let foreign_addr = Address::from_str(MY_FOREIGN_ADDR) .unwrap() @@ -60,13 +59,21 @@ impl RegTestEnv { // make the newly mined coins spendable self.generate_to_address(100, &foreign_addr); - let client = Client::new(self.electrum_url()).unwrap(); - let blockchain = ElectrumBlockchain::from(client); - wallets.iter().enumerate().for_each(|(i, wallet)| { - wallet.sync(&blockchain, SyncOptions::default()).unwrap(); - let balance = wallet.get_balance().unwrap(); + let client: BdkElectrumClient = + BdkElectrumClient::new(electrum_client::Client::new(self.electrum_url()).unwrap()); + + const STOP_GAP: usize = 10; + const BATCH_SIZE: usize = 5; + wallets.iter_mut().enumerate().for_each(|(i, wallet)| { + let full_scan_request = wallet.start_full_scan(); + let update = client + .full_scan(full_scan_request, STOP_GAP, BATCH_SIZE, true) + .unwrap(); + wallet.apply_update(update).unwrap(); + + let balance = wallet.balance(); assert!( - balance.confirmed == 50_000_000_000, + balance.confirmed == Amount::from_int_btc(500), "balance of wallet {} is {} but should be 50_000_000_000", i, balance @@ -75,24 +82,28 @@ impl RegTestEnv { let mut builder = wallets[0].build_tx(); builder - .add_recipient(addr1.address.script_pubkey(), 1_000_000) - .fee_rate(bdk::FeeRate::from_sat_per_vb(2.0)); - let (mut psbt, _) = builder.finish().unwrap(); + .add_recipient(addr1.address.script_pubkey(), Amount::from_sat(1_000_000)) + .fee_rate(FeeRate::from_sat_per_vb(2).unwrap()); + let mut psbt = builder.finish().unwrap(); let signopts = SignOptions { ..Default::default() }; let finalized = wallets - .iter() + .iter_mut() .any(|wallet| wallet.sign(&mut psbt, signopts.clone()).unwrap()); assert!(finalized); - blockchain.broadcast(&psbt.extract_tx()).unwrap(); + client + .transaction_broadcast(&psbt.extract_tx().unwrap()) + .unwrap(); // make the newly moved coins spendable self.generate_to_address(6, &foreign_addr); - wallets - .iter() - .for_each(|wallet| wallet.sync(&blockchain, SyncOptions::default()).unwrap()); + wallets.iter_mut().for_each(|wallet| { + let sync_req = wallet.start_sync_with_revealed_spks().build(); + let update = client.sync(sync_req, BATCH_SIZE, true).unwrap(); + wallet.apply_update(update).unwrap(); + }); } fn generate_to_address(&self, blocks: usize, address: &Address) { diff --git a/tests/single_sig.rs b/tests/single_sig.rs index 7b3f44d..b3a4103 100644 --- a/tests/single_sig.rs +++ b/tests/single_sig.rs @@ -1,15 +1,16 @@ -use bdk::wallet::get_funded_wallet; -use bdk::SignOptions; use bdk_reserves::reserves::*; +use bdk_wallet::bitcoin::psbt::Psbt; +use bdk_wallet::test_utils::get_funded_wallet_single; +use bdk_wallet::SignOptions; use rstest::rstest; #[rstest] #[case("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)")] #[case("wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(6)))")] // and(pk(Alice),older(6)) #[case("wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(100000)))")] // and(pk(Alice),after(100000)) -fn test_proof(#[case] descriptor: &'static str) -> Result<(), ProofError> { - let (wallet, _, _) = get_funded_wallet(descriptor); - let balance = wallet.get_balance()?; +fn test_proof_singlesig(#[case] descriptor: &'static str) -> Result<(), ProofError> { + let (mut wallet, _) = get_funded_wallet_single(descriptor); + let balance = wallet.balance(); let message = "This belongs to me."; let mut psbt = wallet.create_proof(message)?; @@ -24,15 +25,22 @@ fn test_proof(#[case] descriptor: &'static str) -> Result<(), ProofError> { &mut psbt, SignOptions { trust_witness_utxo: true, - remove_partial_sigs: false, + //remove_partial_sigs: false, ..Default::default() }, )?; - let num_sigs = psbt - .inputs - .iter() - .fold(0, |acc, i| acc + i.partial_sigs.len()); - assert_eq!(num_sigs, num_inp - 1); + + // returns a tuple with the counts of (partial_sigs, final_script_sig, final_script_witness) + let count_signatures = |psbt: &Psbt| { + psbt.inputs.iter().fold((0usize, 0, 0), |acc, i| { + ( + acc.0 + i.partial_sigs.len(), + acc.1 + usize::from(i.final_script_sig.is_some()), + acc.2 + usize::from(i.final_script_witness.is_some()), + ) + }) + }; + assert_eq!(count_signatures(&psbt), (0, 1, 1)); assert!(finalized); let spendable = wallet.verify_proof(&psbt, message, None)?; diff --git a/tests/tampering.rs b/tests/tampering.rs index a728164..ef25adb 100644 --- a/tests/tampering.rs +++ b/tests/tampering.rs @@ -1,14 +1,15 @@ -use bdk::bitcoin::sighash::EcdsaSighashType; -use bdk::wallet::get_funded_wallet; -use bdk::SignOptions; use bdk_reserves::reserves::*; +use bdk_wallet::bitcoin::sighash::EcdsaSighashType; +use bdk_wallet::bitcoin::Amount; +use bdk_wallet::test_utils::get_funded_wallet_single; +use bdk_wallet::SignOptions; #[test] #[should_panic(expected = "ChallengeInputMismatch")] fn tampered_proof_message() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); - let balance = wallet.get_balance().unwrap(); + let (mut wallet, _) = get_funded_wallet_single(descriptor); + let balance = wallet.balance(); let message_alice = "This belongs to Alice."; let mut psbt_alice = wallet.create_proof(message_alice).unwrap(); @@ -42,7 +43,7 @@ fn tampered_proof_message() { #[should_panic(expected = "UnsupportedSighashType(1)")] fn tampered_proof_sighash_tx() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (mut wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to Alice."; let mut psbt = wallet.create_proof(message).unwrap(); @@ -65,7 +66,7 @@ fn tampered_proof_sighash_tx() { #[should_panic(expected = "InAndOutValueNotEqual")] fn tampered_proof_miner_fee() { let descriptor = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"; - let (wallet, _, _) = get_funded_wallet(descriptor); + let (mut wallet, _) = get_funded_wallet_single(descriptor); let message = "This belongs to Alice."; let mut psbt = wallet.create_proof(message).unwrap(); @@ -77,7 +78,7 @@ fn tampered_proof_miner_fee() { }; // reduce the output value to grant a miner fee - psbt.unsigned_tx.output[0].value -= 100; + psbt.unsigned_tx.output[0].value -= Amount::from_sat(100); let _finalized = wallet.sign(&mut psbt, signopt).unwrap();