From 2eab75c167d5609e1e96374ffb08cdf22a12a09a Mon Sep 17 00:00:00 2001 From: aagbotemi Date: Sat, 26 Apr 2025 13:07:37 +0100 Subject: [PATCH 1/3] bip322: update proof handling for new bdk-bip322 API --- .github/workflows/audit.yml | 7 ++++++ Cargo.lock | 45 +++++++++++-------------------------- Cargo.toml | 8 ++++--- src/commands.rs | 31 +++++++++++++++++++++++++ src/handlers.rs | 45 +++++++++++++++++++++++++++++++++++++ src/utils.rs | 18 +++++++++++++++ 6 files changed, 119 insertions(+), 35 deletions(-) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 93806d53..d5f42dda 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -3,10 +3,17 @@ name: Audit on: push: paths: + # Run if workflow changes + - '.github/workflows/audit.yml' + # Run on changed dependencies - '**/Cargo.toml' - '**/Cargo.lock' + # Run if the configuration file changes + - '**/audit.toml' schedule: - cron: '0 0 * * 0' # Once per week + # Run manually + workflow_dispatch: jobs: diff --git a/Cargo.lock b/Cargo.lock index b491c2e6..3e6dbe4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,10 +192,20 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bdk-bip322" +version = "0.1.0" +source = "git+https://github.com/aagbotemi/bdk-bip322.git?branch=master#aabe67ee68c14992b0d529192641d25ce376b6b1" +dependencies = [ + "bdk_wallet", + "bitcoin", +] + [[package]] name = "bdk-cli" version = "3.0.0" dependencies = [ + "bdk-bip322", "bdk_bitcoind_rpc", "bdk_electrum", "bdk_esplora", @@ -209,7 +219,7 @@ dependencies = [ "env_logger", "log", "payjoin", - "reqwest 0.13.2", + "reqwest", "serde", "serde_json", "shlex", @@ -1002,7 +1012,7 @@ dependencies = [ "hex-conservative 0.2.2", "log", "minreq", - "reqwest 0.12.28", + "reqwest", "serde", "tokio", ] @@ -1866,7 +1876,7 @@ dependencies = [ "bitcoin-ohttp", "bitcoin_uri", "http", - "reqwest 0.12.28", + "reqwest", "serde", "serde_json", "tracing", @@ -2207,35 +2217,6 @@ dependencies = [ "webpki-roots 1.0.5", ] -[[package]] -name = "reqwest" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "ring" version = "0.17.14" diff --git a/Cargo.toml b/Cargo.toml index 7a1a31b4..fb2e54d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,9 +34,10 @@ bdk_esplora = { version = "0.22.1", features = ["async-https", "tokio"], optiona bdk_kyoto = { version = "0.15.4", optional = true } bdk_redb = { version = "0.1.1", optional = true } shlex = { version = "1.3.0", optional = true } -payjoin = { version = "=1.0.0-rc.1", features = ["v1", "v2", "io", "_test-utils"], optional = true} -reqwest = { version = "0.13.2", default-features = false, optional = true } -url = { version = "2.5.8", optional = true } +payjoin = { version = "1.0.0-rc.1", features = ["v1", "v2", "io", "_test-utils"], optional = true} +reqwest = { version = "0.12.23", default-features = false, optional = true } +url = { version = "2.5.4", optional = true } +bdk-bip322 = { git = "https://github.com/aagbotemi/bdk-bip322.git", branch = "master", optional = true } [features] default = ["repl", "sqlite"] @@ -56,6 +57,7 @@ rpc = ["bdk_bitcoind_rpc", "_payjoin-dependencies"] # Internal features _payjoin-dependencies = ["payjoin", "reqwest", "url"] +bip322 = ["bdk-bip322"] # Use this to consensus verify transactions at sync time verify = [] diff --git a/src/commands.rs b/src/commands.rs index 44b13f27..14932e4a 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -461,6 +461,37 @@ pub enum OfflineWalletSubCommand { #[arg(env = "BASE64_PSBT", required = true)] psbt: Vec, }, + /// Sign a message using BIP322 + #[cfg(feature = "bip322")] + SignBip322 { + /// The message to sign + #[arg(long)] + message: String, + /// The signature format (e.g., Legacy, Simple, Full) + #[arg(long, default_value = "simple")] + signature_type: String, + /// Address to sign + #[arg(long)] + address: String, + // Optional list of specific UTXOs for proof-of-funds (only for `FullWithProofOfFunds`) #[arg(long)] + utxos: Option>, + }, + /// Verify a BIP322 signature + #[cfg(feature = "bip322")] + VerifyBip322 { + /// The signature proof to verify + #[arg(long)] + proof: String, + /// The message that was signed + #[arg(long)] + message: String, + /// The signature format (e.g., Legacy, Simple, Full) + #[arg(long, default_value = "simple")] + signature_type: String, + /// The address associated with the signature + #[arg(long)] + address: String, + }, } /// Wallet subcommands that needs a blockchain backend. diff --git a/src/handlers.rs b/src/handlers.rs index a98b172d..ad087263 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -70,6 +70,11 @@ use std::str::FromStr; ))] use std::sync::Arc; +#[cfg(feature = "bip322")] +use crate::error::BDKCliError; +#[cfg(feature = "bip322")] +use bdk_bip322::{BIP322, Bip322Proof, Bip322VerificationResult}; + #[cfg(any( feature = "electrum", feature = "esplora", @@ -592,6 +597,46 @@ pub fn handle_offline_wallet_subcommand( &json!({ "psbt": BASE64_STANDARD.encode(final_psbt.serialize()) }), )?) } + #[cfg(feature = "bip322")] + SignBip322 { + message, + signature_type, + address, + utxos, + } => { + let address: Address = parse_address(&address)?; + let signature_format = parse_signature_format(&signature_type)?; + + let proof: Bip322Proof = wallet + .sign_bip322(message.as_str(), signature_format, &address, utxos) + .map_err(|e| { + BDKCliError::Generic(format!("Failed to sign BIP-322 message: {e}")) + })?; + + Ok(json!({"proof": proof.to_base64()}).to_string()) + } + #[cfg(feature = "bip322")] + VerifyBip322 { + proof, + message, + signature_type, + address, + } => { + let address: Address = parse_address(&address)?; + let signature_format = parse_signature_format(&signature_type)?; + + let parsed_proof: Bip322Proof = Bip322Proof::from_base64(&proof) + .map_err(|e| BDKCliError::Generic(format!("Invalid proof: {e}")))?; + + let is_valid: Bip322VerificationResult = + wallet.verify_bip322(&parsed_proof, &message, signature_format, &address)?; + + Ok(json!({ + "valid": is_valid.valid, + "proven_amount": is_valid.proven_amount.map(|a| a.to_sat()) // optional field + }) + .to_string()) + } } } diff --git a/src/utils.rs b/src/utils.rs index 76e56a0f..cf60c380 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -56,6 +56,9 @@ use bdk_wallet::descriptor::Segwitv0; use bdk_wallet::keys::{GeneratableKey, GeneratedKey, bip39::WordCount}; use serde_json::{Value, json}; +#[cfg(feature = "bip322")] +use bdk_bip322::SignatureFormat; + /// Parse the recipient (Address,Amount) argument from cli input. pub(crate) fn parse_recipient(s: &str) -> Result<(ScriptBuf, u64), String> { let parts: Vec<_> = s.split(':').collect(); @@ -95,6 +98,21 @@ pub(crate) fn parse_address(address_str: &str) -> Result { Ok(unchecked_address.assume_checked()) } +/// Function to parse the signature format from a string +#[cfg(feature = "bip322")] +pub(crate) fn parse_signature_format(format_str: &str) -> Result { + match format_str.to_lowercase().as_str() { + "legacy" => Ok(SignatureFormat::Legacy), + "simple" => Ok(SignatureFormat::Simple), + "full" => Ok(SignatureFormat::Full), + "fullproofoffunds" => Ok(SignatureFormat::FullProofOfFunds), + _ => Err(Error::Generic( + "Invalid signature format. Use 'legacy', 'simple', 'full', or 'fullproofoffunds'" + .to_string(), + )), + } +} + /// Prepare bdk-cli home directory /// /// This function is called to check if [`crate::CliOpts`] datadir is set. From e03f123c480d50517be98cae9b34c4dfa0ff5f92 Mon Sep 17 00:00:00 2001 From: Abiodun Date: Thu, 19 Feb 2026 09:12:42 +0100 Subject: [PATCH 2/3] fix: rename BIP322 commands, validate address ownership before signing and add Bip322Error variant --- .github/workflows/audit.yml | 7 ------- Cargo.lock | 37 +++++++++++++++++++++++++++++++++---- Cargo.toml | 6 +++--- src/commands.rs | 10 ++++------ src/error.rs | 4 ++++ src/handlers.rs | 29 +++++++++++++++-------------- 6 files changed, 59 insertions(+), 34 deletions(-) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index d5f42dda..93806d53 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -3,17 +3,10 @@ name: Audit on: push: paths: - # Run if workflow changes - - '.github/workflows/audit.yml' - # Run on changed dependencies - '**/Cargo.toml' - '**/Cargo.lock' - # Run if the configuration file changes - - '**/audit.toml' schedule: - cron: '0 0 * * 0' # Once per week - # Run manually - workflow_dispatch: jobs: diff --git a/Cargo.lock b/Cargo.lock index 3e6dbe4e..acad9d7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,7 +195,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bdk-bip322" version = "0.1.0" -source = "git+https://github.com/aagbotemi/bdk-bip322.git?branch=master#aabe67ee68c14992b0d529192641d25ce376b6b1" +source = "git+https://github.com/aagbotemi/bdk-bip322.git?branch=master#8aa8195ba378e340cbdf6ab6644b1c641fdfa48c" dependencies = [ "bdk_wallet", "bitcoin", @@ -219,7 +219,7 @@ dependencies = [ "env_logger", "log", "payjoin", - "reqwest", + "reqwest 0.13.2", "serde", "serde_json", "shlex", @@ -1012,7 +1012,7 @@ dependencies = [ "hex-conservative 0.2.2", "log", "minreq", - "reqwest", + "reqwest 0.12.28", "serde", "tokio", ] @@ -1876,7 +1876,7 @@ dependencies = [ "bitcoin-ohttp", "bitcoin_uri", "http", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "tracing", @@ -2217,6 +2217,35 @@ dependencies = [ "webpki-roots 1.0.5", ] +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "ring" version = "0.17.14" diff --git a/Cargo.toml b/Cargo.toml index fb2e54d0..38424d8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,9 +34,9 @@ bdk_esplora = { version = "0.22.1", features = ["async-https", "tokio"], optiona bdk_kyoto = { version = "0.15.4", optional = true } bdk_redb = { version = "0.1.1", optional = true } shlex = { version = "1.3.0", optional = true } -payjoin = { version = "1.0.0-rc.1", features = ["v1", "v2", "io", "_test-utils"], optional = true} -reqwest = { version = "0.12.23", default-features = false, optional = true } -url = { version = "2.5.4", optional = true } +payjoin = { version = "=1.0.0-rc.1", features = ["v1", "v2", "io", "_test-utils"], optional = true} +reqwest = { version = "0.13.2", default-features = false, optional = true } +url = { version = "2.5.8", optional = true } bdk-bip322 = { git = "https://github.com/aagbotemi/bdk-bip322.git", branch = "master", optional = true } [features] diff --git a/src/commands.rs b/src/commands.rs index 14932e4a..80035b08 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -463,7 +463,7 @@ pub enum OfflineWalletSubCommand { }, /// Sign a message using BIP322 #[cfg(feature = "bip322")] - SignBip322 { + SignMessage { /// The message to sign #[arg(long)] message: String, @@ -473,21 +473,19 @@ pub enum OfflineWalletSubCommand { /// Address to sign #[arg(long)] address: String, - // Optional list of specific UTXOs for proof-of-funds (only for `FullWithProofOfFunds`) #[arg(long)] + /// Optional list of specific UTXOs for proof-of-funds (only for `FullWithProofOfFunds`) + #[arg(long)] utxos: Option>, }, /// Verify a BIP322 signature #[cfg(feature = "bip322")] - VerifyBip322 { + VerifyMessage { /// The signature proof to verify #[arg(long)] proof: String, /// The message that was signed #[arg(long)] message: String, - /// The signature format (e.g., Legacy, Simple, Full) - #[arg(long, default_value = "simple")] - signature_type: String, /// The address associated with the signature #[arg(long)] address: String, diff --git a/src/error.rs b/src/error.rs index 3690d4f0..98468666 100644 --- a/src/error.rs +++ b/src/error.rs @@ -140,6 +140,10 @@ pub enum BDKCliError { #[cfg(feature = "payjoin")] #[error("Payjoin create request error: {0}")] PayjoinCreateRequest(#[from] payjoin::send::v2::CreateRequestError), + + #[cfg(feature = "bip322")] + #[error("BIP-322 error: {0}")] + Bip322Error(#[from] bdk_bip322::error::Error), } impl From for BDKCliError { diff --git a/src/handlers.rs b/src/handlers.rs index ad087263..fb1544f0 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -73,7 +73,7 @@ use std::sync::Arc; #[cfg(feature = "bip322")] use crate::error::BDKCliError; #[cfg(feature = "bip322")] -use bdk_bip322::{BIP322, Bip322Proof, Bip322VerificationResult}; +use bdk_bip322::{BIP322, MessageProof, MessageVerificationResult}; #[cfg(any( feature = "electrum", @@ -598,7 +598,7 @@ pub fn handle_offline_wallet_subcommand( )?) } #[cfg(feature = "bip322")] - SignBip322 { + SignMessage { message, signature_type, address, @@ -607,29 +607,30 @@ pub fn handle_offline_wallet_subcommand( let address: Address = parse_address(&address)?; let signature_format = parse_signature_format(&signature_type)?; - let proof: Bip322Proof = wallet - .sign_bip322(message.as_str(), signature_format, &address, utxos) - .map_err(|e| { - BDKCliError::Generic(format!("Failed to sign BIP-322 message: {e}")) - })?; + if !wallet.is_mine(address.script_pubkey()) { + return Err(Error::Generic(format!( + "Address {} does not belong to this wallet.", + address + ))); + } + + let proof: MessageProof = + wallet.sign_message(message.as_str(), signature_format, &address, utxos)?; Ok(json!({"proof": proof.to_base64()}).to_string()) } #[cfg(feature = "bip322")] - VerifyBip322 { + VerifyMessage { proof, message, - signature_type, address, } => { let address: Address = parse_address(&address)?; - let signature_format = parse_signature_format(&signature_type)?; - - let parsed_proof: Bip322Proof = Bip322Proof::from_base64(&proof) + let parsed_proof: MessageProof = MessageProof::from_base64(&proof) .map_err(|e| BDKCliError::Generic(format!("Invalid proof: {e}")))?; - let is_valid: Bip322VerificationResult = - wallet.verify_bip322(&parsed_proof, &message, signature_format, &address)?; + let is_valid: MessageVerificationResult = + wallet.verify_message(&parsed_proof, &message, &address)?; Ok(json!({ "valid": is_valid.valid, From 7c33b33a2bfd9065cbe0768bcb00ce0ec1653686 Mon Sep 17 00:00:00 2001 From: Abiodun Awoyemi Date: Thu, 23 Apr 2026 07:25:01 +0100 Subject: [PATCH 3/3] chore(deps): switch bdk_bip322 from git to crates.io release --- Cargo.lock | 21 +++++++++++---------- Cargo.toml | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index acad9d7d..206c5806 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,20 +192,11 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bdk-bip322" -version = "0.1.0" -source = "git+https://github.com/aagbotemi/bdk-bip322.git?branch=master#8aa8195ba378e340cbdf6ab6644b1c641fdfa48c" -dependencies = [ - "bdk_wallet", - "bitcoin", -] - [[package]] name = "bdk-cli" version = "3.0.0" dependencies = [ - "bdk-bip322", + "bdk_bip322", "bdk_bitcoind_rpc", "bdk_electrum", "bdk_esplora", @@ -231,6 +222,16 @@ dependencies = [ "url", ] +[[package]] +name = "bdk_bip322" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "262ee5f0c1056400b354391cabc541cb732c10488679de6389641e37f7e6c698" +dependencies = [ + "bdk_wallet", + "bitcoin", +] + [[package]] name = "bdk_bitcoind_rpc" version = "0.21.0" diff --git a/Cargo.toml b/Cargo.toml index 38424d8c..20190f4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ shlex = { version = "1.3.0", optional = true } payjoin = { version = "=1.0.0-rc.1", features = ["v1", "v2", "io", "_test-utils"], optional = true} reqwest = { version = "0.13.2", default-features = false, optional = true } url = { version = "2.5.8", optional = true } -bdk-bip322 = { git = "https://github.com/aagbotemi/bdk-bip322.git", branch = "master", optional = true } +bdk_bip322 = { version = "0.1.0", optional = true } [features] default = ["repl", "sqlite"] @@ -57,7 +57,7 @@ rpc = ["bdk_bitcoind_rpc", "_payjoin-dependencies"] # Internal features _payjoin-dependencies = ["payjoin", "reqwest", "url"] -bip322 = ["bdk-bip322"] +bip322 = ["bdk_bip322"] # Use this to consensus verify transactions at sync time verify = []