Skip to content

Commit 2ce28b2

Browse files
committed
ref(handlers): rebase bip322 feature
- rebase master for bip322 feature - update types to use simple table helper
1 parent 2bf4cfc commit 2ce28b2

4 files changed

Lines changed: 135 additions & 44 deletions

File tree

src/handlers/offline.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ use bdk_wallet::{KeychainKind, SignOptions, Wallet};
1414
use serde_json::json;
1515
use std::collections::BTreeMap;
1616
use std::str::FromStr;
17+
#[cfg(feature = "bip322")]
18+
use {
19+
crate::utils::{parse_address, parse_signature_format},
20+
bdk_bip322::{BIP322, MessageProof, MessageVerificationResult},
21+
bdk_wallet::bitcoin::Address,
22+
};
1723

1824
/// Execute an offline wallet sub-command
1925
///
@@ -269,5 +275,46 @@ pub fn handle_offline_wallet_subcommand(
269275
let result = PsbtResult::new(&final_psbt, wallet_opts.verbose, None);
270276
result.format(pretty)
271277
}
278+
#[cfg(feature = "bip322")]
279+
SignMessage {
280+
message,
281+
signature_type,
282+
address,
283+
utxos,
284+
} => {
285+
let address: Address = parse_address(&address)?;
286+
let signature_format = parse_signature_format(&signature_type)?;
287+
288+
if !wallet.is_mine(address.script_pubkey()) {
289+
return Err(Error::Generic(format!(
290+
"Address {} does not belong to this wallet.",
291+
address
292+
)));
293+
}
294+
295+
let proof: MessageProof =
296+
wallet.sign_message(message.as_str(), signature_format, &address, utxos)?;
297+
298+
Ok(json!({"proof": proof.to_base64()}).to_string())
299+
}
300+
#[cfg(feature = "bip322")]
301+
VerifyMessage {
302+
proof,
303+
message,
304+
address,
305+
} => {
306+
let address: Address = parse_address(&address)?;
307+
let parsed_proof: MessageProof = MessageProof::from_base64(&proof)
308+
.map_err(|e| Error::Generic(format!("Invalid proof: {e}")))?;
309+
310+
let is_valid: MessageVerificationResult =
311+
wallet.verify_message(&parsed_proof, &message, &address)?;
312+
313+
Ok(json!({
314+
"valid": is_valid.valid,
315+
"proven_amount": is_valid.proven_amount.map(|a| a.to_sat()) // optional field
316+
})
317+
.to_string())
318+
}
272319
}
273320
}

src/handlers/types.rs

Lines changed: 70 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use bdk_wallet::bitcoin::{
77
Address, Network, Psbt, Transaction, base64::Engine, consensus::encode::serialize_hex,
88
};
99
use bdk_wallet::{AddressInfo, Balance, LocalOutput, chain::ChainPosition};
10-
use cli_table::{Cell, CellStruct, Style, Table, format::Justify};
10+
use cli_table::{Cell, CellStruct, Style, format::Justify};
1111
use serde::Serialize;
1212
use serde_json::json;
1313

@@ -83,20 +83,16 @@ impl FormatOutput for TransactionListResult {
8383
]);
8484
}
8585

86-
let table = rows
87-
.table()
88-
.title(vec![
89-
"Txid".cell().bold(true),
90-
"Version".cell().bold(true),
91-
"Is RBF".cell().bold(true),
92-
"Input Count".cell().bold(true),
93-
"Output Count".cell().bold(true),
94-
"Total Value (sat)".cell().bold(true),
95-
])
96-
.display()
97-
.map_err(|e| Error::Generic(e.to_string()))?;
86+
let title = vec![
87+
"Txid".cell().bold(true),
88+
"Version".cell().bold(true),
89+
"Is RBF".cell().bold(true),
90+
"Input Count".cell().bold(true),
91+
"Output Count".cell().bold(true),
92+
"Total Value (sat)".cell().bold(true),
93+
];
9894

99-
Ok(format!("{table}"))
95+
simple_table(rows, Some(title))
10096
}
10197
}
10298

@@ -240,22 +236,17 @@ impl FormatOutput for UnspentListResult {
240236
]);
241237
}
242238

243-
let table = rows
244-
.table()
245-
.title(vec![
246-
"Outpoint".cell().bold(true),
247-
"Output (sat)".cell().bold(true),
248-
"Output Address".cell().bold(true),
249-
"Keychain".cell().bold(true),
250-
"Is Spent".cell().bold(true),
251-
"Index".cell().bold(true),
252-
"Block Height".cell().bold(true),
253-
"Block Hash".cell().bold(true),
254-
])
255-
.display()
256-
.map_err(|e| Error::Generic(e.to_string()))?;
257-
258-
Ok(format!("{table}"))
239+
let title = vec![
240+
"Outpoint".cell().bold(true),
241+
"Output (sat)".cell().bold(true),
242+
"Output Address".cell().bold(true),
243+
"Keychain".cell().bold(true),
244+
"Is Spent".cell().bold(true),
245+
"Index".cell().bold(true),
246+
"Block Height".cell().bold(true),
247+
"Block Hash".cell().bold(true),
248+
];
249+
simple_table(rows, Some(title))
259250
}
260251
}
261252

@@ -302,11 +293,7 @@ impl FormatOutput for PsbtResult {
302293
]);
303294
}
304295

305-
let table = rows
306-
.table()
307-
.display()
308-
.map_err(|e| Error::Generic(e.to_string()))?;
309-
Ok(format!("{table}"))
296+
simple_table(rows, None)
310297
}
311298
}
312299

@@ -325,15 +312,11 @@ impl RawPsbt {
325312

326313
impl FormatOutput for RawPsbt {
327314
fn to_table(&self) -> Result<String, Error> {
328-
let table = vec![vec![
315+
let rows = vec![vec![
329316
"Raw Transaction".cell().bold(true),
330317
self.raw_tx.clone().cell(),
331-
]]
332-
.table()
333-
.display()
334-
.map_err(|e| Error::Generic(e.to_string()))?;
335-
336-
Ok(format!("{table}"))
318+
]];
319+
simple_table(rows, None)
337320
}
338321
}
339322

@@ -407,7 +390,6 @@ impl FormatOutput for KeyResult {
407390
}
408391
}
409392

410-
411393
#[derive(Serialize)]
412394
#[serde(transparent)]
413395
pub struct WalletsListResult(pub HashMap<String, WalletConfigInner>);
@@ -444,3 +426,48 @@ impl FormatOutput for WalletsListResult {
444426
)
445427
}
446428
}
429+
430+
431+
#[derive(Serialize)]
432+
pub struct DescriptorResult {
433+
#[serde(skip_serializing_if = "Option::is_none")]
434+
pub descriptor: Option<String>,
435+
436+
#[serde(skip_serializing_if = "Option::is_none")]
437+
pub multipath_descriptor: Option<String>,
438+
439+
#[serde(skip_serializing_if = "Option::is_none")]
440+
pub public_descriptors: Option<KeychainPair<String>>,
441+
442+
#[serde(skip_serializing_if = "Option::is_none")]
443+
pub private_descriptors: Option<KeychainPair<String>>,
444+
445+
#[serde(skip_serializing_if = "Option::is_none")]
446+
pub mnemonic: Option<String>,
447+
}
448+
449+
impl FormatOutput for DescriptorResult {
450+
fn to_table(&self) -> Result<String, Error> {
451+
let mut rows: Vec<Vec<CellStruct>> = vec![];
452+
453+
if let Some(desc) = &self.descriptor {
454+
rows.push(vec!["Descriptor".cell().bold(true), desc.clone().cell()]);
455+
}
456+
if let Some(desc) = &self.multipath_descriptor {
457+
rows.push(vec!["Multipath Descriptor".cell().bold(true), desc.clone().cell()]);
458+
}
459+
if let Some(pub_desc) = &self.public_descriptors {
460+
rows.push(vec!["External Public".cell().bold(true), pub_desc.external.clone().cell()]);
461+
rows.push(vec!["Internal Public".cell().bold(true), pub_desc.internal.clone().cell()]);
462+
}
463+
if let Some(priv_desc) = &self.private_descriptors {
464+
rows.push(vec!["External Private".cell().bold(true), priv_desc.external.clone().cell()]);
465+
rows.push(vec!["Internal Private".cell().bold(true), priv_desc.internal.clone().cell()]);
466+
}
467+
if let Some(mnemonic) = &self.mnemonic {
468+
rows.push(vec!["Mnemonic".cell().bold(true), mnemonic.clone().cell()]);
469+
}
470+
471+
simple_table(rows, None)
472+
}
473+
}

src/handlers/wallets.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use crate::error::BDKCliError as Error;
12
use crate::utils::output::FormatOutput;
23
use crate::{config::WalletConfig, handlers::types::WalletsListResult};
3-
use crate::error::BDKCliError as Error;
44
use std::path::Path;
55

66
/// Handle the top-level `wallets` command (lists all saved wallets)

src/utils/common.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use crate::{commands::WalletOpts, config::WalletConfig, error::BDKCliError as Error};
2+
#[cfg(feature = "bip322")]
3+
use bdk_bip322::SignatureFormat;
24
#[cfg(feature = "cbf")]
35
use bdk_kyoto::{Info, Receiver, UnboundedReceiver, Warning};
46
#[cfg(any(
@@ -190,3 +192,18 @@ pub fn load_wallet_config(
190192

191193
Ok((wallet_opts, network))
192194
}
195+
196+
/// Function to parse the signature format from a string
197+
#[cfg(feature = "bip322")]
198+
pub(crate) fn parse_signature_format(format_str: &str) -> Result<SignatureFormat, Error> {
199+
match format_str.to_lowercase().as_str() {
200+
"legacy" => Ok(SignatureFormat::Legacy),
201+
"simple" => Ok(SignatureFormat::Simple),
202+
"full" => Ok(SignatureFormat::Full),
203+
"fullproofoffunds" => Ok(SignatureFormat::FullProofOfFunds),
204+
_ => Err(Error::Generic(
205+
"Invalid signature format. Use 'legacy', 'simple', 'full', or 'fullproofoffunds'"
206+
.to_string(),
207+
)),
208+
}
209+
}

0 commit comments

Comments
 (0)