Skip to content

Commit 8e89e13

Browse files
feat: implement RPC wallet example
1 parent b69c61f commit 8e89e13

File tree

2 files changed

+130
-2
lines changed

2 files changed

+130
-2
lines changed

example-crates/wallet_rpc/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9+
bdk = { path = "../../crates/bdk" }
10+
bdk_bitcoind_rpc = { path = "../../crates/bitcoind_rpc" }
11+
bdk_file_store = { path = "../../crates/file_store" }
12+
anyhow = "1"
Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,127 @@
1-
fn main() {
2-
println!("Hello, world!");
1+
use bdk::{
2+
bitcoin::{ Network, Address },
3+
chain::{indexed_tx_graph, local_chain, Append},
4+
wallet::{AddressIndex, WalletChangeSet},
5+
Wallet,
6+
SignOptions,
7+
};
8+
use bdk_bitcoind_rpc::{
9+
bitcoincore_rpc::{Auth, Client, RpcApi},
10+
EmittedUpdate, Emitter,
11+
};
12+
use bdk_file_store::Store;
13+
use std::sync::mpsc::sync_channel;
14+
use std::str::FromStr;
15+
16+
const DB_MAGIC: &str = "bdk-rpc-example";
17+
const FALLBACK_HEIGHT: u32 = 0;
18+
const CHANNEL_BOUND: usize = 100;
19+
const SEND_AMOUNT: u64 = 5000;
20+
21+
fn main() -> Result<(), Box<dyn std::error::Error>> {
22+
let db_path = std::env::temp_dir().join("bdk-rpc-example");
23+
let db = Store::<bdk::wallet::ChangeSet>::new_from_path(DB_MAGIC.as_bytes(), db_path)?;
24+
let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
25+
let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
26+
27+
let mut wallet = Wallet::new(
28+
external_descriptor,
29+
Some(internal_descriptor),
30+
db,
31+
Network::Testnet,
32+
)?;
33+
34+
let address = wallet.get_address(AddressIndex::New);
35+
println!("Generated Address: {}", address);
36+
37+
let balance = wallet.get_balance();
38+
println!("Wallet balance before syncing: {} sats", balance.total());
39+
40+
// create a sync function that returns a WalletUpdate
41+
print!("Syncing...");
42+
// implement an internal function that returns a wallet update.
43+
let client = Client::new(
44+
"127.0.0.1:8332",
45+
Auth::UserPass("bitcoin".to_string(), "password".to_string()),
46+
)?;
47+
48+
let (chan, recv) = sync_channel::<(EmittedUpdate, u32)>(CHANNEL_BOUND);
49+
let prev_cp = wallet.latest_checkpoint();
50+
51+
let join_handle = std::thread::spawn(move || -> anyhow::Result<()> {
52+
let mut tip_height = Option::<u32>::None;
53+
54+
let mut emitter = Emitter::new(&client, FALLBACK_HEIGHT, prev_cp);
55+
loop {
56+
let item = emitter.emit_update()?;
57+
let is_mempool = item.is_mempool();
58+
59+
if tip_height.is_none() || is_mempool {
60+
tip_height = Some(client.get_block_count()? as u32);
61+
}
62+
chan.send((item, tip_height.expect("must have tip height")))?;
63+
64+
if !is_mempool {
65+
continue;
66+
} else {
67+
break;
68+
}
69+
}
70+
71+
Ok(())
72+
});
73+
74+
for (item, _) in recv {
75+
let chain_update = item.chain_update();
76+
77+
let mut indexed_changeset = indexed_tx_graph::ChangeSet::default();
78+
79+
let graph_update = item.indexed_tx_graph_update(bdk_bitcoind_rpc::confirmation_time_anchor);
80+
indexed_changeset.append(wallet.mut_indexed_tx_graph().insert_relevant_txs(graph_update));
81+
82+
let chain_changeset = match chain_update {
83+
Some(update) => wallet.mut_local_chain().apply_update(update)?,
84+
None => local_chain::ChangeSet::default(),
85+
};
86+
87+
let mut wallet_changeset = WalletChangeSet::from(chain_changeset);
88+
wallet_changeset.append(WalletChangeSet::from(indexed_changeset));
89+
wallet.stage(wallet_changeset);
90+
wallet.commit()?;
91+
}
92+
93+
let _ = join_handle.join().expect("failed to join chain source thread");
94+
95+
let balance = wallet.get_balance();
96+
println!("Wallet balance before syncing: {} sats", balance.total());
97+
98+
if balance.total() < SEND_AMOUNT {
99+
println!(
100+
"Please send at least {} sats to the receiving address",
101+
SEND_AMOUNT
102+
);
103+
std::process::exit(0);
104+
}
105+
106+
let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?
107+
.require_network(Network::Testnet)?;
108+
109+
let mut tx_builder = wallet.build_tx();
110+
tx_builder
111+
.add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
112+
.enable_rbf();
113+
114+
let (mut psbt, _) = tx_builder.finish()?;
115+
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
116+
assert!(finalized);
117+
118+
let tx = psbt.extract_tx();
119+
let client = Client::new(
120+
"127.0.0.1:8332",
121+
Auth::UserPass("bitcoin".to_string(), "password".to_string()),
122+
)?;
123+
client.send_raw_transaction(&tx)?;
124+
println!("Tx broadcasted! Txid: {}", tx.txid());
125+
126+
Ok(())
3127
}

0 commit comments

Comments
 (0)