Bitcoin XS Modules

Summary

This is a library to allow users to write Bitcoin applications, including SPV nodes, in Perl. The library relies on a C library called picocoin. In order to get the library to work with Perl XS, some of the header files were modified and are located here. This set of Perl modules, the customized picocoin library, and all related dependencies have been compiled into debian packages.

This guide goes over setting up a simple HD Tree, drafting a P2SH transaction, and running an SPV client.

For instructions on how to install libcbitcoin-perl, see this page.

As for the Github page for libcbitcoin-perl, look here.

HD Trees

There are several ways to think about an HD Tree. Probably the simplest way is as a "Wallet".

Background

The Perl module CBitcoin::CBHD acts as an intuitive interface in handling BIP32 objects as described below:
  BIP: 32
  Title: Hierarchical Deterministic Wallets
  Author: Pieter Wuille <pieter.wuille@gmail.com>
  Status: Final
  Type: Informational
  Created: 2012-02-11
This document describes hierarchical deterministic wallets (or "HD Wallets"): wallets which can be shared partially or entirely with different systems, each with or without the ability to spend coins.

The specification is intended to set a standard for deterministic wallets that can be interchanged between different clients. Although the wallets described here have many features, not all are required by supporting clients.

The specification consists of two parts. In a first part, a system for deriving a tree of keypairs from a single seed is presented. The second part demonstrates how to build a wallet structure on top of such a tree.

The Root

The first step in creating the HD Tree is to generate the root. Now, since Bitcoin is a form of cash, protecting the root should be your top priority. As is shown in the sample script below, there are two ways to create the root: first, with a seed; and second, using /dev/random . /dev/random is the most secure way to create a root without an adversary being able to guess the root you generate. If you decide to use /dev/random , then you need to keep the private part stored somewhere. To see the base58 encoded private part (the xprv value), just use export_xprv .

use CBitcoin;
use CBitcoin::CBHD;

my $root_withseed = CBitcoin::CBHD->generate("my magic seed! 123456789012345678901234567890");
my $root_devrandom = CBitcoin::CBHD->generate();

print "Print base58 encoded xprv value part\n";
print $root_devrandom->export_xprv();

Some recommendations on security:
  1. If you write the xprv value to disk, make sure to set umask to 077 or to use sysopen:
    sysopen(my $fh, '/tmp/xprv.txt', O_WRONLY | O_TRUNC | O_CREAT, , 0600)

The Children

Children of the root HD key form the branches of the HD Tree. So, image you have a child, named Alice, in real life that you want to give some Bitcoins to, but at the same time would like to still retain control of her money.

So, let's give Alice an $math$i=1$math$

use CBitcoin;
use CBitcoin::CBHD;

my $root = CBitcoin::CBHD->generate();

my $alice_id = 1;
my $alice_hd = $root->deriveChild(1,$alice_id);

print "Alice's xprv=".$alice_hd->export_xprv()."\n";

  • For deriveChild , the first argument should by default, be 1. This signifies that the child HD key will be a a hardened key . At this time, it is not necessary to know the significance of that statement, other than 1 is a safer choice than 0.
  • Give Alice's xprv to Alice. It is secret, so do not share it with anyone except for Alice.
  • Alice can receive and spend bitcoins using her xprv as her root and you can spend her bitcoins since $math$ root_{1}^{1} \rightarrow alice$math$
  • For Bob, just do the samething except give him $math$id=2$math$ so that $math$root_{2}^{1} \rightarrow bob$math$

Customizing the Tree

.....

Receiving Money

Load up your root.

use CBitcoin;
use CBitcoin::CBHD;

my ($buf,$xprv);
open(my $fh,'<','/tmp/xprv.txt') || die "cannot open xprv file";
while(sysread($fh,$buf,8192)){  $xprv .= $_ }
close($fh);

my $root = CBitcoin::CBHD->new($xprv);

Easy

The easy way to receive money is to first print an address.

print "Address:".$root->address()."\n";

An address looks like 17ce3nnsFoLdFNpLok5qyu2sBwYtxHUXLB. A transaction sending money to that address can be found here. Please notice that the address starts with a "1". This indicates that it is an address on the Main network and is an address of simple type, ie only one person can spend the money in the address.

Paranoid

Like the 2 key simultaneous switch to launch nuclear missiles, perhaps you would feel more comfortable if 2 or more people had to agree to spend money before any money could be moved. Let Alice and Bob be in a situation in which they both have to agree whether or not to spend money. By the way, we will make a multisig address.

my @public_keys;

my $alicehd = $root->deriveChild(1,1);
push(@public_keys,$alicehd->publickey);

my $bobhd = $root->deriveChild(1,2);
push(@public_keys,$bobhd->publickey);

my $total_number_of_pubkeys = scalar(@public_keys);

my $number_of_pubkeys_needed_to_spend = scalar(@public_keys); # everyone must agree

my $multisig_script = CBitcoin::Script::multisig_p2sh_script(
      $total_number_of_pubkeys
      ,$total_number_of_pubkeys
      # for convinience reasons, sortings allows you to recreate the multisig address without knowing the order 
     @public_keys
);

my $multsig_address = CBitcoin::Script::script_to_address($multisig_script);

print "Multisig Address:$multisig_address\n";
  • An example address looks like 3DS7Y6bdePdnFCoXqddkevovh4s5M8NhgM. The 3 indicates that it is a p2sh type address on the Main Network

Spend Money

Spending money means 3 things:
  1. Download transaction data from the Bitcoin P2P network
  2. Digitally sign a message saying "Alice would like to send 1.3 bitcoins to 3DS7Y6bdePdnFCoXqddkevovh4s5M8NhgM".
  3. Broadcast the digitally signed message to as many peers on the Bitcoin P2P network as you can

Easy

        # make sure that we operate on the testnet
        $CBitcoin::network_bytes = CBitcoin::TESTNET;


   # got these from a block explorer, but we have to reverse the bytes
   my @hashes = (
      '6105e342232a9e67e4fa4ee0651eb8efd146dc0d7d346c788f45d8ad591c4577',
      'da16a3ea5101e9f2ff975ec67a4ad85767dd306c27b94ef52500c26bc88af5c9'
   );
   
   @ins = (
      # address=mwUaFw3zQ8M4iaeuhFiiGWy4QbTphAeSxh 0.01394
      CBitcoin::TransactionInput->new({
         'prevOutHash' => pack('H*',join('',reverse($hashes[0] =~ m/([[:xdigit:]]{2})/g) )  ) #should be 32 byte hash
         ,'prevOutIndex' => 1
         ,'script' =>  CBitcoin::Script::address_to_script($root->deriveChild(1,1)->address()) # scriptPubKey
      }),
      # address=ms2Kt13CEL5jTMs98qXMAD15WpmnsK5ZGg 0.01408
      CBitcoin::TransactionInput->new({
         'prevOutHash' => pack('H*',join('',reverse($hashes[1] =~ m/([[:xdigit:]]{2})/g) ) ) #should be 32 byte hash
         ,'prevOutIndex' => 1
         ,'script' =>  CBitcoin::Script::address_to_script($root->deriveChild(1,2)->address()) # scriptPubKey
      })   
   );
   my $balance = int( (0.01394 + 0.01408) * 100_000_000);
   my $fee = int(0.0001 * 100_000_000);
   
   @outs = (CBitcoin::TransactionOutput->new({
      'script' => CBitcoin::Script::address_to_script($root->deriveChild(1,3)->address())
      ,'value' => ($balance - $fee)
   }));
   
   # mi5W6CfThYwzTDsJg8Swu223dmyPPXDc8w
   # mi5W6CfThYwzTDsJg8Swu223dmyPPXDc8w
   
   my $tx = CBitcoin::Transaction->new({
      'inputs' => \@ins, 'outputs' => \@outs
   });
   
   my $txdata = $tx->assemble_p2pkh(0,$root->deriveChild(1,1));
   $txdata = $tx->assemble_p2pkh(1,$root->deriveChild(1,2),$txdata);

        print "Transaction Data in hex:".unpack('H*',$txdata)."\n";

  • This was a real transaction on the Test Network.
  • The transaction data can be broadcast via website. For more about broadcasting, just browse below.
  • Note: as of 2017-09-01, there is a new fork referred to as Bitcoin Cash (aka UAHF).  See the tests in the CBitcoin module to see how UAHF transactions work.

Multisig

As of 2017-10-15, there is an implementation in CBitcoin::Transaction.  See the tests on the uahf branch in order to get an understanding of how these work.  However, Joel has not figured out a practical way to implement this given the workflows people have surrounding multisig transactions. 

The following is an example work flow. Alice and Bob share a multisig balance.  Alice, who lives in London, creates a raw transaction and adds her signature.  Then Alice sends the raw transaction and her signature to Bob in Tokyo.  Then Bob has to add his signature and broadcast the transaction. 

Broadcasting

Options:
  1. Websites like blockchain.info and blockcypher.com. Just cut and paste the transaction hex from the script above into the html text box and then hit the send button.
  2. Use the JSON RPC mechanism of the bitcoind.
  3. You can try to use the Perl SPV client to connect to a full node and broadcast your transaction.

SPV

An SPV is basically a thin client that connects to the Bitcoin P2P network and downloads the consensus chain. Joel, the author of the SPV code, admits that this part of the library is still in flux. Figuring out how to download and figure out what the longest chain is is difficult. Input from other developers is more than welcome on this point. However, an SPV connected to a trusted node is capable of downloading the whole chain, given enough time.

With an SPV, one has the choice of either downloading headers only at 80 bytes a piece or downloading full blocks. A full node has to download complete blocks in order to do full verification. However, thin clients only need to download headers to determine the consensus chain. Sometimes, the thin client needs to download the whole block instead of just the headers if it is interested in transaction that change the balance of addresses of interest. To this end, there is a special subroutine in the CBitcoin::Block module called deserialize_filtered that lets you create a CBitcoin::BloomFilter and filter out only the transactions which add money to scripts/addresses you specify before hand.

As for the guts of the SPV, the author, Joel, recommends that you take a look at CBitcoin::DefaultEventLoop . At Joel's company, there is a preference of separating the event loop code from the SPV code as much as possible. That way, if you prefer to use AnyEvent or some other event loop framework, you just have to take a look at the DefaultEventLoop module and rewrite the subroutines. As for the SPV code itself, basically, it runs as a single process with no threading off of some event loop. To avoid the need to do threading while at the same time adding multiple processes to the mix, there are some facilities in the code to have multiple SPV processes communicate over mqueues. That code does not run at the moment, but the piping as been laid, so to speak.

Here is an example of how to start an SPV process inside of a perl script in a single thread/process:
use CBitcoin;
use CBitcoin::CLI::SPV;

CBitcoin::CLI::SPV::run_cli_args('spv',
   '--address=127.0.0.1:8333',
   '--node=gb5ypqt63du3wfhn.onion:8333',
   '--watch=1BhT26zK7g9hXb3PDkwenkxpBeGYa6MCK1',
   '--clientname="/BitcoinJ:0.2(iPad; U; CPU OS 3_2_1)/AndroidBuild:0.8/"'
);

  • In order for other peers to play ball (i.e. cooperate), you can choose the client name of a well known SPV client, BitcoinJ .
  • --watch - this specifies an address to which you would like to find corresponding transactions
  • --node - this adds potential nodes to which to connect to. As you can see in the example, you can supply onion addresses as well as plain vanilla network ipv4/ipv6 addresses. However, if you want to connect over tor, you need to run your script with torify .

To communicate with an SPV process, you can use the same CBitcoin::CLI::SPV code to send messages of an mqueue to an SPV process. The 07-spv.t file in the libcbitcoin-perl test suite contains an example of this. Here is what the code looks like:
CBitcoin::CLI::SPV::run_cli_args('cmd',
   '--node=q6m5jhenk33wm4j4.onion:8333','--node=l4xfmcziytzeehcz.onion:8333'
);
  • In this case, we are adding 2 more nodes to the peer database.

About JoelD

user-pic I do a lot of Bitcoin work in Perl, C, and XS. Please check out my Github profile for modules I have not yet managed to upload to CPAN.