Merge pull request #135 from javiervargas/master

15_1_Accessing_Bitcoind_with_C.md
This commit is contained in:
Shannon Appelcline 2020-06-24 11:21:40 -10:00 committed by GitHub
commit 87d5ca072a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 189 additions and 40 deletions

View File

@ -2,18 +2,37 @@
> **NOTE:** This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.
[needs new intro]
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.
## Set 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:
```
$ sudo apt-get install make gcc libcurl4-openssl-dev libjansson-dev uuid-dev
Suggested packages:
libcurl4-doc libidn11-dev libkrb5-dev libldap2-dev librtmp-dev libssh2-1-dev
The following NEW packages will be installed:
libcurl4-openssl-dev libjansson-dev uuid-dev
0 upgraded, 3 newly installed, 0 to remove and 4 not upgraded.
Need to get 358 kB of archives.
After this operation, 1.696 kB of additional disk space will be used.
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/
```
@ -36,11 +55,55 @@ INSTALL_HEADERPATH := $(INSTALL_PREFIX)/usr/include
Then you can compile:
```
$ make
$ ~/libbitcoinrpc-master$ 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
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc.o -c src/bitcoinrpc.c
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_resp.o -c src/bitcoinrpc_resp.c
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_cl.o -c src/bitcoinrpc_cl.c
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -o src/bitcoinrpc_method.o -c src/bitcoinrpc_method.c
gcc -fPIC -O3 -g -Wall -Werror -Wextra -std=c99 -D VERSION=\"0.2\" -shared -Wl,-soname,libbitcoinrpc.so.0 \
src/bitcoinrpc_err.o src/bitcoinrpc_global.o src/bitcoinrpc.o src/bitcoinrpc_resp.o src/bitcoinrpc_cl.o src/bitcoinrpc_method.o \
-o .lib/libbitcoinrpc.so.0.2 \
-Wl,--copy-dt-needed-entries -luuid -ljansson -lcurl
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:
```
$ 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
```
## Write Code in C
@ -212,7 +275,7 @@ 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.
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
@ -233,7 +296,7 @@ int main(void) {
bitcoinrpc_global_init();
rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "73bd45ba60ab8f9ff9846b6404769487", "127.0.0.1", 18332);
rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "73bd45ba60ab8f9ff9846b6404769487", "127.0.0.1", 18443);
if (rpc_client) {
getmininginfo = bitcoinrpc_method_init(BITCOINRPC_METHOD_GETMININGINFO);
@ -295,32 +358,26 @@ As usual, you can compile and run as follows:
$ cc getmininginfo.c -lbitcoinrpc -ljansson -o getmininginfo
$ ./getmininginfo
Full Response: {
"id": "03406237-cd8f-466d-ac31-86711ea9d1db",
"result": {
"blocks": 1147154,
"errors": "Warning: unknown new rules activated (versionbit 28)",
"pooledtx": 0,
"currentblocksize": 0,
"currentblockweight": 0,
"currentblocktx": 0,
"difficulty": 313525.08513550513,
"networkhashps": 3958339463617.417,
"chain": "test"
"blocks": 1773353,
"difficulty": 10178811.406987719,
"networkhashps": 129510207940932.2,
"pooledtx": 9,
"chain": "test",
"warnings": "Warning: unknown new rules activated (versionbit 28)"
},
"error": null
"error": null,
"id": "6e502927-b065-486a-8182-bc1acd843bae"
}
Just the Result: {
"blocks": 1147154,
"errors": "Warning: unknown new rules activated (versionbit 28)",
"pooledtx": 0,
"currentblocksize": 0,
"currentblockweight": 0,
"currentblocktx": 0,
"difficulty": 313525.08513550513,
"networkhashps": 3958339463617.417,
"chain": "test"
"blocks": 1773353,
"difficulty": 10178811.406987719,
"networkhashps": 129510207940932.2,
"pooledtx": 9,
"chain": "test",
"warnings": "Warning: unknown new rules activated (versionbit 28)"
}
Block Count: 1147154
Block Count: 1773353
```

View File

@ -11,10 +11,11 @@ We're going to program a simplistic first cut version of `sendtoaddress`, which
1. Request an address and an amount
2. Set an arbitrary fee
3. Find a UTXO that's large enough for the amount + the fee
4. Create a change address
5. Create a raw transaction that sends from the UTXO to the address and the change address
6. Sign the transaction
7. Send the transaction
4. Create a bench 32 address
5. Create a change address
6. Create a raw transaction that sends from the UTXO to the address and the change address
7. Sign the transaction
8. Send the transaction
### Plan for Your Future
@ -64,7 +65,7 @@ float tx_total = tx_amount + tx_fee;
### X. Prepare Your RPC
Obviously, you're going to need to get all of your variables ready again, as discussed in [§12.2: Accessing Bitcoind with C](12_2_Accessing_Bitcoind_with_C.md). You also need to initialize your library, connect your RPC client, and prepare your response object:
Obviously, you're going to need to get all of your variables ready again, as discussed in [§15.1: Accessing Bitcoind with C](15_1_Accessing_Bitcoind_with_C.md). You also need to initialize your library, connect your RPC client, and prepare your response object:
```
bitcoinrpc_global_init();
rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "73bd45ba60ab8f9ff9846b6404769487", "127.0.0.1", 18332);
@ -152,7 +153,25 @@ if (!tx_id) {
> **WARNING:** A real program would use subroutines for this sort of lookup, so that you could confidentally call various RPCs from a library of C functions. We're just going to blob it all into `main` as part of our KISS philosophy of simple examples.
### 4. Create a Change Address
### 4. Create a Bech32 Address
Repeat the standard RPC-lookup methodology to get a new address using get new address method.
```
rpc_method = bitcoinrpc_method_init(BITCOINRPC_METHOD_GETNEWADDRESS);
lu_response = bitcoinrpc_resp_get (btcresponse);
lu_result = json_object_get(lu_response,"result");
char *address = strdup(json_string_value(lu_result));
printf("adress %s \n", address);
```
Output
```
$ cc getaddress.c -lbitcoinrpc -ljansson -o getaddress
$ ./getaddress
adress tb1qpxlguwckkqalcg6xrv4ujlmkzqe79nkngcvudf
```
### 5. Create a Change Address
Repeat the standard RPC-lookup methodology to get a change address:
```
@ -183,7 +202,7 @@ The only difference is in what particular information we extract from our JSON o
> **WARNING:** Here's another place that a subroutine would be really nice: to abstract out the whole RPC method initialization and call.
### 5. Create a Raw Transaction
### 6. Create a Raw Transaction
Creating the actual raw transaction is the other tricky part of programming your `sendtoaddress` replacement. That's because it requires the creation of a complex JSON object as a paramter.
@ -194,7 +213,7 @@ createrawtransaction [{"txid":"id","vout":n},...] {"address":amount,"data":"hex"
```
To review, your inputs will be a JSON array containing one JSON object for each UTXO. Then the ouputs will all be in one JSON object. It's easiest to create these JSON elements from the inside out, using `jansson` commands.
#### 5.1. Create the Input Parameters
#### 6.1. Create the Input Parameters
To create the input object for your UTXO, use `json_object`, then fill it with key-values using either `json_object_set_new` (for newly created references) or `json_object_set` (for existing references):
```
@ -213,7 +232,7 @@ inputparams = json_array();
json_array_append(inputparams,inputtxid);
```
#### 5.2 Create the Output Parameters
#### 6.2 Create the Output Parameters
To create the output array for your transaction, follow the same format, creating a JSON object with `json_object`, then filling it with `json_object_set`:
```
@ -231,7 +250,7 @@ json_object_set(outputparams, changeaddress, json_string(tx_change_string));
> **WARNING:** You might expect to input your Bitcoin values as numbers, using `json_real`. Unfortunately, this exposes one of the major problems with integrating the `jansson` library and Bitcoin. Bitcoin is only valid to eight significant digits past the decimal point. You might recall that .00000001 BTC is a satoshi, and that's the smallest possible division of a Bitcoin. Doubles in C offer more significant digits than that, though they're often imprecise out past eight decimals. If you try to convert straight from your double value in C (or a float value, for that matter) to a Bitcoin value, the imprecision will often create a Bitcoin value with more than eight significant digits. Before Bitcoin Core 0.12 this appears to work, and you could use `json_real`. But as of Bitcoin Core 0.12, if you try to give `createrawtransaction` a Bitcoin value with too many significant digits, you'll instead get an error and the transaction will not be created. As a result, if the Bitcoin value has _ever_ become a double or float, you must reformat it to eight significant digits past the digit before feeding it in as a string. This is obviously a kludge, so you should make sure it continues to work in future versions of Bitcoin Core.
#### 5.3 Create the Parameter Array
#### 6.3 Create the Parameter Array
To finish creating your parameters, simply to bundle them all up in a JSON array:
```
@ -240,7 +259,7 @@ params = json_array();
json_array_append(params,inputparams);
json_array_append(params,outputparams);
```
#### 5.4 Make the RPC Call
#### 6.4 Make the RPC Call
Use the normal method to create your RPC call:
```
@ -263,7 +282,7 @@ lu_result = json_object_get(lu_response,"result");
char *tx_rawhex = strdup(json_string_value(lu_result));
```
### 6. Sign the Transaction
### 7. Sign the Transaction
It's a lot easier to assign a simple parameter to a function. You just create a JSON array, then assign the parameter to the array:
```
@ -294,7 +313,7 @@ json_decref(lu_signature);
> ***WARNING:*** A real-world program would obviously carefully test the response of every RPC command to make sure there were no errors. That's especially true for `signrawtransaction`, because you might end up with a partially signed transaction. Worse, if you don't check the errors in the JSON object, you'll just see the `hex` and not realize that it's either unsigned or partially signed.
### 7. Send the Transaction
### 8. Send the Transaction
You can now send your transaction, using all of the previous techniques:
```
@ -323,7 +342,7 @@ The entire code, with a _little_ more error-checking appears in the Appendix.
## Summary: Programming Bitcoind with C
Using the techniques outlined in [§12.2](12_2_Accessing_Bitcoind_with_C.md) you can write a much more complex program using C calls. This section offers an example, with the first cut of a program that will send money to an address, without your users worrying about where it's coming from, how much they're paying as a fee, or how they get their change back. Obviously, a real-world program would need much better user-input control and error handling, but by outlining how the RPC code works, this section opens up the programming doorways to allow you to take the next step.
Using the techniques outlined in [§15.1](15_1_Accessing_Bitcoind_with_C.md) you can write a much more complex program using C calls. This section offers an example, with the first cut of a program that will send money to an address, without your users worrying about where it's coming from, how much they're paying as a fee, or how they get their change back. Obviously, a real-world program would need much better user-input control and error handling, but by outlining how the RPC code works, this section opens up the programming doorways to allow you to take the next step.
## Appendix: Sending to an Address

View File

@ -0,0 +1,73 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
# Contributor License Agreement
Version 1.0
Name: Javier Vargas
E-Mail: jevargas@uniandes.edu.co
Legal Jurisdiction: Madrid, Spain
Project: https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line
Date: 06/19/2020
## Purpose
This agreement gives Blockchain Commons, LLC the permission it needs in order to accept my contributions into its open software project and to manage the intellectual property in that project over time.
## License
I hereby license Blockchain Commons, LLC to:
1. do anything with my contributions that would otherwise infringe my copyright in them
2. do anything with my contributions that would otherwise infringe patents that I can or become able to license
3. sublicense these rights to others on any terms they like
## Reliability
I understand that Blockchain Commons will rely on this license. I may not revoke this license.
## Awareness
I promise that I am familiar with legal rules, like ["work made for hire" rules](http://worksmadeforhire.com), that can give employers and clients ownership of intellectual property in work that I do. I am also aware that legal agreements I might sign, like confidential information and invention assignment agreements, will usually give ownership of intellectual property in my work to employers, clients, and companies that I found. If someone else owns intellectual property in my work, I need their permission to license it.
## Copyright Guarantee
I promise not to offer contributions to the project that contain copyrighted work that I do not have legally binding permission to contribute under these terms. When I offer a contribution with permission, I promise to document in the contribution who owns copyright in what work, and how they gave permission to contribute it. If I later become aware that one of my contributions may have copyrighted work of others that I did not have permission to contribute, I will notify Blockchain Commons, in confidence, immediately.
## Patent Guarantee
I promise not to offer contributions to the project that I know infringe patents of others that I do not have permission to contribute under these terms.
## Open Source Guarantee
I promise not to offer contributions that contain or depend on the work of others, unless that work is available under a license that [Blue Oak Council rates bronze or better](https://blueoakconcil.org/list), such as the MIT License, two- or three-clause BSD License, the Apache License Version 2.0, or the Blue Oak Model License 1.0.0. When I offer a contribution containing or depending on others' work, I promise to document in the contribution who licenses that work, along with copies of their license terms.
## Disclaimers
***As far as the law allows, my contributions come as is, without any warranty or condition. Other than under [Copyright Guarantee](#copyright-guarantee), [Patent Guarantee](#patent-guarantee), or [Open Source Guarantee](#open-source-guarantee), I will not be liable to anyone for any damages related to my contributions or this contributor license agreement, under any kind of legal claim.***
- - - ---
To sign this Contributor License Agreement, fill in `$name`, `$email`, and `$date` above. Then sign using GPG using the following command `gpg --armor --clearsign --output ./signed-cla/CLA.YOURGITHUBNAME.YOURGPGFINGERPRINT.asc CLA.md`, then either submit your signed Contributor License Agreement to this repo as a GPG signed Pull Request or email it to [ChristopherA@BlockchainCommons.com](mailto:ChristopherA@BlockchainCommons.com).
-----BEGIN PGP SIGNATURE-----
iQGzBAEBCgAdFiEEcD2N9So1EMOo5qpdy91nx0pUcAQFAl7yQ8cACgkQy91nx0pU
cAQnIQwAsIn+KseET6IN9SqwQoj6zpZX8pXCHohxqFt9c87LbPraKgv/TzuyUphI
AMLRt/a4hOng8cUKdD9bsq70eiGHpW/uOd6VUQKMMdvgyr2+FCY0ljqxB2ZkVccN
/sApr0WAlNnfpxaQAYm6ft+FG3rGKEtdwUBzQ0kGShUdt8I0EiHwvevS5x1SNwaT
kruRWy8xoLD7hWQ3OPOqserwtkqK8fIu4PLVtoiBA+qnFpTCoaREjLoEHpX/KKkk
W2r0VxMm2tr+n0+BgLVOAMMSJkhOA6/hlzFXmUIl5WOOeh5AlLlcowGeaQRsCRJL
Sgd3KwOBA7XRFLai72AjSybLnIVn3yhwoHaCPQiVe4Z5qYf0QDYg1W7janYITgMP
s4a2p/4adH/OP5Xkm/8KFzOLCc0Ixjlm8/jHIfm8odIRCcyEiqJYon2ZSBbYiM3N
UPcxE+XrGnSmanK7PEOjrL9D+pLyozcsZc+HZ56CFwoWUn6PNzBUqeLcalAp5f2X
BRGP20HH
=0qBm
-----END PGP SIGNATURE-----