diff --git a/12_3_Programming_Bitcoind_with_C.md b/12_3_Programming_Bitcoind_with_C.md index 045c111..0da8900 100644 --- a/12_3_Programming_Bitcoind_with_C.md +++ b/12_3_Programming_Bitcoind_with_C.md @@ -324,3 +324,340 @@ 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. + +## Appendix: Sending to an Address + +Here's the complete code for sending funds to someone using C. + +WARNING: This code has not been robustly checked. Beyond that, it's to be functionally minimalistic, not containing necessary error-checking. If you use it as a foundation of your own work, be sure to check it carefully and to make it a lot more robust. + +``` +file: sendtoaddress.c + +#include +#include +#include + +int main(int argc, char *argv[]) { + + /* 1. Request an Address and an Amount */ + + if (argc != 3) { + + printf("ERROR: Only %i arguments! Correct usage is '%s [recipient] [amount]'\n",argc-1,argv[0]); + exit(-1); + + } + char *tx_recipient = argv[1]; + double tx_amount = atof(argv[2]); + + /* 2. Set an Arbitrary Fee */ + + double tx_fee = 0.0005; + double tx_total = tx_amount + tx_fee; + + /* X. Prepare Your RPC */ + + bitcoinrpc_cl_t *rpc_client; + bitcoinrpc_method_t *rpc_method = NULL; + bitcoinrpc_resp_t *btcresponse = NULL; + bitcoinrpc_err_t btcerror; + + json_t *lu_response = NULL; + json_t *lu_result = NULL; + + bitcoinrpc_global_init(); + + rpc_client = bitcoinrpc_cl_init_params ("bitcoinrpc", "73bd45ba60ab8f9ff9846b6404769487", "127.0.0.1", 18332); + + if (rpc_client) { + + /* 3. Find a UTXO */ + + rpc_method = bitcoinrpc_method_init(BITCOINRPC_METHOD_LISTUNSPENT); + + if (!rpc_method) { + + printf("ERROR: Unable to initialize listunspent method!\n"); + exit(-1); + + } + + btcresponse = bitcoinrpc_resp_init(); + if (!btcresponse) { + + printf("Error: Cannot initialize response object!"); + exit(-1); + + } + + bitcoinrpc_call(rpc_client, rpc_method, btcresponse, &btcerror); + + if (btcerror.code != BITCOINRPCE_OK) { + + printf("Error: listunspent error code %d [%s]\n", btcerror.code,btcerror.msg); + exit(-1); + + } + + lu_response = bitcoinrpc_resp_get (btcresponse); + lu_result = json_object_get(lu_response,"result"); + + int i; + + const char *tx_id = 0; + int tx_vout = 0; + double tx_value = 0.0; + + for (i = 0 ; i < json_array_size(lu_result) ; i++) { + + json_t *lu_data = NULL; + lu_data = json_array_get(lu_result, i); + + json_t *lu_value = NULL; + lu_value = json_object_get(lu_data,"amount"); + tx_value = json_real_value(lu_value); + + if (tx_value > tx_total) { + + json_t *lu_txid = NULL; + lu_txid = json_object_get(lu_data,"txid"); + tx_id = strdup(json_string_value(lu_txid)); + + json_t *lu_vout = NULL; + lu_vout = json_object_get(lu_data,"vout"); + tx_vout = json_integer_value(lu_vout); + + json_decref(lu_value); + json_decref(lu_txid); + json_decref(lu_vout); + json_decref(lu_data); + break; + + } + + } + + json_decref(lu_result); + json_decref(lu_response); + + if (!tx_id) { + + printf("Very Sad: You don't have any UTXOs larger than %f",tx_total); + exit(-1); + } + + bitcoinrpc_method_free(rpc_method); + + /* 4. Create a Change Address */ + + rpc_method = bitcoinrpc_method_init(BITCOINRPC_METHOD_GETRAWCHANGEADDRESS); + + if (!rpc_method) { + + printf("ERROR: Unable to initialize createrawchangeaddress method!\n"); + exit(-1); + + } + + bitcoinrpc_call(rpc_client, rpc_method, btcresponse, &btcerror); + + if (btcerror.code != BITCOINRPCE_OK) { + + printf("Error: createrawchangeaddress error code %d [%s]\n", btcerror.code, btcerror.msg); + exit(-1); + + } + + lu_response = bitcoinrpc_resp_get (btcresponse); + lu_result = json_object_get(lu_response,"result"); + char *changeaddress = strdup(json_string_value(lu_result)); + + json_decref(lu_result); + json_decref(lu_response); + bitcoinrpc_method_free(rpc_method); + + /* 5. Create a Raw Transaction */ + + /* 5.1. Create the Input Parameters */ + + json_t *inputtxid = NULL; + inputtxid = json_object(); + + json_object_set_new(inputtxid,"txid",json_string(tx_id)); + json_object_set_new(inputtxid,"vout",json_integer(tx_vout)); + + json_t *inputparams = NULL; + inputparams = json_array(); + json_array_append(inputparams,inputtxid); + + /* 5.2 Create the Output Parameters */ + + json_t *outputparams = NULL; + outputparams = json_object(); + + char tx_amount_string[32]; + sprintf(tx_amount_string,"%.8f",tx_amount); + char tx_change_string[32]; + sprintf(tx_change_string,"%.8f",tx_value - tx_total); + + json_object_set(outputparams, + tx_recipient, + json_string(tx_amount_string)); + json_object_set(outputparams, + changeaddress, + json_string(tx_change_string)); + + /* 5.3 Create the Parameter Array */ + + json_t *params = NULL; + params = json_array(); + json_array_append(params,inputparams); + json_array_append(params,outputparams); + + /* 5.4 Make the RPC Call */ + + rpc_method = bitcoinrpc_method_init(BITCOINRPC_METHOD_CREATERAWTRANSACTION); + + if (!rpc_method) { + + printf("ERROR: Unable to initialize createrawtransaction method!\n"); + exit(-1); + + } + + + if (bitcoinrpc_method_set_params(rpc_method, params) != BITCOINRPCE_OK) { + + fprintf (stderr, "Error: Could not set params for createrawtransaction"); + + } + + json_decref(inputtxid); + json_decref(inputparams); + json_decref(outputparams); + json_decref(params); + + bitcoinrpc_call(rpc_client, rpc_method, btcresponse, &btcerror); + + if (btcerror.code != BITCOINRPCE_OK) { + + printf("Error: createrawtransaction error code %d [%s]\n", btcerror.code,btcerror.msg); + exit(-1); + + } + + lu_response = bitcoinrpc_resp_get(btcresponse); + lu_result = json_object_get(lu_response,"result"); + + char *tx_rawhex = strdup(json_string_value(lu_result)); + + json_decref(lu_result); + json_decref(lu_response); + bitcoinrpc_method_free(rpc_method); + + /* 6. Sign the Transaction */ + + params = json_array(); + json_array_append_new(params,json_string(tx_rawhex)); + + rpc_method = bitcoinrpc_method_init(BITCOINRPC_METHOD_SIGNRAWTRANSACTION); + + if (!rpc_method) { + + printf("ERROR: Unable to initialize signrawtransaction method!\n"); + exit(-1); + + } + + if (bitcoinrpc_method_set_params(rpc_method, params) != BITCOINRPCE_OK) { + + fprintf (stderr, "Error: Could not set params for signrawtransaction"); + + } + + json_decref(params); + + bitcoinrpc_call(rpc_client, rpc_method, btcresponse, &btcerror); + + if (btcerror.code != BITCOINRPCE_OK) { + + printf("Error: signrawtransaction error code %d [%s]\n", btcerror.code,btcerror.msg); + exit(-1); + + } + + lu_response = bitcoinrpc_resp_get(btcresponse); + lu_result = json_object_get(lu_response,"result"); + + json_t *lu_signature = json_object_get(lu_result,"hex"); + char *tx_signrawhex = strdup(json_string_value(lu_signature)); + json_decref(lu_signature); + + json_decref(lu_result); + json_decref(lu_response); + bitcoinrpc_method_free(rpc_method); + + /* 7. Sign the Transaction */ + + params = json_array(); + json_array_append_new(params,json_string(tx_signrawhex)); + + rpc_method = bitcoinrpc_method_init(BITCOINRPC_METHOD_SENDRAWTRANSACTION); + + if (!rpc_method) { + + printf("ERROR: Unable to initialize sendrawtransaction method!\n"); + exit(-1); + + } + + if (bitcoinrpc_method_set_params(rpc_method, params) != BITCOINRPCE_OK) { + + fprintf (stderr, "Error: Could not set params for sendrawtransaction"); + + } + + json_decref(params); + + bitcoinrpc_call(rpc_client, rpc_method, btcresponse, &btcerror); + + if (btcerror.code != BITCOINRPCE_OK) { + + printf("Error: endrawtransaction error code %d [%s]\n", btcerror.code,btcerror.msg); + exit(-1); + + } + + lu_response = bitcoinrpc_resp_get(btcresponse); + lu_result = json_object_get(lu_response,"result"); + + char *tx_newid = strdup(json_string_value(lu_result)); + + printf("Txid: %s\n",tx_newid); + + json_decref(lu_result); + json_decref(lu_response); + bitcoinrpc_method_free(rpc_method); + + } else { + + printf("ERROR: Failed to connect to server!\n"); + + } + + bitcoinrpc_cl_free(rpc_client); + bitcoinrpc_global_cleanup(); + +} +``` +Compile this as usual: +``` +$ cc sendtoaddress.c -lbitcoinrpc -ljansson -o sendtoaddress +``` +You can then use it to send funds to an address: +``` +$ ./sendtoaddress mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf .10 +Txid: 23f3faea4e1e933e981fe7ed66008f33c00983a865f6ccce9af0005e9670e916 +``` +You can see information on this transaction that we sent [here](https://live.blockcypher.com/btc-testnet/tx/23f3faea4e1e933e981fe7ed66008f33c00983a865f6ccce9af0005e9670e916/).