Skip to content

Commit a494e27

Browse files
vladimirfomeneevanlinjin
authored andcommitted
feat(example): add RPC wallet example
1 parent f2891f0 commit a494e27

File tree

5 files changed

+184
-0
lines changed

5 files changed

+184
-0
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ members = [
1414
"example-crates/wallet_electrum",
1515
"example-crates/wallet_esplora_blocking",
1616
"example-crates/wallet_esplora_async",
17+
"example-crates/wallet_rpc",
1718
"nursery/tmp_plan",
1819
"nursery/coin_select"
1920
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Example RPC CLI
2+
3+
### Simple Regtest Test
4+
5+
1. Start local regtest bitcoind.
6+
```
7+
mkdir -p /tmp/regtest/bitcoind
8+
bitcoind -regtest -server -fallbackfee=0.0002 -rpcuser=<your-rpc-username> -rpcpassword=<your-rpc-password> -datadir=/tmp/regtest/bitcoind -daemon
9+
```
10+
2. Create a test bitcoind wallet and set bitcoind env.
11+
```
12+
bitcoin-cli -datadir=/tmp/regtest/bitcoind -regtest -rpcuser=<your-rpc-username> -rpcpassword=<your-rpc-password> -named createwallet wallet_name="test"
13+
export RPC_URL=127.0.0.1:18443
14+
export RPC_USER=<your-rpc-username>
15+
export RPC_PASS=<your-rpc-password>
16+
```
17+
3. Get test bitcoind wallet info.
18+
```
19+
bitcoin-cli -rpcwallet="test" -rpcuser=<your-rpc-username> -rpcpassword=<your-rpc-password> -datadir=/tmp/regtest/bitcoind -regtest getwalletinfo
20+
```
21+
4. Get new test bitcoind wallet address.
22+
```
23+
BITCOIND_ADDRESS=$(bitcoin-cli -rpcwallet="test" -datadir=/tmp/regtest/bitcoind -regtest -rpcuser=<your-rpc-username> -rpcpassword=<your-rpc-password> getnewaddress)
24+
echo $BITCOIND_ADDRESS
25+
```
26+
5. Generate 101 blocks with reward to test bitcoind wallet address.
27+
```
28+
bitcoin-cli -datadir=/tmp/regtest/bitcoind -regtest -rpcuser=<your-rpc-username> -rpcpassword=<your-rpc-password> generatetoaddress 101 $BITCOIND_ADDRESS
29+
```
30+
6. Verify test bitcoind wallet balance.
31+
```
32+
bitcoin-cli -rpcwallet="test" -datadir=/tmp/regtest/bitcoind -regtest -rpcuser=<your-rpc-username> -rpcpassword=<your-rpc-password> getbalances
33+
```
34+
7. Set descriptor env and get address from RPC CLI wallet.
35+
```
36+
export DESCRIPTOR="wpkh(tprv8ZgxMBicQKsPfK9BTf82oQkHhawtZv19CorqQKPFeaHDMA4dXYX6eWsJGNJ7VTQXWmoHdrfjCYuDijcRmNFwSKcVhswzqs4fugE8turndGc/1/*)"
37+
cargo run -- --network regtest address next
38+
```
39+
8. Send 5 test bitcoin to RPC CLI wallet.
40+
```
41+
bitcoin-cli -rpcwallet="test" -datadir=/tmp/regtest/bitcoind -regtest -rpcuser=<your-rpc-username> -rpcpassword=<your-rpc-password> sendtoaddress <address> 5
42+
```
43+
9. Sync blockchain with RPC CLI wallet.
44+
```
45+
cargo run -- --network regtest sync
46+
<CNTRL-C to stop syncing>
47+
```
48+
10. Get RPC CLI wallet unconfirmed balances.
49+
```
50+
cargo run -- --network regtest balance
51+
```
52+
11. Generate 1 block with reward to test bitcoind wallet address.
53+
```
54+
bitcoin-cli -datadir=/tmp/regtest/bitcoind -rpcuser=<your-rpc-username> -rpcpassword=<your-rpc-password> -regtest generatetoaddress 10 $BITCOIND_ADDRESS
55+
```
56+
12. Sync the blockchain with RPC CLI wallet.
57+
```
58+
cargo run -- --network regtest sync
59+
<CNTRL-C to stop syncing>
60+
```
61+
13. Get RPC CLI wallet confirmed balances.
62+
```
63+
cargo run -- --network regtest balance
64+
```
65+
14. Get RPC CLI wallet transactions.
66+
```
67+
cargo run -- --network regtest txout list
68+
```

example-crates/wallet_rpc/Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "wallet_rpc"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
bdk = { path = "../../crates/bdk" }
10+
bdk_file_store = { path = "../../crates/file_store" }
11+
bdk_bitcoind_rpc = { path = "../../crates/bitcoind_rpc" }

example-crates/wallet_rpc/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Wallet RPC Example
2+
3+
# To run the wallet example, execute the following code (replace arguments with values that match your setup)
4+
5+
```
6+
cargo run -- <RPC_URL> <RPC_USER> <RPC_PASS> <LOOKAHEAD> <FALLBACK_HEIGHT>
7+
```
8+
9+
Here is the command we used during testing
10+
11+
```
12+
cargo run -- 127.0.0.1:18332 bitcoin password 20 2532323
13+
```

example-crates/wallet_rpc/src/main.rs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use bdk::{
2+
bitcoin::{Address, Network},
3+
wallet::{AddressIndex, Wallet},
4+
SignOptions,
5+
};
6+
use bdk_bitcoind_rpc::{
7+
bitcoincore_rpc::{Auth, Client, RpcApi},
8+
Emitter,
9+
};
10+
use bdk_file_store::Store;
11+
use std::str::FromStr;
12+
13+
const DB_MAGIC: &str = "bdk-rpc-wallet-example";
14+
const SEND_AMOUNT: u64 = 5000;
15+
16+
fn main() -> Result<(), Box<dyn std::error::Error>> {
17+
let args = std::env::args().collect::<Vec<_>>();
18+
let db_path = std::env::temp_dir().join("bdk-rpc-example");
19+
let db = Store::<bdk::wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), db_path)?;
20+
21+
let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
22+
let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
23+
24+
if args.len() < 6 {
25+
println!("Usage: wallet_rpc <RPC_URL> <RPC_USER> <RPC_PASS> <LOOKAHEAD> <FALLBACK_HEIGHT>");
26+
std::process::exit(1);
27+
}
28+
29+
let mut wallet = Wallet::new(
30+
external_descriptor,
31+
Some(internal_descriptor),
32+
db,
33+
Network::Testnet,
34+
)?;
35+
36+
let address = wallet.try_get_address(AddressIndex::New)?;
37+
println!("Generated Address: {}", address);
38+
39+
let balance = wallet.get_balance();
40+
println!("Wallet balance before syncing: {} sats", balance.total());
41+
42+
let rpc_client = Client::new(&args[1], Auth::UserPass(args[2].clone(), args[3].clone()))?;
43+
44+
println!(
45+
"Connected to Bitcoin Core RPC at {:?}",
46+
rpc_client.get_blockchain_info().unwrap()
47+
);
48+
49+
let chain_tip = wallet.latest_checkpoint();
50+
let mut emitter = Emitter::new(&rpc_client, chain_tip, args[5].parse::<u32>()?);
51+
52+
while let Some((height, block)) = emitter.next_block()? {
53+
println!("Applying block {} at height {}", block.block_hash(), height);
54+
wallet.apply_block(block, height)?;
55+
wallet.commit()?;
56+
}
57+
58+
let unconfirmed_txs = emitter.mempool()?;
59+
println!("Applying unconfirmed transactions: ...");
60+
wallet.process_unconfirmed_txs(unconfirmed_txs.iter().map(|(tx, time)| (tx, *time)));
61+
wallet.commit()?;
62+
63+
let balance = wallet.get_balance();
64+
println!("Wallet balance after syncing: {} sats", balance.total());
65+
66+
if balance.total() < SEND_AMOUNT {
67+
println!(
68+
"Please send at least {} sats to the receiving address",
69+
SEND_AMOUNT
70+
);
71+
std::process::exit(1);
72+
}
73+
74+
let faucet_address = Address::from_str("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6")?
75+
.require_network(Network::Testnet)?;
76+
77+
let mut tx_builder = wallet.build_tx();
78+
tx_builder
79+
.add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
80+
.enable_rbf();
81+
82+
let mut psbt = tx_builder.finish()?;
83+
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
84+
assert!(finalized);
85+
86+
let tx = psbt.extract_tx();
87+
rpc_client.send_raw_transaction(&tx)?;
88+
println!("Tx broadcasted! Txid: {}", tx.txid());
89+
90+
Ok(())
91+
}

0 commit comments

Comments
 (0)