The Testnet
Building and signing a raw transaction in libbitcoin is relatively easy but can still be prone to costly errors. For this reason, I’ve adapted my previous HD_Wallet class to be formatted as a testnet wallet.
The bitcoin tesnet is basically a development server for the blockchain and bitcoin protocol. It essentially runs as an altcoin with the same rules as bitcoin except that the difficulty is much lower and it is easier to test non-standard transactions on chain. This makes it a great place to practice bitcoin programming without risking any real btc. Tesnet coin’s are essentially valueless and can be obtained for free from several faucets.
In order to change my wallet class to work on the testnet, I will simply need to pass a testnet prefix code to the libbitcoin functions that instantiate the HD master key. Which in my case is in all three of the HD wallet constructors where I will pass the version constant wallet::hd_private::testnet.
HD_Wallet()
{
entropy = data_chunk(16);
pseudo_random_fill(entropy);
mnemonic = wallet::create_mnemonic(entropy);
seed = to_chunk(wallet::decode_mnemonic(mnemonic));
privateKey = wallet::hd_private(seed, wallet::hd_private::testnet);
publicKey = privateKey.to_public();
}
In addition to passing the version prefix in all three constructors, I will pass the testnet address prefix(0x6f) to the payment_adress constructor that directly creates child key addresses.
wallet::payment_address childAddress(int index)
{
return wallet::payment_address(wallet::ec_public(childPublicKey(index).point()), 0x6f);
}
The HD wallet is now adapted to work on the testnet.
Getting Testnet Coins
Now, once I’ve generated a new wallet for the testnet, I’ll save the mnemonic and the first child address I want to use. In order to make a transaction, I’ll need some coins to spend, luckily many bitcoin faucets give out free testnet coins. https://testnet.manu.backend.hamburg/faucet and http://tpfaucet.appspot.com/ to name just a couple.
Once I have coins in my wallet, I can use libbitcoin tools to make a raw transaction.
Bitcoin Transactions
Bitcoin transactions are comprised of 2 building blocks, Inputs and an Outputs. Inputs are your unspent coins, where they came from and a script proving they were sent to you. Outputs, are where your coins are going, how many your sending and a script saying how your locking them up.
Each output corresponds to a destination for your bitcoin. they are created from an amount of BTC to spend and a script which locks the output to the receivers keys, generally a P2KH.
Each input corresponds to money you’ve received from a previous transaction and is created from a hash of the transaction that received the coin, the index of the output in it’s source transaction and the script that locks the coin.
Once, Inputs and Outputs are added to a transaction the whole thing is signed by the senders private key and then that signature is added to the redeem script to create the unlocking script which replaces the locking script in the input before the transaction is broadcast to the network.
Raw Transaction Builder(TXB)
Raw Transaction Builder is going to be a command line utility that allows a user to:
- Import an HD wallet to spend from
- Enter a Destination and Amount to send
- Enter a UTXO and index to spend
- See the corresponding scripts and signatures that make the transaction
- See the final raw transaction
It will only work for HD wallets and will be assuming the user is constructing a pay to key hash payment.
TX Builder begins by including the necessary library as well as namespacing.
#include <bitcoin/bitcoin.hpp>;
#include "HD_walletTESTNET.cpp"
#include <string.h>;
using namespace bc;
using namespace bc::wallet;
using namespace bc::machine;
using namespace bc::chain;
Then, it uses a get input function to obtain strings from the user.
std::string getInput2()
{
std::string input;
getline(cin, input);
return input;
}
Now, I can work inside my main function. First, instantiating an HD Wallet from a users inputted mnemonic and child key index. This is the wallet the user will spend from.
int main()
{
//import Wallet
std::cout << "Import Wallet Via Mnemonic: " << std::endl;
std::string Mnemonic1 = getInput2();
HD_Wallet wallet1(split(Mnemonic1));
std::cout << "\nChild Index To Spend From: " << std::endl;
int child = atoi(getInput(2).c_str());
data_chunk pubkey1 = to_chunk(wallet1.childPublicKey(child).point());
}
Now, I can construct my output by prompting the user for an address. Once I construct a payment_address object from the string I can pass it’s hash member to a pay to key hash script factory, this creates the output locking script.
//Make Output//
std::cout << "\nEnter Destination Address: " << std::endl;
std::string Destination = getInput2();
payment_address destinationAddy(Destination);
script outputScript = script().to_pay_key_hash_pattern(destinationAddy.hash());
Next, the Transaction Builder needs to get the amount of bitcoin to spend. Making use of the base10 format function in libbitcoin allows us to take a bitcoin amount from the user and convert it to a satoshi amount, which the output uses. This satoshi amount and the output locking script are passed to an output constructor to create an output argument. TXB then displays this output to the user.
std::cout << "\nEnter Amount(BTC) To Send: " << std::endl;
std::string BTC = getInput2();
uint64_t Satoshis;
decode_base10(Satoshis, BTC, 8);
output output1(Satoshis, outputScript);
std::cout << "\nAmount: " << encode_base10(output1.value(), 8) << "BTC : Output Script: " << output1.script().to_string(0) << "\n" << std::endl;
Now that the output is built, the input is still needed. So, the user will be prompted for a spendable utxo hash and the index of the transaction that the utxo is at. The difference in amount between this utxo and the output is the miner fee. This utxo is then put into an output_point object and used in the input.
//Get UTXO
std::cout << "\nEnter UTXO Hash To Spend: " << std::endl;
std::string hashString = getInput2();
std::cout << "\nEnter Output Index: " << std::endl;
std::string index = getInput2();
uint32_t index1 = atoi(index.c_str());
hash_digest utxoHash;
decode_hash(utxoHash, hashString);
output_point utxo(utxoHash, index1);
Now in order to sign this transaction, the previous locking script is needed. This is the script that was used to pay us, essentially a pay to key hash script to our public key hash. We can recreate it using our public key and a script pattern factory.
//Previous Locking Script
script lockingScript = script().to_pay_key_hash_pattern(bitcoin_short_hash(pubkey1));
std::cout << "\nPrevious Locking Script: " << std::endl;
std::cout << lockingScript.to_string(0) << "\n" << std::endl;
With that we can build our input object using it’s setters, like this:
//make Input
input input1 = input();
input1.set_previous_output(utxo);
input1.set_sequence(0xffffffff);
Now that we have our input and output created we can construct our transaction. Note here, that transactions can have many inputs and outputs so we are adding our inputs to an input vector member, hence the use of the push_back() method.
//build TX
transaction tx = transaction();
tx.inputs().push_back(input1);
tx.outputs().push_back(output1);
With all the parts of our transaction done, we can sign the whole thing using our private key and transaction.
//Endorse TX
endorsement sig;
if(lockingScript.create_endorsement(sig, wallet1.childPrivateKey(1).secret(), lockingScript, tx, 0u, all))
{
std::cout << "Signature: " << std::endl;
std::cout << encode_base16(sig) << "\n" << std::endl;
}
Now that we have the signature for this transaction, we can construct the signature script by adding the signature and then the public key to a script. After that we just need to replace the script of our transaction’s first input to our new signature script and output the entire transaction to the user.
//make Sig Script
operation::list sigScript;
sigScript.push_back(operation(sig));
sigScript.push_back(operation(pubkey1));
script unlockingScript(sigScript);
//Make Signed TX
tx.inputs()[0].set_script(unlockingScript);
std::cout << "Raw Transaction: " << std::endl;
std::cout << encode_base16(tx.to_data()) << std::endl;
…and compile as usual:
$ g++ -std=c++11 -o ./txBuilder rawTX.cpp HD_WalletTESTNET.cpp $(pkg-config --cflags libbitcoin --libs libbitcoin)
$./rawTX
Bam! Great Transaction! Now I just need to send the transaction to the network by broadcasting to a BS. Alternatively, I can copy the raw encoded transaction and paste it into this transaction broadcaster and the transaction will be processed by the miners.