mirror of
https://github.com/ChristopherA/Learning-Bitcoin-from-the-Command-Line.git
synced 2025-06-08 00:16:26 +00:00
re-edited and brought back up to date
This commit is contained in:
parent
ac52f255a2
commit
7132022f57
@ -2,11 +2,11 @@
|
||||
|
||||
> **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
|
||||
|
||||
Interacting with the bitcoind directly and using command-line curl can get simple if you understand how it works, thus in this section we'll show how to use a good package for doing so in C called libbitcoinrpc that provides the functionality to access JSON-RPC bitcoind API. It uses a curl library for accessing the data and it uses the jansson library for encoding and decoding the JSON.
|
||||
You've already seen one alternative way to access the Bitcoind's RPC ports: `curl`, which was covered in a [Chapter 4 Interlude](04_4__Interlude_Using_Curl.md). Interacting with `bitcoind` through a C API is no different than that, you just need some good software. This section uses a package called `libbitcoinrpc` that that allows you to access JSON-RPC `bitcoind` port. It uses a curl library for accessing the data and it uses the jansson library for encoding and decoding the JSON.
|
||||
|
||||
## Set Up libbitcoinrpc
|
||||
## Setting Up libbitcoinrpc
|
||||
|
||||
To use `libbitcoinrpc`, you need to install a basic C setup and the dependent packages `libcurl`, `libjansson`, and `libuuid`. The following will do so on a Ubuntu system:
|
||||
To use `libbitcoinrpc`, you need to install a basic C setup and the dependent packages `libcurl`, `libjansson`, and `libuuid`. The following will do so on your Bitcoin Standup server (or any other Ubuntu server).
|
||||
```
|
||||
$ sudo apt-get install make gcc libcurl4-openssl-dev libjansson-dev uuid-dev
|
||||
Suggested packages:
|
||||
@ -20,23 +20,11 @@ Do you want to continue? [Y/n] y
|
||||
```
|
||||
You can then download [libbitcoinrpc from Github](https://github.com/gitmarek/libbitcoinrpc/blob/master/README.md). Clone it or grab a zip file, as you prefer.
|
||||
```
|
||||
$ sudo apt-get unzip
|
||||
$ unzip libbitcoinrpc-master.zip
|
||||
unzip libbitcoinrpc-master.zip
|
||||
Archive: libbitcoinrpc-master.zip
|
||||
a2285e8f221185cd0afdcfaf1bc8c78988fce09a
|
||||
creating: libbitcoinrpc-master/
|
||||
inflating: libbitcoinrpc-master/.gitignore
|
||||
inflating: libbitcoinrpc-master/.travis.yml
|
||||
inflating: libbitcoinrpc-master/CREDITS
|
||||
inflating: libbitcoinrpc-master/Changelog.md
|
||||
inflating: libbitcoinrpc-master/LICENSE
|
||||
inflating: libbitcoinrpc-master/Makefile
|
||||
inflating: libbitcoinrpc-master/README.md
|
||||
$ cd libbitcoinrpc-master/
|
||||
$ sudo apt-get install git
|
||||
$ git clone https://github.com/gitmarek/libbitcoinrpc
|
||||
```
|
||||
|
||||
### Compile libbitcoinrpc
|
||||
### Compiling libbitcoinrpc
|
||||
|
||||
Before you can compile and install the package, you'll probably need to adjust your `$PATH`, so that you can access `/sbin/ldconfig`:
|
||||
```
|
||||
@ -44,18 +32,22 @@ $ PATH="/sbin:$PATH"
|
||||
```
|
||||
For an Ubuntu system, you'll also want to adjust the `INSTALL_LIBPATH` in the `libbitcoinrpc` `Makefile` to install to `/usr/lib` instead of `/usr/local/lib`:
|
||||
```
|
||||
$ emacs ~/libbitcoinrpc/Makefile
|
||||
...
|
||||
INSTALL_LIBPATH := $(INSTALL_PREFIX)/usr/lib
|
||||
```
|
||||
(If you prefer not to sully your `/usr/lib`, the alternative is to change your `etc/ld.so.conf` or its dependent files appropriately ... but for a test setup on a test machine, this is probably fine.)
|
||||
|
||||
Likewise, you'll also want to adjust the `INSTALL_HEADERPATH` in the `libbitcoinrpc` `Makefile` to install to `/usr/include` instead of `/usr/local/include`:
|
||||
```
|
||||
...
|
||||
INSTALL_HEADERPATH := $(INSTALL_PREFIX)/usr/include
|
||||
```
|
||||
|
||||
Then you can compile:
|
||||
```
|
||||
$ ~/libbitcoinrpc-master$ make
|
||||
$ cd libbitcoinrpc
|
||||
~/libbitcoinrpc$ make
|
||||
|
||||
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_err.o -c src/bitcoinrpc_err.c
|
||||
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_global.o -c src/bitcoinrpc_global.c
|
||||
@ -71,22 +63,6 @@ ldconfig -v -n .lib
|
||||
.lib:
|
||||
libbitcoinrpc.so.0 -> libbitcoinrpc.so.0.2 (changed)
|
||||
ln -fs libbitcoinrpc.so.0 .lib/libbitcoinrpc.so
|
||||
~/libbitcoinrpc-master$ sudo make install
|
||||
Installing to
|
||||
install .lib/libbitcoinrpc.so.0.2 /usr/local/lib
|
||||
ldconfig -n /usr/local/lib
|
||||
ln -fs libbitcoinrpc.so.0 /usr/local/lib/libbitcoinrpc.so
|
||||
install -m 644 src/bitcoinrpc.h /usr/local/include
|
||||
Installing docs to /usr/share/doc/bitcoinrpc
|
||||
mkdir -p /usr/share/doc/bitcoinrpc
|
||||
install -m 644 doc/*.md /usr/share/doc/bitcoinrpc
|
||||
install -m 644 CREDITS /usr/share/doc/bitcoinrpc
|
||||
install -m 644 LICENSE /usr/share/doc/bitcoinrpc
|
||||
install -m 644 Changelog.md /usr/share/doc/bitcoinrpc
|
||||
Installing man pages
|
||||
install -m 644 doc/man3/bitcoinrpc*.gz /usr/local/man/man3
|
||||
~/libbitcoinrpc-master$
|
||||
|
||||
```
|
||||
If that works, you can install the package:
|
||||
```
|
||||
@ -106,12 +82,10 @@ Installing man pages
|
||||
install -m 644 doc/man3/bitcoinrpc*.gz /usr/local/man/man3
|
||||
```
|
||||
|
||||
## Write Code in C
|
||||
## Preparing Your Code
|
||||
|
||||
`libbitcoinrpc` has well-structured and simple methods for connecting to your `bitcoind`, executing RPC calls, and decoding the response.
|
||||
|
||||
### Setup Your Code
|
||||
|
||||
To use `libbitcoinrpc`, make sure that your code files include the appropriate headers:
|
||||
```
|
||||
#include <jansson.h>
|
||||
@ -119,10 +93,10 @@ To use `libbitcoinrpc`, make sure that your code files include the appropriate h
|
||||
```
|
||||
You'll also need to link in the appropriate libraries whenever you compile:
|
||||
```
|
||||
$ cc mybitcoinclient.c -lbitcoinrpc -ljansson -o mybitcoinclient
|
||||
$ cc yourcode.c -lbitcoinrpc -ljansson -o yourcode
|
||||
```
|
||||
|
||||
### Build Your Connection
|
||||
## Building Your Connection
|
||||
|
||||
Building the connection to your `bitcoind` server takes a few simple steps.
|
||||
|
||||
@ -130,10 +104,34 @@ First, initialize the library:
|
||||
```
|
||||
bitcoinrpc_global_init();
|
||||
```
|
||||
Then connect to your `bitcoind` with `bitcoinrpc_cl_init_params`. The four arguments for `bitcoinrpc_cl_init_params` are username, password, IP address, and port. You should already know all of this information from your work with `curl`. As you'll recall, the IP address 127.0.0.1 and port 18332 should be correct for the standard testnet setup described in this documents, while you can extract the user and password from `~/.bitcoin/bitcoin.conf`.
|
||||
Then connect to your `bitcoind` with `bitcoinrpc_cl_init_params`. The four arguments for `bitcoinrpc_cl_init_params` are username, password, IP address, and port. You should already know all of this information from your work with [Curl](04_4__Interlude_Using_Curl.md). As you'll recall, the IP address 127.0.0.1 and port 18332 should be correct for the standard testnet setup described in this documents, while you can extract the user and password from `~/.bitcoin/bitcoin.conf`.
|
||||
```
|
||||
$ cat bitcoin.conf
|
||||
server=1
|
||||
dbcache=1536
|
||||
par=1
|
||||
maxuploadtarget=137
|
||||
maxconnections=16
|
||||
rpcuser=StandUp
|
||||
rpcpassword=6305f1b2dbb3bc5a16cd0f4aac7e1eba
|
||||
rpcallowip=127.0.0.1
|
||||
debug=tor
|
||||
prune=550
|
||||
testnet=1
|
||||
[test]
|
||||
rpcbind=127.0.0.1
|
||||
rpcport=18332
|
||||
[main]
|
||||
rpcbind=127.0.0.1
|
||||
rpcport=8332
|
||||
[regtest]
|
||||
rpcbind=127.0.0.1
|
||||
rpcport=18443
|
||||
```
|
||||
Which you then place in the `bitcoinrpc_cl_init_params`:
|
||||
```
|
||||
bitcoinrpc_cl_t *rpc_client;
|
||||
rpc_client = bitcoinrpc_cl_init_params("bitcoinrpc", "d8340efbcd34e312044c8431c59c792c", "127.0.0.1", 18332);
|
||||
rpc_client = bitcoinrpc_cl_init_params("StandUp", "6305f1b2dbb3bc5a16cd0f4aac7e1eba", "127.0.0.1", 18332);
|
||||
```
|
||||
|
||||
> **MAINNET VS TESTNET:** The port would be 8332 for a mainnet setup.
|
||||
@ -145,9 +143,18 @@ Later, when you're all done with your `bitcoind` connection, you should close it
|
||||
bitcoinrpc_global_cleanup();
|
||||
```
|
||||
|
||||
Appendix I shows the complete code for a test of a `bitcoind` connection.
|
||||
### Testing the Test Code
|
||||
|
||||
### Make an RPC Call
|
||||
Test code can be found at [15_1_testbitcoin.c in the src directory](src/15_1_testbitcoin.c). Download it to your testnet machine, then insert the correct RPC password (and change the RPC user if you didn't create your server with StandUp).
|
||||
|
||||
You can compile and run this as follows:
|
||||
```
|
||||
$ cc testbitcoin.c -lbitcoinrpc -ljansson -o testbitcoin
|
||||
$ ./testbitcoin
|
||||
Successfully connected to server!
|
||||
```
|
||||
|
||||
## Making an RPC Call
|
||||
|
||||
In order to use an RPC method using `libbitcoinrpc`, you must initialize a variable of type `bitcoinrpc_method_t`. You do so with the appropriate value for the method you want to use, all of which are listed in the [bitcoinrpc Reference](https://github.com/gitmarek/libbitcoinrpc/blob/master/doc/reference.md).
|
||||
```
|
||||
@ -163,11 +170,11 @@ btcresponse = bitcoinrpc_resp_init();
|
||||
|
||||
bitcoinrpc_err_t btcerror;
|
||||
```
|
||||
With your four standard variables in hand, you can make a `getmininginfo` RPC call using `bitcoinrpc_call`:
|
||||
You use the `rpc_client` variable that you already learned about in the previous test, and add on your `getmininginfo` method and the two other objects:
|
||||
```
|
||||
bitcoinrpc_call(rpc_client, getmininginfo, btcresponse, &btcerror);
|
||||
```
|
||||
### Output Your Response
|
||||
### Outputting Your Response
|
||||
|
||||
You'll want to know what the RPC call returned. To do so, retrieve the output of your call as a JSON object with `bitcoinrpc_resp_get` and save it into a standard `jansson` object, of type `json_t`:
|
||||
```
|
||||
@ -178,9 +185,9 @@ If you want to output the complete JSON results of the RPC call, you can do so w
|
||||
```
|
||||
printf ("%s\n", json_dumps(j, JSON_INDENT(2)));
|
||||
```
|
||||
However, since you're now writing complete programs, you probablywant to do more subtle work, such as pulling out individual JSON values for specific usage. The [jansson Reference](https://jansson.readthedocs.io/en/2.10/apiref.html) details how to do so.
|
||||
However, since you're now writing complete programs, you probably want to do more subtle work, such as pulling out individual JSON values for specific usage. The [jansson Reference](https://jansson.readthedocs.io/en/2.10/apiref.html) details how to do so.
|
||||
|
||||
Just as when you were using `curl`, you'll find that RPC returns a JSON object containing an `id`, an `error`, and most importantly a JSON object of the `result`.
|
||||
Just as when you were using [Curl](04_4__Interlude_Using_Curl.md), you'll find that RPC returns a JSON object containing an `id`, an `error`, and most importantly a JSON object of the `result`.
|
||||
|
||||
The `json_object_get` function will let you retrieve a value (such as the `result`) from a JSON object by key:
|
||||
```
|
||||
@ -198,15 +205,45 @@ blocks = json_integer_value(jsonblocks);
|
||||
printf("Block Count: %d\n",blocks);
|
||||
```
|
||||
|
||||
> **WARNING:** It's extremely easy to segfault your C code when working with `jansson` objects if you get confused with what type of object you're retrieving. Make careful use of `bitcoin-cli help` to know what you should expect, and if you experience a segmentation fault, first look at your JSON retrieval functions.
|
||||
> :warning: **WARNING:** It's extremely easy to segfault your C code when working with `jansson` objects if you get confused with what type of object you're retrieving. Make careful use of `bitcoin-cli help` to know what you should expect, and if you experience a segmentation fault, first look at your JSON retrieval functions.
|
||||
|
||||
Appendix II has an example of this complete code for accessing mining information.
|
||||
|
||||
### Make an RPC Call with Arguments
|
||||
### Testing the Info Code
|
||||
|
||||
Retrieve the test code from [the src directory](15_1_getmininginfo.c).
|
||||
```
|
||||
$ cc getmininginfo.c -lbitcoinrpc -ljansson -o getmininginfo
|
||||
$ ./getmininginfo
|
||||
Full Response: {
|
||||
"result": {
|
||||
"blocks": 1804406,
|
||||
"difficulty": 4194304,
|
||||
"networkhashps": 54842097951591.781,
|
||||
"pooledtx": 127,
|
||||
"chain": "test",
|
||||
"warnings": "Warning: unknown new rules activated (versionbit 28)"
|
||||
},
|
||||
"error": null,
|
||||
"id": "474ccddd-ef8c-4e3f-93f7-fde72fc08154"
|
||||
}
|
||||
|
||||
Just the Result: {
|
||||
"blocks": 1804406,
|
||||
"difficulty": 4194304,
|
||||
"networkhashps": 54842097951591.781,
|
||||
"pooledtx": 127,
|
||||
"chain": "test",
|
||||
"warnings": "Warning: unknown new rules activated (versionbit 28)"
|
||||
}
|
||||
|
||||
Block Count: 1804406
|
||||
```
|
||||
## Making an RPC Call with Arguments
|
||||
|
||||
But what if your RPC call _did_ have arguments?
|
||||
|
||||
#### Create a JSON Array
|
||||
### Creating a JSON Array
|
||||
|
||||
To send parameters to your RPC call using `libbitcoinrpc` you have to wrap them in a JSON array. Since an array is just a simple listing of values, all you have to do is encode the parameters as ordered elements in the array.
|
||||
|
||||
@ -221,9 +258,9 @@ json_array_append_new(params,json_string(tx_rawhex));
|
||||
```
|
||||
Note that there are two variants to the append command: `json_array_append_new`, which appends a newly created variable, and `json_array_append`, which appends an existing variable.
|
||||
|
||||
This simple methodology will serve for the majority of RPC commands with parameters, but some RPC commands require more complex inputs. In these cases you may need to create subsidiary JSON objects or JSON arrays, which you will then append to the parameters array as usual. The next section contains an example of doing so using `createrawtransaction`, which contains a JSON array of JSON objects for the inputs, a JSON object for the outputs, and the `locktime` parameter.
|
||||
This simple `json_array_append_new` methodology will serve for the majority of RPC commands with parameters, but some RPC commands require more complex inputs. In these cases you may need to create subsidiary JSON objects or JSON arrays, which you will then append to the parameters array as usual. The next section contains an example of doing so using `createrawtransaction`, which contains a JSON array of JSON objects for the inputs, a JSON object for the outputs, and the `locktime` parameter.
|
||||
|
||||
#### Assign the Parameters
|
||||
### Assigning the Parameters
|
||||
|
||||
When you've created your parameters JSON array, you simply assign it after you've initialized your RPC method, as follows:
|
||||
```
|
||||
@ -235,149 +272,4 @@ This section doesn't include a full example of this more complex methodology, bu
|
||||
|
||||
By linking to the `bitcoinrpc` and `jansson` libraries, you can easily access `bitcoind` via RPC calls from a C library. To do so, you create an RPC connection, then make individual RPC calls, some of them with parameters. `jansson` then allows you to decode the JSON responses.
|
||||
|
||||
_What is the power of C?_ C allows you to take the next step beyond shell-scripting, permitting the creation of more comprehensive and robust programs. A few examples appear in the next two sections.
|
||||
|
||||
## Appendix I: Testing a Bitcoind Connection
|
||||
|
||||
Here's the complete code for a test of the connection to `bitcoind`.
|
||||
```
|
||||
file: testbitcoin.c
|
||||
|
||||
#include <jansson.h>
|
||||
#include <bitcoinrpc.h>
|
||||
|
||||
int main(void) {
|
||||
|
||||
bitcoinrpc_global_init();
|
||||
|
||||
bitcoinrpc_cl_t *rpc_client;
|
||||
rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "d8340efbcd34e312044c8431c59c792c", "127.0.0.1", 18332);
|
||||
|
||||
if (rpc_client) {
|
||||
|
||||
printf("Successfully connected to server!\n");
|
||||
|
||||
} else {
|
||||
|
||||
printf("Failed to connect to server!\n");
|
||||
|
||||
}
|
||||
|
||||
bitcoinrpc_global_cleanup();
|
||||
|
||||
}
|
||||
```
|
||||
You can compile and run this as follows:
|
||||
```
|
||||
$ cc testbitcoin.c -lbitcoinrpc -ljansson -o testbitcoin
|
||||
$ ./testbitcoin
|
||||
Successfully connected to server!
|
||||
```
|
||||
## Appendix II: Getting Mining Info
|
||||
|
||||
Here's the complete code for the `getmininginfo` command, with organized variable initiatialization, error checking, and variable cleanup. For this example we use a testnet network and show it's output.
|
||||
```
|
||||
file: getmininginfo.c
|
||||
|
||||
#include <jansson.h>
|
||||
#include <bitcoinrpc.h>
|
||||
|
||||
int main(void) {
|
||||
|
||||
bitcoinrpc_cl_t *rpc_client;
|
||||
bitcoinrpc_method_t *getmininginfo = NULL;
|
||||
bitcoinrpc_resp_t *btcresponse = NULL;
|
||||
bitcoinrpc_err_t btcerror;
|
||||
|
||||
json_t *jsonresponse = NULL;
|
||||
json_t *jsonresult = NULL;
|
||||
json_t *jsonblocks = NULL;
|
||||
int blocks;
|
||||
|
||||
bitcoinrpc_global_init();
|
||||
|
||||
rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "73bd45ba60ab8f9ff9846b6404769487", "127.0.0.1", 18443);
|
||||
|
||||
if (rpc_client) {
|
||||
getmininginfo = bitcoinrpc_method_init(BITCOINRPC_METHOD_GETMININGINFO);
|
||||
|
||||
if (!getmininginfo) {
|
||||
|
||||
printf("ERROR: Unable to initialize getmininginfo method!\n");
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
btcresponse = bitcoinrpc_resp_init();
|
||||
if (!btcresponse) {
|
||||
|
||||
printf("Error: Cannot initialize response object!\n");
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
bitcoinrpc_call(rpc_client, getmininginfo, btcresponse, &btcerror);
|
||||
|
||||
if (btcerror.code != BITCOINRPCE_OK) {
|
||||
|
||||
|
||||
printf("Error: getmininginfo error code %d [%s]\n", btcerror.code,btcerror.msg);
|
||||
exit(-1);
|
||||
|
||||
}
|
||||
|
||||
printf("Full Response: ");
|
||||
jsonresponse = bitcoinrpc_resp_get (btcresponse);
|
||||
printf ("%s\n", json_dumps (jsonresponse, JSON_INDENT(2)));
|
||||
|
||||
printf("\nJust the Result: ");
|
||||
jsonresult = json_object_get(jsonresponse,"result");
|
||||
printf ("%s\n", json_dumps (jsonresult, JSON_INDENT(2)));
|
||||
|
||||
jsonblocks = json_object_get(jsonresult,"blocks");
|
||||
blocks = json_integer_value(jsonblocks);
|
||||
printf("\nBlock Count: %d\n",blocks);
|
||||
|
||||
json_decref(jsonblocks);
|
||||
json_decref(jsonresult);
|
||||
json_decref(jsonresponse);
|
||||
|
||||
} else {
|
||||
|
||||
printf("ERROR: Failed to connect to server!\n");
|
||||
|
||||
}
|
||||
|
||||
bitcoinrpc_cl_free(rpc_client);
|
||||
bitcoinrpc_global_cleanup();
|
||||
|
||||
}
|
||||
```
|
||||
As usual, you can compile and run as follows:
|
||||
```
|
||||
$ cc getmininginfo.c -lbitcoinrpc -ljansson -o getmininginfo
|
||||
$ ./getmininginfo
|
||||
Full Response: {
|
||||
"result": {
|
||||
"blocks": 1773353,
|
||||
"difficulty": 10178811.406987719,
|
||||
"networkhashps": 129510207940932.2,
|
||||
"pooledtx": 9,
|
||||
"chain": "test",
|
||||
"warnings": "Warning: unknown new rules activated (versionbit 28)"
|
||||
},
|
||||
"error": null,
|
||||
"id": "6e502927-b065-486a-8182-bc1acd843bae"
|
||||
}
|
||||
|
||||
Just the Result: {
|
||||
"blocks": 1773353,
|
||||
"difficulty": 10178811.406987719,
|
||||
"networkhashps": 129510207940932.2,
|
||||
"pooledtx": 9,
|
||||
"chain": "test",
|
||||
"warnings": "Warning: unknown new rules activated (versionbit 28)"
|
||||
}
|
||||
|
||||
Block Count: 1773353
|
||||
```
|
||||
* :fire: ***What is the power of C?*** C allows you to take the next step beyond shell-scripting, permitting the creation of more comprehensive and robust programs. A few examples appear in the next two sections.
|
||||
|
Loading…
x
Reference in New Issue
Block a user