finished edit of first section

This commit is contained in:
Shannon Appelcline 2020-09-08 12:02:50 -10:00 committed by GitHub
parent 203d6b84fa
commit 188d07a600
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,139 +1,118 @@
# 18.5: Accessing Bitcoind with Rust
# 17.5: Accessing Bitcoind with Rust
## Setup
> :information_source: **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
We'll need `Rust` and `Cargo`. Installing them is easy:
This section explains how to interact with `bitcoind` using the Rust programming language and the [`bitcoincore-rpc` crate](https://github.com/rust-bitcoin/rust-bitcoincore-rpc).
## Setting Up Rust
You need to install both Rust and Cargo.
They can be installed via `curl`. Just use the "default" installation:
```vim
$ curl https://sh.rustup.rs -sSf | sh
```
If everything goes well, we should see:
If everything goes well, you should see:
```vim
Rust is installed now. Great!
```
To set `Bitcoin Regtest` network up and allow communication with our Rust program we
will be using the following `bitcoind` configuration in `bitcoin.conf`
```vim
regtest=1
server=1
rpcuser=bitcoin
rpcpassword=password
[test]
rpcport=18443
You'll then need to either logout and back in, or else add Cargo's binary directory to your path by hand:
```
$ source $HOME/.cargo/env
```
> Note: Never use a simple password like that when on Bitcoin Mainnet!
### Setting Up `bitcoincore-rpc`
### Create a New Project
For most programming languages, you need to install a Bitcoin RPC library before you create your first project, but here you'll do it as part of your project creation.
We create a new project with `cargo new btc_test`:
### Creating a `bitcoincore-rpc` Project
```vim
gorazd@gorazd-MS-7C37:~/Projects/BlockchainCommons$ cargo new btc_test
You can create a new project using `cargo new btc_test`:
```
$ cargo new btc_test
Created binary (application) `btc_test` package
```
Let's move into the newly created project `btc_test`. We notice a "hello world" example
with the source code in `src/main.rs` and a `Cargo.toml` file. Let's run it with `cargo run`:
This will create a `btc_test` directory that contains a "hello world" source-code example in `src/main.rs` and a `Cargo.toml` file.
```vim
gorazd@gorazd-MS-7C37:~/Projects/BlockchainCommons/btc_test$ cargo run
Compiling btc_test v0.1.0 (/home/gorazd/Projects/BlockchainCommons/btc_test)
You'll compile and run your code with `cargo run`:
```
$ cd btc_test
$ cargo run
Compiling btc_test v0.1.0 (/home/standup/btc_test)
Finished dev [unoptimized + debuginfo] target(s) in 0.14s
Running `target/debug/btc_test`
Hello, world!
```
> Note: if you run into error “linker cc not found”, you'll have to install a
> :information_source: **NOTE:** if you run into error `linker cc not found`, you'll have to install a
C compiler. If on Linux, go ahead and install the [development tools](https://www.ostechnix.com/install-development-tools-linux/).
We will use `bitcoincore-rpc` crate (library), therefore we add it to our `Cargo.toml`
under section `dependencies` like so:
In order to access the `bitcoincore-rpc` crate (library), you must add it to your `Cargo.toml`
file under the section `dependencies`:
```rust
[dependencies]
bitcoincore-rpc = "0.11.0"
```
Running our example again will install our crate and
its dependencies.
```vim
gorazd@gorazd-MS-7C37:~/Projects/BlockchainCommons/btc_test$ cargo run
When you `cargo run` again, it will install the crate and
its (numerous) dependencies.
```
$ cargo run
Updating crates.io index
...
Compiling bitcoin v0.23.0
Compiling bitcoincore-rpc-json v0.11.0
Compiling bitcoincore-rpc v0.11.0
Compiling btc_test v0.1.0 (/home/gorazd/Projects/BlockchainCommons/btc_test)
Compiling btc_test v0.1.0 (/home/standup/btc_test)
Finished dev [unoptimized + debuginfo] target(s) in 23.56s
Running `target/debug/btc_test`
Hello, world!
```
## Build Your Connection
When you are using `bitcoin-rpc`, you will typically need to include the following:
```
use bitcoincore_rpc::{Auth, Client, RpcApi};
```
Let us create a Bitcoin `RPC client` and modify the `main.rs`:
## Building Your Connection
To create a Bitcoin `RPC client`, modify the `src/main.rs`:
```rust
use bitcoincore_rpc::{Auth, Client};
use bitcoincore_rpc::{Auth, Client, RpcApi};
fn main() {
let rpc = Client::new(
"http://localhost:18443".to_string(),
Auth::UserPass("bitcoin".to_string(), "password".to_string()),
"http://localhost:18332".to_string(),
Auth::UserPass("StandUp".to_string(), "password".to_string()),
)
.unwrap();
}
```
`Cargo run` should successfully compile and run the example with one warning
`warning: unused variable: rpc`
As usual, make sure to insert your proper user name and password from `~/.bitcoin/bitcoin.conf`. Here, they're placed as the arguments for `Auth::UserPass`.
> :link: **TESTNET vs MAINNET:** And, as usual, use port 8332 for mainnet.
### Make an RPC Call
When you're done, you should also close your connection:
```rust
let _ = rpc.stop().unwrap();
```
This is a simple RPC call without arguments:
`cargo run` should successfully compile and run the example with one warning `warning: unused variable: rpc`
### Making an RPC Call
RPC calls made made using the `rpc` `Client` that you created:
```rust
let mining_info = rpc.get_mining_info().unwrap();
println!("{:#?}", mining_info);
```
The compiler will tell us to include traits into scope. So lets add them:
```rust
use bitcoincore_rpc::{Auth, Client, RpcApi};
```
If our properly configured `bitcoind` is running, executing our example should
result in:
```vim
gorazd@gorazd-MS-7C37:~/Projects/BlockchainCommons/btc_test$ cargo run
Compiling btc_test v0.1.0 (/home/gorazd/Projects/BlockchainCommons/btc_test)
Finished dev [unoptimized + debuginfo] target(s) in 0.80s
Running `target/debug/btc_test`
GetMiningInfoResult {
blocks: 5167,
current_block_weight: Some(
0,
),
current_block_tx: Some(
0,
),
difficulty: 0.00000000046565423739069247,
network_hash_ps: 1.764705882352941,
pooled_tx: 2,
chain: "regtest",
warnings: "",
}
```
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).
If we wanted we could close the connection:
@ -141,57 +120,46 @@ If we wanted we could close the connection:
let _ = rpc.stop().unwrap();
```
### Making an RPC Call with Arguments
## Manipulate Your Wallet
### Look Up Addresses
Here we will make our first call with an argument. To see the type of an argument,
we want to look at the function definition:
Sending an RPC call with arguments using Rust just requires knowing how the function is laid out. For example, the `get_block` function is defined as follows in the [docs](https://docs.rs/bitcoincore-rpc/0.11.0/bitcoincore_rpc/trait.RpcApi.html#method.get_block):
```rust
fn get_address_info(&self, address: &Address) -> Result<json::GetAddressInfoResult> {
self.call("getaddressinfo", &[address.to_string().into()])
fn get_block(&self, hash: &BlockHash) -> Result<Block>
```
We just need to allow it to borrow a blockhash, which can be retrieved (for example) by `get_best_block_hash` for the newest (and most reliable).
Here's the complete code to retrieve a block hash, turn that into a block, and print it.
```
let hash = rpc.get_best_block_hash().unwrap();
let block = rpc.get_block(&hash).unwrap();
println!("{:?}", block);
```
> :note: **NOTE:** Another possible call that we considered for this section was `get_address_info`, but unfortunately as of this writing, the `bitcoincore-rpc` function doesn't work with recent versions of Bitcoin Core due to the crate not addressing the latest API changes in Bitcoin Core. We expect this will be solved in the next crate's release, but in the meantime, _caveat programmer_.
### Running Your Code
You can access the [src code](src/17_5_main-getinfo.rs) and run it. Unfortunately, the "Block" info will come out a bit ugly because we don't have a library to prettify it.
```
$ cargo run
Compiling btc_test v0.1.0 (/home/standup/btc_test)
Finished dev [unoptimized + debuginfo] target(s) in 1.61s
Running `target/debug/btc_test`
GetMiningInfoResult {
blocks: 1832335,
current_block_weight: None,
current_block_tx: None,
difficulty: 4194304.0,
network_hash_ps: 77436285865245.1,
pooled_tx: 4,
chain: "test",
warnings: "Warning: unknown new rules activated (versionbit 28)",
}
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) }] }, ...] }
```
We see that our argument is of type `Address` and that it will be borrowed. Further,
looking at the structure `Address`, we notice a convenient `trait` implemented which
allows us to create an `Address` out of a string:
```rust
impl FromStr for Address {
type Err = Error;
fn from_str(s: &str) -> Result<Address, Error> {
```
Now that we now what structure and trait we are dealing with, we bring them into
scope
```rust
use bitcoincore_rpc::bitcoin::Address;
use std::str::FromStr;
```
so we can use them:
```rust
let addr = Address::from_str("bcrt1qanga5jxx845q82h9qgjfuedps92lktqv073qct").unwrap();
let addr_info = rpc.get_address_info(&addr).unwrap();
println!("{:?}", addr_info);
```
Running our program results in:
```vim
GetAddressInfoResult { address: bcrt1qanga5jxx845q82h9qgjfuedps92lktqv073qct, script_pub_key: Script(OP_0 OP_PUSHBYTES_20 ecd1da48c63d6803aae502249e65a18155fb2c0c), is_mine: Some(true), is_watchonly: Some(false), is_script: Some(false), is_witness: Some(true), witness_version: Some(0), witness_program: Some([236, 209, 218, 72, 198, 61, 104, 3, 170, 229, 2, 36, 158, 101, 161, 129, 85, 251, 44, 12]), script: None, hex: None, pubkeys: None, n_signatures_required: None, pubkey: Some(PublicKey { compressed: true, key: PublicKey(f895d610ab1ceddfd87814b1f7a911fee1135a9347d4fd1754a06ddf84757c5c527a90804949b025d7272bef4d58a1324c18d7a8f6b7ffa949447bcb6a225e6e) }), embedded: None, is_compressed: None, label: "lbl", timestamp: Some(1582063890), hd_key_path: Some(m/0'/0'/99'), hd_seed_id: Some(00b332a133c03c4e613f0106dc814bcc79af60ff), labels: [GetAddressInfoResultLabel { name: "lbl", purpose: Receive }] }
```
> Note: this call doesn't work with recent versions of Bitcoin Core due to the
crate not addressing the latest API changes in Bitcoin Core.
We expect it to be solved in the next crate's release.
### Look Up Funds
We can look up our funds without optional arguments like so: