diff --git a/17_5_Accessing_Bitcoind_with_Rust.md b/17_5_Accessing_Bitcoind_with_Rust.md index e163706..0db11ee 100644 --- a/17_5_Accessing_Bitcoind_with_Rust.md +++ b/17_5_Accessing_Bitcoind_with_Rust.md @@ -112,7 +112,7 @@ RPC calls made made using the `rpc` `Client` that you created: let mining_info = rpc.get_mining_info().unwrap(); println!("{:#?}", mining_info); ``` -Generally, the words in the RPC call are separated by `_`s. A complete list is available at the [crate docs](https://docs.rs/bitcoincore-rpc/0.11.0/bitcoincore_rpc/trait.RpcApi.html). +Generally, the words in the RPC call are separated by `_`s. A complete list is available at the [crate docs](https://crates.io/crates/bitcoincore-rpc). If we wanted we could close the connection: @@ -160,23 +160,22 @@ GetMiningInfoResult { Block { header: BlockHeader { version: 541065216, prev_blockhash: 000000000000027715981d5a3047daf6819ea3b8390b73832587594a2074cbf5, merkle_root: 4b2e2c2754b6ed9cf5c857a66ed4c8642b6f6b33b42a4859423e4c3dca462d0c, time: 1599602277, bits: 436469756, nonce: 218614401 }, txdata: [Transaction { version: 1, lock_time: 0, input: [TxIn { previous_output: OutPoint { txid: 0000000000000000000000000000000000000000000000000000000000000000, vout: 4294967295 }, script_sig: Script(OP_PUSHBYTES_3 8ff51b OP_PUSHBYTES_22 315448617368263538434f494e1d00010320a48db852 OP_PUSHBYTES_32 ), sequence: 4294967295, witness: [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] }], output: [TxOut { value: 19721777, script_pubkey: Script(OP_HASH160 OP_PUSHBYTES_20 011beb6fb8499e075a57027fb0a58384f2d3f784 OP_EQUAL) }, TxOut { value: 0, script_pubkey: Script(OP_RETURN OP_PUSHBYTES_36 aa21a9ed63363f3620ab5e38b8860a50c84050e5ec31af3636bbd73f01ba9f14103100ee) }] }, Transaction { version: 2, lock_time: 1832282, input: [TxIn { previous_output: OutPoint { txid: cbf880f73d421baf0aa4f0d28e63ba00e5bc6bd934b91eb0641354ce5ca42f7e, vout: 0 }, script_sig: Script(OP_PUSHBYTES_22 00146b8dbd32e5deb90d22934e1513bae6e70156cd50), sequence: 4294967294, witness: [[48, 68, 2, 32, 13, 89, 205, 30, 67, 24, 196, 83, 65, 224, 44, 138, 98, 58, 81, 135, 132, 209, 23, 166, 23, 44, 3, 228, 95, 102, 166, 214, 62, 38, 155, 147, 2, 32, 119, 2, 34, 246, 148, 255, 166, 10, 90, 52, 242, 32, 74, 241, 123, 148, 89, 199, 197, 3, 152, 134, 242, 215, 109, 61, 241, 241, 13, 70, 86, 207, 1], [2, 192, 145, 170, 206, 55, 4, 36, 138, 145, 217, 50, 19, 73, 130, 136, 245, 131, 184, 142, 239, 75, 13, 67, 17, 177, 57, 86, 151, 139, 89, 35, 109]] }], output: [TxOut { value: 1667908, script_pubkey: Script(OP_HASH160 OP_PUSHBYTES_20 908ca2b8b49ccf53efa2226afa85f6cc58dfd7e7 OP_EQUAL) }, TxOut { value: 9093, script_pubkey: Script(OP_DUP OP_HASH160 OP_PUSHBYTES_20 42ee67664ce16edefc68ad0e4c5b7ce2fc2ccc18 OP_EQUALVERIFY OP_CHECKSIG) }] }, ...] } ``` -### Look Up Funds +## Looking Up Funds -We can look up our funds without optional arguments like so: +You can look up funds without optional arguments using the `get_balance` function: ```rust let balance = rpc.get_balance(None, None).unwrap(); println!("Balance: {:?} BTC", balance.as_btc()); ``` - -```vim +As shown, the `as_btc()` function helps to output the balance in a readable form: +``` Balance: 3433.71692741 BTC ``` -### Create an Address +## Creating an Address -Here is an example of calling an RPC method with the optional arguments specified, i.e. -a label and an address type: +Creating an address demonstrates how to make an RPC call with multiple optional arguments specified (e.g., a label and an address type). ```rust // Generate a new address @@ -185,31 +184,24 @@ let myaddress = rpc .unwrap(); println!("address: {:?}", myaddress); ``` - -If we have inspected our function's definition we bring the missing things into -scope. Otherwise the compiler will hint us to do so: - +This will also require you to bring the `json` definition into scope: ```rust use bitcoincore_rpc::{json, Auth, Client, RpcApi}; ``` -Program execution results in: +## Sending a Transaction -```vim -address: bcrt1q0y0dk70lut5l3y4f0fe52am23egfmr63dejy9r -``` +You now have everything you need to create a transaction, which will be done in five parts: -Now, we would like to have some bitcoins to our newly generated address. Since -we are on the `Regtest` network we can generate them ourselves: +1. List UTXOs +2. Populate Variables +3. Create Raw Transaction +4. Sign Transaction +5. Send Transaction -```rust -// Generate 101 blocks to our address -let _ = rpc.generate_to_address(101, &myaddress); -``` +### 1. List UTXOs -## Create a Transaction - -First, we list unspent transactions. Let's look at those with at least 3 BTC and take the first one: +To start the creation of a transaction, you first find a UTXO to use. The following takes the first UTXO with at least 0.01 BTC ```rust let unspent = rpc @@ -219,7 +211,7 @@ let unspent = rpc None, None, Option::Some(json::ListUnspentQueryOptions { - minimum_amount: Option::Some(Amount::from_btc(3.0).unwrap()), + minimum_amount: Option::Some(Amount::from_btc(0.01).unwrap()), maximum_amount: None, maximum_count: None, minimum_sum_amount: None, @@ -231,89 +223,123 @@ let selected_tx = &unspent[0]; println!("selected unspent transaction: {:#?}", selected_tx); ``` -Here it is: - -```vim -selected unspent transaction: ListUnspentResultEntry { - txid: 34e283eb5b52c66aba9766bdda46eb038bc1138e992b593c22f7cbf1d2e9ba10, - vout: 0, - address: Some( - bcrt1q7lju6c0ynwerch0te4saxwxgm70ltd3lr9vj6l, - ), - label: Some( - "", - ), - redeem_script: None, - witness_script: None, - script_pub_key: Script(OP_0 OP_PUSHBYTES_20 f7e5cd61e49bb23c5debcd61d338c8df9ff5b63f), - amount: Amount(625000000 satoshi), - confirmations: 4691, - spendable: true, - solvable: true, - descriptor: None, - safe: true, -} -``` - -This will require to bring another structure into scope: +This will require bringing more structures into scope: ```rust use bitcoincore_rpc::bitcoin::{Address, Amount}; ``` -We can now populate some variables: the available amount and -the utxo, the recipient's address and the amount we want to send. +Note that you're passing `list_unspent` five variables. The first four (`minconf`, `maxconf`, `addresses`, and `include_unsafe`) aren't used here. The fifth is `query_options`, which we haven't used before, but has some powerful filtering options, including the ability to only look at UTXOs with a certain minimum (or maximum) value. + +### 2. Populate Variables + +First, you can create the input from the `txid` and the `vout` of the UTXO you selected: ```rust -let unspent_amount = selected_tx.amount; - let selected_utxos = json::CreateRawTransactionInput { -txid: selected_tx.txid, -vout: selected_tx.vout, -sequence: None, + txid: selected_tx.txid, + vout: selected_tx.vout, + sequence: None, }; - -let recipient = Address::from_str("bcrt1q6rhpng9evdsfnn833a4f4vej0asu6dk5srld6x").unwrap(); -println!("recipient: {:?}", recipient); - +``` +Second, you can calculate the amount you're going to spend by subtracting a mining fee from the funds in the UTXO: +``` // send all bitcoin in the UTXO except a minor value which will be paid to miners +let unspent_amount = selected_tx.amount; let amount = unspent_amount - Amount::from_btc(0.00001).unwrap(); - +``` +Third, you can create a hash map of the address and the amount: +``` let mut output = HashMap::new(); output.insert( -"bcrt1q6rhpng9evdsfnn833a4f4vej0asu6dk5srld6x".to_string(), -amount, + myaddress.to_string(), + amount, ); ``` - -Another trait is necessary for the output variable: HashMap. It allows us to store -values by key which we need to represent `{address : amount}` information. +Another trait is necessary for the output variable: `HashMap`. It allows you to store +values by key, which you need to represent `{address : amount}` information. ```rust use std::collections::HashMap; ``` -We are ready to create a raw transaction: +### 3. Create Raw Transaction +You are ready to create a raw transaction: ```rust let unsigned_tx = rpc -.create_raw_transaction(&[selected_utxos], &output, None, None) -.unwrap(); + .create_raw_transaction(&[selected_utxos], &output, None, None) + .unwrap(); +``` +### 4. Sign Transaction -println!("unsigned tx {:#?}", unsigned_tx); +Signing your transaction can be done with a simple use of `sign_raw_transaction_with_wallet`: +```rust +let signed_tx = rpc + .sign_raw_transaction_with_wallet(&unsigned_tx, None, None) + .unwrap(); + +println!("signed tx {:?}", signed_tx.transaction().unwrap()); ``` -Here it is: +### 5. Send Transaction -```vim +Finally, you can sign and broadcast the transaction: + +```rust +let txid_sent = rpc + .send_raw_transaction(&signed_tx.transaction().unwrap()) + .unwrap(); + +println!("{:?}", txid_sent); +``` + +You can now run the complete code from the [src](src/17_5_main-sendtx.rs). + +``` +$ cargo run + Compiling btc_test v0.1.0 (/home/standup/btc_test) +warning: unused variable: `unspent_amount` + --> src/main.rs:86:9 + | +86 | let unspent_amount = selected_tx.amount; + | ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unspent_amount` + | + = note: `#[warn(unused_variables)]` on by default + +warning: 1 warning emitted + + Finished dev [unoptimized + debuginfo] target(s) in 2.11s + Running `target/debug/btc_test` +Balance: 0.01031434 BTC +address: tb1qx5jz36xgt9q2rkh4daee8ewfj0g5z05v8qsua2 +selected unspent transaction: ListUnspentResultEntry { + txid: 84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e, + vout: 1, + address: Some( + tb1qrcf8c29966tvqxhwrtd2se3rj6jeqtll3r46a4, + ), + label: None, + redeem_script: None, + witness_script: None, + script_pub_key: Script(OP_0 OP_PUSHBYTES_20 1e127c28a5d696c01aee1adaa8662396a5902fff), + amount: Amount(1029734 satoshi), + confirmations: 1246, + spendable: true, + solvable: true, + descriptor: Some( + "wpkh([ce0c7e14/0\'/1\'/26\']02c581259ba7e6aef6d7ea23adb08f7c7f10c4c678f2e097a4074639e7685d4805)#j3pctfhf", + ), + safe: true, +} unsigned tx Transaction { version: 2, lock_time: 0, input: [ TxIn { previous_output: OutPoint { - txid: 34e283eb5b52c66aba9766bdda46eb038bc1138e992b593c22f7cbf1d2e9ba10, - vout: 0, + txid: 84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e, + vout: 1, }, script_sig: Script(), sequence: 4294967295, @@ -322,42 +348,20 @@ unsigned tx Transaction { ], output: [ TxOut { - value: 624999000, - script_pubkey: Script(OP_0 OP_PUSHBYTES_20 d0ee19a0b9636099ccf18f6a9ab3327f61cd36d4), + value: 1028734, + script_pubkey: Script(OP_0 OP_PUSHBYTES_20 352428e8c85940a1daf56f7393e5c993d1413e8c), }, ], } +signed tx Transaction { version: 2, lock_time: 0, input: [TxIn { previous_output: OutPoint { txid: 84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e, vout: 1 }, script_sig: Script(), sequence: 4294967295, witness: [[48, 68, 2, 32, 98, 230, 199, 113, 156, 242, 158, 42, 148, 229, 239, 44, 9, 226, 127, 219, 72, 51, 26, 135, 44, 212, 179, 200, 213, 63, 56, 167, 0, 55, 236, 235, 2, 32, 41, 43, 30, 109, 60, 162, 124, 67, 20, 126, 4, 107, 124, 95, 9, 200, 132, 246, 147, 235, 176, 55, 59, 45, 190, 18, 211, 201, 143, 62, 163, 36, 1], [2, 197, 129, 37, 155, 167, 230, 174, 246, 215, 234, 35, 173, 176, 143, 124, 127, 16, 196, 198, 120, 242, 224, 151, 164, 7, 70, 57, 231, 104, 93, 72, 5]] }], output: [TxOut { value: 1028734, script_pubkey: Script(OP_0 OP_PUSHBYTES_20 352428e8c85940a1daf56f7393e5c993d1413e8c) }] } +b0eda3517e6fac69e58ae315d7fe7a1981e3a858996cc1e3135618cac9b79d1a ``` -Finally, we can sign and broadcast our transaction: - -```rust -// sign transaction -let signed_tx = rpc -.sign_raw_transaction_with_wallet(&unsigned_tx, None, None) -.unwrap(); - -println!("singed tx {:?}", signed_tx.transaction().unwrap()); - -// broadcast transaction -let txid_sent = rpc -.send_raw_transaction(&signed_tx.transaction().unwrap()) -.unwrap(); - -println!("{:?}", txid_sent); -``` - -```vim -singed tx Transaction { version: 2, lock_time: 0, input: [TxIn { previous_output: OutPoint { txid: 34e283eb5b52c66aba9766bdda46eb038bc1138e992b593c22f7cbf1d2e9ba10, vout: 0 }, script_sig: Script(), sequence: 4294967295, witness: [[48, 68, 2, 32, 85, 113, 140, 197, 142, 140, 122, 26, 174, 71, 94, 152, 76, 104, 5, 111, 113, 192, 179, 1, 58, 6, 27, 141, 18, 50, 217, 53, 154, 26, 5, 98, 2, 32, 53, 148, 139, 57, 234, 151, 71, 149, 134, 202, 160, 136, 15, 144, 103, 232, 134, 37, 136, 184, 117, 159, 235, 92, 59, 102, 197, 213, 67, 64, 89, 207, 1], [3, 4, 197, 157, 36, 136, 177, 169, 182, 219, 121, 187, 251, 153, 207, 165, 173, 117, 142, 93, 181, 107, 185, 97, 10, 168, 210, 148, 67, 127, 246, 229, 12]] }], output: [TxOut { value: 624999000, script_pubkey: Script(OP_0 OP_PUSHBYTES_20 d0ee19a0b9636099ccf18f6a9ab3327f61cd36d4) }] } -5d2f1b7c6fc29967d820532c46200b35f62b6e6f8da614ae86922c20167f6d0e -``` - -## For More Information - -You can now mine a block and try to see for yourself if the last transaction is really in the block. -If you need help look at the crate's [documentation](https://crates.io/crates/bitcoincore-rpc) or run some tests in its [repository](https://github.com/rust-bitcoin/rust-bitcoincore-rpc). - ## Summary -We have shown how to access `bitcoind` in `Rust` and send a transaction -on the `Bitcoin Regtest Network` explaining all the steps required. +`bitcoincore-rpc` is a simple and robust crate that will allow you to interact with Bitcoin RPC using Rust. However, as of this writing it has fallen behind Bitcoin Core, which might cause some issues with usage. + +## What's Next? + +Learn more about "Talking to Bitcoin in Other Languages" in [17.6: Accessing Bitcoin with Swift](17_6_Accessing_Bitcoind_with_Swift.md). +