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:
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",
"env_logger",
"log",
"payjoin",
- "reqwest 0.13.2",
+ "reqwest",
"serde",
"serde_json",
"shlex",
"hex-conservative 0.2.2",
"log",
"minreq",
- "reqwest 0.12.28",
+ "reqwest",
"serde",
"tokio",
]
"bitcoin-ohttp",
"bitcoin_uri",
"http",
- "reqwest 0.12.28",
+ "reqwest",
"serde",
"serde_json",
"tracing",
"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"
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"]
# Internal features
_payjoin-dependencies = ["payjoin", "reqwest", "url"]
+bip322 = ["bdk-bip322"]
# Use this to consensus verify transactions at sync time
verify = []
#[arg(env = "BASE64_PSBT", required = true)]
psbt: Vec<String>,
},
+ /// 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<Vec<OutPoint>>,
+ },
+ /// 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.
))]
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",
&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())
+ }
}
}
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();
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<SignatureFormat, Error> {
+ 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.