]> Untitled Git - bdk/commitdiff
Add rpc wallet creation example
authorrajarshimaitra <rajarshi149@gmail.com>
Tue, 22 Mar 2022 06:55:39 +0000 (12:25 +0530)
committerrajarshimaitra <rajarshi149@gmail.com>
Wed, 23 Mar 2022 06:29:51 +0000 (11:59 +0530)
This adds an example wallet creation code with sled DB and RPC
Blockchain.

The backend RPC is managed using electrsd::bitcoind

Cargo.toml
examples/rpcwallet.rs [new file with mode: 0644]

index 49fc8a225ec05c1af99eec720bd627c297bf2adb..9763e98a970d995f8d5d16f8b8ffe0cff339d502 100644 (file)
@@ -109,6 +109,11 @@ name = "miniscriptc"
 path = "examples/compiler.rs"
 required-features = ["compiler"]
 
+[[example]]
+name = "rpcwallet"
+path = "examples/rpcwallet.rs"
+required-features = ["keys-bip39", "key-value-db", "rpc"]
+
 [workspace]
 members = ["macros"]
 [package.metadata.docs.rs]
diff --git a/examples/rpcwallet.rs b/examples/rpcwallet.rs
new file mode 100644 (file)
index 0000000..0263ef7
--- /dev/null
@@ -0,0 +1,233 @@
+// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+use bdk::bitcoin::secp256k1::Secp256k1;
+use bdk::bitcoin::util::bip32::ExtendedPrivKey;
+use bdk::bitcoin::Amount;
+use bdk::bitcoin::Network;
+use bdk::bitcoincore_rpc::RpcApi;
+
+use bdk::blockchain::rpc::{Auth, RpcBlockchain, RpcConfig};
+use bdk::blockchain::ConfigurableBlockchain;
+
+use bdk::keys::bip39::{Language, Mnemonic, WordCount};
+use bdk::keys::{DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey};
+
+use bdk::miniscript::miniscript::Segwitv0;
+
+use bdk::sled;
+use bdk::template::Bip84;
+use bdk::wallet::{signer::SignOptions, wallet_name_from_descriptor, AddressIndex, SyncOptions};
+use bdk::KeychainKind;
+use bdk::Wallet;
+
+use bdk::blockchain::Blockchain;
+
+use electrsd;
+
+use std::error::Error;
+use std::path::PathBuf;
+use std::str::FromStr;
+
+/// This example demonstrates a typical way to create a wallet and work with bdk.
+///
+/// This example bdk wallet is connected to a bitcoin core rpc regtest node,
+/// and will attempt to receive, create and broadcast transactions.
+///
+/// To start a bitcoind regtest node programmatically, this example uses
+/// `electrsd` library, which is also a bdk dev-dependency.
+///
+/// But you can start your own bitcoind backend, and the rest of the example should work fine.
+
+fn main() -> Result<(), Box<dyn Error>> {
+    // -- Setting up background bitcoind process
+
+    println!(">> Setting up bitcoind");
+
+    // Start the bitcoind process
+    let bitcoind_conf = electrsd::bitcoind::Conf::default();
+
+    // electrsd will automatically download the bitcoin core binaries
+    let bitcoind_exe =
+        electrsd::bitcoind::downloaded_exe_path().expect("We should always have downloaded path");
+
+    // Launch bitcoind and gather authentication access
+    let bitcoind = electrsd::bitcoind::BitcoinD::with_conf(bitcoind_exe, &bitcoind_conf).unwrap();
+    let bitcoind_auth = Auth::Cookie {
+        file: bitcoind.params.cookie_file.clone(),
+    };
+
+    // Get a new core address
+    let core_address = bitcoind.client.get_new_address(None, None)?;
+
+    // Generate 101 blocks and use the above address as coinbase
+    bitcoind.client.generate_to_address(101, &core_address)?;
+
+    println!(">> bitcoind setup complete");
+    println!(
+        "Available coins in Core wallet : {}",
+        bitcoind.client.get_balance(None, None)?
+    );
+
+    // -- Setting up the Wallet
+
+    println!("\n>> Setting up BDK wallet");
+
+    // Get a random private key
+    let xprv = generate_random_ext_privkey()?;
+
+    // Use the derived descriptors from the privatekey to
+    // create unique wallet name.
+    // This is a special utility function exposed via `bdk::wallet_name_from_descriptor()`
+    let wallet_name = wallet_name_from_descriptor(
+        Bip84(xprv, KeychainKind::External),
+        Some(Bip84(xprv, KeychainKind::Internal)),
+        Network::Regtest,
+        &Secp256k1::new(),
+    )?;
+
+    // Create a database (using default sled type) to store wallet data
+    let mut datadir = PathBuf::from_str("/tmp/")?;
+    datadir.push(".bdk-example");
+    let database = sled::open(datadir)?;
+    let database = database.open_tree(wallet_name.clone())?;
+
+    // Create a RPC configuration of the running bitcoind backend we created in last step
+    // Note: If you are using custom regtest node, use the appropriate url and auth
+    let rpc_config = RpcConfig {
+        url: bitcoind.params.rpc_socket.to_string(),
+        auth: bitcoind_auth,
+        network: Network::Regtest,
+        wallet_name,
+        skip_blocks: None,
+    };
+
+    // Use the above configuration to create a RPC blockchain backend
+    let blockchain = RpcBlockchain::from_config(&rpc_config)?;
+
+    // Combine Database + Descriptor to create the final wallet
+    let wallet = Wallet::new(
+        Bip84(xprv, KeychainKind::External),
+        Some(Bip84(xprv, KeychainKind::Internal)),
+        Network::Regtest,
+        database,
+    )?;
+
+    // The `wallet` and the `blockchain` are independent structs.
+    // The wallet will be used to do all wallet level actions
+    // The blockchain can be used to do all blockchain level actions.
+    // For certain actions (like sync) the wallet will ask for a blockchain.
+
+    // Sync the wallet
+    // The first sync is important as this will instantiate the
+    // wallet files.
+    wallet.sync(&blockchain, SyncOptions::default())?;
+
+    println!(">> BDK wallet setup complete.");
+    println!(
+        "Available initial coins in BDK wallet : {} sats",
+        wallet.get_balance()?
+    );
+
+    // -- Wallet transaction demonstration
+
+    println!("\n>> Sending coins: Core --> BDK, 10 BTC");
+    // Get a new address to receive coins
+    let bdk_new_addr = wallet.get_address(AddressIndex::New)?.address;
+
+    // Send 10 BTC from core wallet to bdk wallet
+    bitcoind.client.send_to_address(
+        &bdk_new_addr,
+        Amount::from_btc(10.0)?,
+        None,
+        None,
+        None,
+        None,
+        None,
+        None,
+    )?;
+
+    // Confirm transaction by generating 1 block
+    bitcoind.client.generate_to_address(1, &core_address)?;
+
+    // Sync the BDK wallet
+    // This time the sync will fetch the new transaction and update it in
+    // wallet database
+    wallet.sync(&blockchain, SyncOptions::default())?;
+
+    println!(">> Received coins in BDK wallet");
+    println!(
+        "Available balance in BDK wallet: {} sats",
+        wallet.get_balance()?
+    );
+
+    println!("\n>> Sending coins: BDK --> Core, 5 BTC");
+    // Attempt to send back 5.0 BTC to core address by creating a transaction
+    //
+    // Transactions are created using a `TxBuilder`.
+    // This helps us to systematically build a transaction with all
+    // required customization.
+    // A full list of APIs offered by `TxBuilder` can be found at
+    // https://docs.rs/bdk/latest/bdk/wallet/tx_builder/struct.TxBuilder.html
+    let mut tx_builder = wallet.build_tx();
+
+    // For a regular transaction, just set the recipient and amount
+    tx_builder.set_recipients(vec![(core_address.script_pubkey(), 500000000)]);
+
+    // Finalize the transaction and extract the PSBT
+    let (mut psbt, _) = tx_builder.finish()?;
+
+    // Set signing option
+    let signopt = SignOptions {
+        assume_height: None,
+        ..Default::default()
+    };
+
+    // Sign the psbt
+    wallet.sign(&mut psbt, signopt)?;
+
+    // Extract the signed transaction
+    let tx = psbt.extract_tx();
+
+    // Broadcast the transaction
+    blockchain.broadcast(&tx)?;
+
+    // Confirm transaction by generating some blocks
+    bitcoind.client.generate_to_address(1, &core_address)?;
+
+    // Sync the BDK wallet
+    wallet.sync(&blockchain, SyncOptions::default())?;
+
+    println!(">> Coins sent to Core wallet");
+    println!(
+        "Remaining BDK wallet balance: {} sats",
+        wallet.get_balance()?
+    );
+    println!("\nCongrats!! you made your first test transaction with bdk and bitcoin core.");
+
+    Ok(())
+}
+
+// Helper function demonstrating privatekey extraction using bip39 mnemonic
+// The mnemonic can be shown to user to safekeeping and the same wallet
+// private descriptors can be recreated from it.
+fn generate_random_ext_privkey() -> Result<ExtendedPrivKey, Box<dyn Error>> {
+    // a Bip39 passphrase can be set optionally
+    let password = Some("random password".to_string());
+
+    // Generate a random mnemonic, and use that to create an Extended PrivateKey
+    let mnemonic: GeneratedKey<_, Segwitv0> =
+        Mnemonic::generate((WordCount::Words12, Language::English))
+            .map_err(|e| e.expect("Unknown Error"))?;
+    let mnemonic = mnemonic.into_key();
+    let xkey: ExtendedKey = (mnemonic, password).into_extended_key()?;
+    let xprv = xkey
+        .into_xprv(Network::Regtest)
+        .expect("Expected Private Key");
+    Ok(xprv)
+}