Today I am going to go over how to spend a segwit output from a P2SH-P2WKH address of the type built here.
To follow along with this tutorial, you should first build a P2SH-P2WKH address, by following my previous tutorial, and then head over to a testnet faucet and have some coins sent to that address.
Once your address has received some testnet coins, these outputs will be segwit ready and we will be able to spend them with a segwit transaction taking advantage of the reduced fees.
#include <bitcoin/bitcoin.hpp> #include <bitcoin/client.hpp> #include <string.h> using namespace bc; using namespace bc::wallet; using namespace bc::machine; using namespace bc::chain;
The first thing we are going to do, after including the requisite libbitcoin libraries of course, is to add two function that interact with libbitcoin-client allowing us the connect to the network. These functions are the getUTXOs function along with the bradcastTX function and are described in detail elsewhere on this blog.
points_value getUTXOs(payment_address Addy, uint64_t amount) { client::connection_type connection = {}; connection.retries = 3; connection.timeout_seconds = 8; connection.server = config::endpoint("tcp://testnet1.libbitcoin.net:19091"); client::obelisk_client client(connection); points_value val1; static const auto on_done = [&val1](const points_value& vals) { std::cout << "Success: " << vals.value() << std::endl; val1 = vals; }; static const auto on_error = [](const code& ec) { std::cout << "Error Code: " << ec.message() << std::endl; }; if(!client.connect(connection)) { std::cout << "Fail" << std::endl; } else { std::cout << "Connection Succeeded" << std::endl; } client.blockchain_fetch_unspent_outputs(on_error, on_done, Addy, amount, select_outputs::algorithm::greedy); client.wait(); //return allPoints; return val1; } void broadcastTX(transaction tx) { client::connection_type connection = {}; connection.retries = 3; connection.timeout_seconds = 8; connection.server = config::endpoint("tcp://testnet3.libbitcoin.net:19091"); client::obelisk_client client(connection); if(!client.connect(connection)) { std::cout << "Fail" << std::endl; } else { std::cout << "Connection Succeeded" << std::endl; } static const auto on_done = [](const code& ec) { std::cout << "Success: " << ec.message() << std::endl; }; static const auto on_error2 = [](const code& ec) { std::cout << "Error Code: " << ec.message() << std::endl; }; client.transaction_pool_broadcast(on_error2, on_done, tx); client.wait(); }
Once we have those, we are going to need to set up two helper functions. The first will simply take mnemonic as a string and convert it to and hd_private object so that we can access the necessary keys for our address.
hd_private getPrivateKey(std::string walletMnemonic) { std::string mnemonic = walletMnemonic; data_chunk seed = to_chunk(decode_mnemonic(split(mnemonic))); return hd_private(seed, hd_private::testnet); }
Next, we will create a function that takes a compressed public key and returns a witness script program in the form of an operation list. The witness program its self is simply an OP_0 followed by a bitcoin short hash of the compressed public key. This can be implemented in libbitcoin like so:
operation::list witnessProgram(ec_compressed publicKey) { short_hash KeyHash = bitcoin_short_hash(publicKey); return {operation(opcode(0)), operation(to_chunk(KeyHash))}; }
No we can go to the main function and start building our transaction by first getting out private and public keys for our desired mnemonic.
int main() { hd_private privateKey = getPrivateKey("portion shop border uniform loan grab dismiss boss wild magnet strong supreme era swing else keep voyage forest"); ec_compressed compressedPublicKey = privateKey.to_public().point(); }
Once we have our keys, we can wrap our witness program in a script object, take the short hash of the script and create a P2SH address from that. This will allow us to send our change back to the same address.
script P2WPKH = script(witnessProgram(compressedPublicKey)); short_hash WitnessProgramHash = bitcoin_short_hash(P2WPKH.to_data(0)); payment_address fromAddress = payment_address(P2WPKH, payment_address::testnet_p2sh);
Then we set up our transaction object and create our output as we usually would.
transaction tx; tx.set_version(1u); //Make Output payment_address toAddress = wallet::payment_address("mnrnjVFimDFrNkszzMtecr4yrMKmEuMRbv"); uint64_t amount; btc_to_satoshi(amount, "0.5"); tx.outputs().push_back(output(amount, script(script().to_pay_key_hash_pattern(toAddress.hash()))));
Once we have our output set up, we can get the utxos via our libbitcoin client function and then set up our change output.
Note: For this tutorial we are assuming that we are only using 1 UTXO, so if you are following along make sure that the amount you’re sending is small enough to only require 1 utxo from your address.
//Make Change points_value UTXOs = getUTXOs(fromAddress, amount); uint64_t change = UTXOs.value() - amount - 10000; script outScript = script(script().to_pay_script_hash_pattern(fromAddress.hash())); tx.outputs().push_back(output(change, outScript));
Now we can start building our input as usual, the only extra thing we will need to do is save the value of our UTXO as it is required to sign a witness sighash.
//Make Input input workingInput = input(); workingInput.set_previous_output(output_point(UTXOs.points[0])); workingInput.set_sequence(max_input_sequence); tx.inputs().push_back(workingInput); uint64_t previous_amount = UTXOs.points[0].value();
From here we are ready to create our signature, to do this according for a segwit transaction we are going to need to sign the usual components in addition to a script_code, script version, the index of the our utxo and the amount of the utxo.
The script code is simply a P2KH pattern script with our public key and the script version is 0.
The sig is implemented like this:
//Make Signature script script_code = script::to_pay_key_hash_pattern(bitcoin_short_hash(compressedPublicKey)); endorsement sig; script().create_endorsement(sig, privateKey.secret(), script_code, tx, tx.inputs()[0].previous_output().index(), sighash_algorithm::all, script_version::zero, previous_amount);
Once the signature is generated we can set the witness data for our input, which is just a data stack of the signature followed by the compressed public key. This data is the portion of the transaction data that is discounted (in terms of fees) with segregated witness.
//Make Witness data_stack witness_data {to_chunk(sig), to_chunk(compressedPublicKey)}; tx.inputs()[0].set_witness(witness(witness_data));
Finally we can set the input script with the P2WPKH script data.
//set input script data_chunk scriptChunk = to_chunk(P2WPKH.to_data(1)); tx.inputs()[0].set_script(script(scriptChunk, false));
Now we will print out the serialized transaction data and broadcast the transaction to the network.
std::cout << encode_base16(tx.to_data(1, 1)) << std::endl; broadcastTX(tx);
As usual we will build and run this on the command line with:
$ g++ -std=c++11 -o spend segwit_spend.cpp $(pkg-config --cflags libbitcoin --libs libbitcoin libbitcoin-client) $ ./spend
As usual full code can be found at github.