Skip to content

Commit ecea7fd

Browse files
committed
add simulator and utxos to wallet
1 parent e8849bc commit ecea7fd

File tree

2 files changed

+140
-64
lines changed

2 files changed

+140
-64
lines changed

examples/example_wallet_hwi_signer/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ version = "0.1.0"
44
edition = "2021"
55
authors.workspace = true
66

7+
[features]
8+
simulator = []
9+
710
[dependencies]
811
bdk_wallet = { path = "../../wallet", features = ["file_store"] }
9-
bdk_bitcoind_rpc = { version = "0.18" }
1012
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
13+
bitbox-api = {version = "0.6.0", features = ["tokio", "simulator"]}
1114

1215
async-hwi = "0.0.27"
1316
anyhow = "1"
17+
18+
[patch.crates-io]
19+
bitbox-api = {git = "https://github.com/sdmg15/bitbox-api-rs", branch = "0.6.0-simulator-patch", features = ["tokio", "simulator"]}
Lines changed: 133 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,161 @@
1-
use std::sync::Arc;
2-
31
use async_hwi::bitbox::api::runtime::TokioRuntime;
42
use async_hwi::bitbox::api::{usb, BitBox};
53
use async_hwi::bitbox::NoiseConfigNoCache;
6-
use bdk_wallet::bitcoin::secp256k1::{All, Secp256k1};
7-
use bdk_wallet::bitcoin::{Network, Psbt};
8-
use bdk_wallet::signer::{SignerError, SignerOrdering, TransactionSigner};
9-
use bdk_wallet::KeychainKind;
10-
use bdk_wallet::{signer::SignerCommon, signer::SignerId, Wallet};
4+
use bdk_wallet::bitcoin::absolute::LockTime;
5+
use bdk_wallet::bitcoin::hashes::Hash;
6+
use bdk_wallet::bitcoin::{
7+
absolute, transaction, Amount, BlockHash, FeeRate, Network, OutPoint, Transaction, TxIn, TxOut,
8+
};
9+
use bdk_wallet::chain::{BlockId, TxUpdate};
10+
use bdk_wallet::file_store::Store;
11+
use bdk_wallet::Wallet;
12+
use bdk_wallet::{KeychainKind, Update};
13+
use std::sync::Arc;
1114

1215
use async_hwi::{bitbox::BitBox02, HWI};
13-
use tokio::runtime::Runtime;
14-
15-
#[derive(Debug)]
16-
struct HwiSigner<T: HWI> {
17-
hw_device: T,
18-
}
1916

20-
impl<T: HWI> HwiSigner<T> {
21-
async fn sign_tx(&self, psbt: &mut Psbt) -> Result<(), SignerError> {
22-
if let Err(e) = self.hw_device.sign_tx(psbt).await {
23-
return Err(SignerError::External(e.to_string()));
24-
}
25-
Ok(())
26-
}
27-
28-
fn new(hw_device: T) -> Self {
29-
HwiSigner { hw_device }
30-
}
31-
32-
fn get_id(&self) -> SignerId {
33-
SignerId::Dummy(0)
17+
const DB_MAGIC: &str = "bdk_wallet_electrum_example";
18+
const SEND_AMOUNT: Amount = Amount::from_sat(5000);
19+
const NETWORK: Network = Network::Regtest;
20+
const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdfCLpvozodGytD3gRUa1M5WQz4kNuDZVf1inhcsSHXRpyLWN3k3Qy3nucrzz5hw2iZiEs6spehpee2WxqfSi31ByRJEu4rZ/84h/1h/0h/0/*)";
21+
const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdfCLpvozodGytD3gRUa1M5WQz4kNuDZVf1inhcsSHXRpyLWN3k3Qy3nucrzz5hw2iZiEs6spehpee2WxqfSi31ByRJEu4rZ/84h/1h/0h/1/*)";
22+
23+
pub fn new_tx(locktime: u32) -> Transaction {
24+
Transaction {
25+
version: transaction::Version::ONE,
26+
lock_time: absolute::LockTime::from_consensus(locktime),
27+
input: vec![],
28+
output: vec![],
3429
}
3530
}
3631

37-
impl<T> SignerCommon for HwiSigner<T>
38-
where
39-
T: Sync + Send + HWI,
40-
{
41-
fn id(&self, _secp: &Secp256k1<All>) -> SignerId {
42-
self.get_id()
43-
}
32+
pub fn insert_checkpoint(wallet: &mut Wallet, block: BlockId) {
33+
let mut cp = wallet.latest_checkpoint();
34+
cp = cp.insert(block);
35+
wallet
36+
.apply_update(Update {
37+
chain: Some(cp),
38+
..Default::default()
39+
})
40+
.unwrap();
4441
}
4542

46-
impl<T> TransactionSigner for HwiSigner<T>
47-
where
48-
T: Sync + Send + HWI,
49-
{
50-
fn sign_transaction(
51-
&self,
52-
psbt: &mut Psbt,
53-
_sign_options: &bdk_wallet::SignOptions,
54-
_secp: &Secp256k1<All>,
55-
) -> Result<(), SignerError> {
56-
let rt = Runtime::new().unwrap();
57-
rt.block_on(self.sign_tx(psbt))?;
58-
Ok(())
59-
}
43+
fn feed_wallet(wallet: &mut Wallet) {
44+
let sendto_address = wallet.next_unused_address(KeychainKind::External);
45+
let change_addr = wallet.next_unused_address(KeychainKind::Internal);
46+
47+
let tx0 = Transaction {
48+
output: vec![TxOut {
49+
value: Amount::from_sat(76_000),
50+
script_pubkey: change_addr.script_pubkey(),
51+
}],
52+
..new_tx(0)
53+
};
54+
55+
let tx1 = Transaction {
56+
input: vec![TxIn {
57+
previous_output: OutPoint {
58+
txid: tx0.compute_txid(),
59+
vout: 0,
60+
},
61+
..Default::default()
62+
}],
63+
output: vec![
64+
TxOut {
65+
value: Amount::from_sat(50_000),
66+
script_pubkey: sendto_address.script_pubkey(),
67+
},
68+
TxOut {
69+
value: Amount::from_sat(25_000),
70+
script_pubkey: change_addr.script_pubkey(),
71+
},
72+
],
73+
..new_tx(0)
74+
};
75+
76+
insert_checkpoint(
77+
wallet,
78+
BlockId {
79+
height: 42,
80+
hash: BlockHash::all_zeros(),
81+
},
82+
);
83+
insert_checkpoint(
84+
wallet,
85+
BlockId {
86+
height: 1_000,
87+
hash: BlockHash::all_zeros(),
88+
},
89+
);
90+
91+
insert_checkpoint(
92+
wallet,
93+
BlockId {
94+
height: 2_000,
95+
hash: BlockHash::all_zeros(),
96+
},
97+
);
98+
99+
wallet
100+
.apply_update(Update {
101+
tx_update: TxUpdate {
102+
txs: vec![Arc::new(tx0), Arc::new(tx1)],
103+
..Default::default()
104+
},
105+
..Default::default()
106+
})
107+
.unwrap();
60108
}
61109

62110
#[tokio::main]
63111
async fn main() -> Result<(), anyhow::Error> {
64-
let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/0/*)";
65-
let change_descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/1/*)";
66-
112+
let db_path = "bdk-signer-example.db";
113+
let mut db = Store::<bdk_wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), db_path)?;
114+
115+
let wallet_opt = Wallet::load()
116+
.descriptor(KeychainKind::External, Some(EXTERNAL_DESC))
117+
.descriptor(KeychainKind::Internal, Some(INTERNAL_DESC))
118+
.extract_keys()
119+
.check_network(NETWORK)
120+
.load_wallet(&mut db)?;
121+
122+
let mut wallet = match wallet_opt {
123+
Some(wallet) => wallet,
124+
None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
125+
.network(NETWORK)
126+
.create_wallet(&mut db)?,
127+
};
128+
129+
// Pairing with Bitbox connected Bitbox device
67130
let noise_config = Box::new(NoiseConfigNoCache {});
68-
let bitbox =
131+
let bitbox = if cfg!(feature = "simulator") {
132+
bitbox_api::BitBox::<TokioRuntime>::from_simulator(None, noise_config).await?
133+
} else {
69134
BitBox::<TokioRuntime>::from_hid_device(usb::get_any_bitbox02().unwrap(), noise_config)
70-
.await?;
135+
.await?
136+
};
71137

72138
let pairing_device = bitbox.unlock_and_pair().await?;
73139
let paired_device = pairing_device.wait_confirm().await?;
140+
141+
// paired_device.restore_from_mnemonic().await?;
74142
let bb = BitBox02::from(paired_device);
143+
let bb = bb.with_network(NETWORK);
75144

76-
let _ = bb.register_wallet("test-wallet", descriptor).await.unwrap();
145+
let receiving_wallet = wallet.next_unused_address(KeychainKind::External);
77146

78-
let bitbox_signer = HwiSigner::new(bb);
147+
feed_wallet(&mut wallet);
79148

80-
let mut wallet = Wallet::create(descriptor, change_descriptor)
81-
.network(Network::Testnet)
82-
.create_wallet_no_persist()?;
149+
let mut tx_builder = wallet.build_tx();
83150

84-
wallet.add_signer(
85-
KeychainKind::External,
86-
SignerOrdering(100),
87-
Arc::new(bitbox_signer),
88-
);
151+
tx_builder
152+
.add_recipient(receiving_wallet.script_pubkey(), SEND_AMOUNT)
153+
.fee_rate(FeeRate::from_sat_per_vb(2).unwrap())
154+
.nlocktime(LockTime::from_height(0).unwrap());
155+
156+
let mut psbt = tx_builder.finish()?;
89157

158+
// Sign with the connected bitbox or any hardware device
159+
bb.sign_tx(&mut psbt).await?;
90160
Ok(())
91161
}

0 commit comments

Comments
 (0)