Update 17_5_Accessing_Bitcoind_with_Rust.md

This commit is contained in:
Shannon Appelcline 2020-09-08 13:09:42 -10:00 committed by GitHub
parent e79833a14f
commit be7ff16f6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -112,7 +112,7 @@ RPC calls made made using the `rpc` `Client` that you created:
let mining_info = rpc.get_mining_info().unwrap(); let mining_info = rpc.get_mining_info().unwrap();
println!("{:#?}", mining_info); 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: 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 <push past end>), 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) }] }, ...] } 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 <push past end>), 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 ```rust
let balance = rpc.get_balance(None, None).unwrap(); let balance = rpc.get_balance(None, None).unwrap();
println!("Balance: {:?} BTC", balance.as_btc()); println!("Balance: {:?} BTC", balance.as_btc());
``` ```
As shown, the `as_btc()` function helps to output the balance in a readable form:
```vim ```
Balance: 3433.71692741 BTC 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. Creating an address demonstrates how to make an RPC call with multiple optional arguments specified (e.g., a label and an address type).
a label and an address type:
```rust ```rust
// Generate a new address // Generate a new address
@ -185,31 +184,24 @@ let myaddress = rpc
.unwrap(); .unwrap();
println!("address: {:?}", myaddress); println!("address: {:?}", myaddress);
``` ```
This will also require you to bring the `json` definition into scope:
If we have inspected our function's definition we bring the missing things into
scope. Otherwise the compiler will hint us to do so:
```rust ```rust
use bitcoincore_rpc::{json, Auth, Client, RpcApi}; use bitcoincore_rpc::{json, Auth, Client, RpcApi};
``` ```
Program execution results in: ## Sending a Transaction
```vim You now have everything you need to create a transaction, which will be done in five parts:
address: bcrt1q0y0dk70lut5l3y4f0fe52am23egfmr63dejy9r
```
Now, we would like to have some bitcoins to our newly generated address. Since 1. List UTXOs
we are on the `Regtest` network we can generate them ourselves: 2. Populate Variables
3. Create Raw Transaction
4. Sign Transaction
5. Send Transaction
```rust ### 1. List UTXOs
// Generate 101 blocks to our address
let _ = rpc.generate_to_address(101, &myaddress);
```
## Create a Transaction 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
First, we list unspent transactions. Let's look at those with at least 3 BTC and take the first one:
```rust ```rust
let unspent = rpc let unspent = rpc
@ -219,7 +211,7 @@ let unspent = rpc
None, None,
None, None,
Option::Some(json::ListUnspentQueryOptions { 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_amount: None,
maximum_count: None, maximum_count: None,
minimum_sum_amount: None, minimum_sum_amount: None,
@ -231,89 +223,123 @@ let selected_tx = &unspent[0];
println!("selected unspent transaction: {:#?}", selected_tx); println!("selected unspent transaction: {:#?}", selected_tx);
``` ```
Here it is: This will require bringing more structures into scope:
```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:
```rust ```rust
use bitcoincore_rpc::bitcoin::{Address, Amount}; use bitcoincore_rpc::bitcoin::{Address, Amount};
``` ```
We can now populate some variables: the available amount and 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.
the utxo, the recipient's address and the amount we want to send.
### 2. Populate Variables
First, you can create the input from the `txid` and the `vout` of the UTXO you selected:
```rust ```rust
let unspent_amount = selected_tx.amount;
let selected_utxos = json::CreateRawTransactionInput { let selected_utxos = json::CreateRawTransactionInput {
txid: selected_tx.txid, txid: selected_tx.txid,
vout: selected_tx.vout, vout: selected_tx.vout,
sequence: None, sequence: None,
}; };
```
let recipient = Address::from_str("bcrt1q6rhpng9evdsfnn833a4f4vej0asu6dk5srld6x").unwrap(); Second, you can calculate the amount you're going to spend by subtracting a mining fee from the funds in the UTXO:
println!("recipient: {:?}", recipient); ```
// send all bitcoin in the UTXO except a minor value which will be paid to miners // 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(); 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(); let mut output = HashMap::new();
output.insert( output.insert(
"bcrt1q6rhpng9evdsfnn833a4f4vej0asu6dk5srld6x".to_string(), myaddress.to_string(),
amount, amount,
); );
``` ```
Another trait is necessary for the output variable: `HashMap`. It allows you to store
Another trait is necessary for the output variable: HashMap. It allows us to store values by key, which you need to represent `{address : amount}` information.
values by key which we need to represent `{address : amount}` information.
```rust ```rust
use std::collections::HashMap; 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 ```rust
let unsigned_tx = rpc let unsigned_tx = rpc
.create_raw_transaction(&[selected_utxos], &output, None, None) .create_raw_transaction(&[selected_utxos], &output, None, None)
.unwrap(); .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 { unsigned tx Transaction {
version: 2, version: 2,
lock_time: 0, lock_time: 0,
input: [ input: [
TxIn { TxIn {
previous_output: OutPoint { previous_output: OutPoint {
txid: 34e283eb5b52c66aba9766bdda46eb038bc1138e992b593c22f7cbf1d2e9ba10, txid: 84207ffec658ae29ad1fdd330d8a13613303c3cf281ce628fadeb7636ffb535e,
vout: 0, vout: 1,
}, },
script_sig: Script(), script_sig: Script(),
sequence: 4294967295, sequence: 4294967295,
@ -322,42 +348,20 @@ unsigned tx Transaction {
], ],
output: [ output: [
TxOut { TxOut {
value: 624999000, value: 1028734,
script_pubkey: Script(OP_0 OP_PUSHBYTES_20 d0ee19a0b9636099ccf18f6a9ab3327f61cd36d4), 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 ## Summary
We have shown how to access `bitcoind` in `Rust` and send a transaction `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.
on the `Bitcoin Regtest Network` explaining all the steps required.
## 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).