The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Bitcoin::Crypto::Manual - Module overview

DESCRIPTION

This module allows you to perform low-level tasks for Bitcoin such as:

  • creating extended keys and utilizing bip32 key derivation

  • creating private key / public key pairs

  • address generation (in legacy, compatibility and segwit formats)

  • importing / exporting using popular mediums (WIF, mnemonic, hex)

  • building, serializing and running transaction scripts

  • serializing, signing and verifying transactions

This module won't help you with:

  • handling Bitcoin blocks and the blockchain

  • using any Bitcoin CLI tools / clients

  • connecting to Bitcoin network

WHERE TO START?

Documentation and examples in this module assume you're already familiar with the basics of Bitcoin protocol and asymmetric cryptography. If that's not the case, start with reading about those topics.

If you like to learn by example, dive right into the examples directory.

There are many goals which you may want to achieve with this module. Common topics include:

  • create a key pair for signature or address generation

            use Bitcoin::Crypto qw(btc_prv);
    
            # create a key pair from wif
    
            my $private = btc_prv->from_wif($wif_string);
            my $public = $private->get_public_key;

    Start with Bitcoin::Crypto::Key::Private if you already have some data you want to use as a private key entropy (like Bitcoin's WIF format or hex data). If you'd like to generate list of words (a mnemonic) instead, see "generate_mnemonic" in Bitcoin::Crypto::Util and "from_mnemonic" in Bitcoin::Crypto::Key::ExtPrivate.

  • generate many keys at once

            use Bitcoin::Crypto qw(btc_extprv);
            use Bitcoin::Crypto::Util qw(generate_mnemonic);
            use Bitcoin::Crypto::BIP85;
    
            # generate a mnemonic and derive more mnemonics from it deterministically
    
            my $mnemonic = generate_mnemonic;
            my $generator = Bitcoin::Crypto::BIP85->new(
                    key => btc_extprv->from_mnemonic($mnemonic),
            );
    
            for my $i (0 .. 9) {
                    my $new_mnemonic = $generator->derive_mnemonic(index => $i);
            }

    Bitcoin::Crypto::BIP85 lets you derive mnemonics, private keys or just entropy from a single extended private key.

    Bitcoin::Crypto::Key::ExtPrivate allows you to derive multiple keys from a master key, so you don't have to store multiple private keys. Bitcoin::Crypto::Key::ExtPublic can be then used to derive public keys lazily. (Note: storing extended public keys together with private keys in a hot storage will put your extended private key at risk!)

  • create a transaction object from scratch or from serialized transaction data

            use Bitcoin::Crypto qw(btc_transaction);
    
            # create and dump a transaction
    
            my $tx = btc_transaction->new;
    
            $tx->add_input(
                    utxo => [[hex => '9dd8d1ebba95d4ddc2b9fa21b9bc893385e9d5928d0d4835433f34b0ad4b9527'], 0],
            );
    
            $tx->add_output(
                    locking_script => [address => 'bc1p857ug9whvzapyhkxtjsha0376y2w2yc2dnegx4jlaqcftrfmnt0shkqj6h'],
                    value => 1337,
            );
    
            print $tx->dump;

    See Bitcoin::Crypto::Manual::Transactions.

  • utilize Bitcoin Script

            use Bitcoin::Crypto qw(btc_script);
    
            # create a script by hand and run it
    
            btc_script
                    ->new
                    ->push($bytestring)
                    ->add('OP_SIZE')
                    ->add('OP_10')
                    ->add('OP_EQUALVERIFY')
                    ->run;

    Bitcoin::Crypto::Script will help you build, de/serialize and run a script. Bitcoin::Crypto::Script::Runner gives you more control over script execution, including running the script step by step, stopping after each opcode.

  • work with Bitcoin-related encodings

            use Bitcoin::Crypto::Bech32 qw(encode_bech32);
    
            # encode some data as bech32
    
            my $encoded_str = encode_bech32('customdata', [25, 13, 31, 8, 0, 5]);

    There are Bitcoin::Crypto::Base58 and Bitcoin::Crypto::Bech32. PSBT format is supported in Bitcoin::Crypto::PSBT. You can validate an address and get its type using "get_address_type" in Bitcoin::Crypto::Util.

  • work with other cryptocurrencies

            use Bitcoin::Crypto::Network;
    
            # put module into single-network mode and choose testnet
    
            Bitcoin::Crypto::Network->get('bitcoin_testnet')->set_single;

    You can work with any cryptocurrency as long as it is based on the same fundamentals as Bitcoin. You have to register a network in Bitcoin::Crypto::Network first, with the protocol data valid for your cryptocurrency. Also see "Support for other cryptocurrencies"

GENERAL INFORMATION

How to read the documentation?

Most functions in this documentation have a code line showcasing the arguments used by the function. These lines are not meant to be valid perl. They're there for you to understand what arguments the function expects.

Most packages in this module have the types of their thrown exceptions documented near the bottom of the document. The exceptions section may be useful to understand which types of exceptions can be thrown when using functions or methods from the package and what they mean. It is not meant to be a full list of exceptions a function can throw and unblessed errors may still be raised.

Class shortcuts

The most frequently used classes have their shortcuts starting with btc_ registered in Bitcoin::Crypto. Examples are btc_prv for basic private key or btc_transaction for transactions. These shortcuts can make the code more compact and avoid manually requiring too many classes, but they are completely optional. If this style does not sound reasonable to you, feel free to ignore it.

See "Exported interface" in Bitcoin::Crypto for details.

How to pass data to functions?

Many frequently used functions (like from_serialized commonly used in keys, scripts and transactions) require you to pass in a string of bytes. Bytestring is a string in which each character has numeric value less than or equal to 255. It is the default way to pass in data to functions in Bitcoin::Crypto.

It is not rare that you may want to use some other format instead, like hex strings. To avoid duplicating functions for different formats or manually transforming data to a bytestring, the module uses data structures called format descriptions. In any place where a bytestring is used you may instead use an array reference with exactly two elements. The first element must be a name of the format, and the second element is the actual data in that format. For strings of hex data, this may look like this:

        use Bitcoin::Crypto qw(btc_prv);
        my $private = btc_prv->from_serialized([hex => '152a3f549597a2bef783']);

Currently supported values for the first argument are:

  • hex (hexadecimal string, may be prefixed by 0x)

  • base58 (base58-encoded string with the checksum)

  • base64 (base64-encoded string)

It is also common for functions to return bytestrings (like to_serialized). If you need help changing that output format you may use "to_format" in Bitcoin::Crypto::Util helper function, which does the reverse operation:

        use Bitcoin::Crypto::Util qw(to_format);
        print to_format [hex => $private->to_serialized];

How to pass commonly used script types to functions?

Similar to format descriptions, you may use a script description anywhere a script is expected. It is an array reference of two elements, where the first one is the short name of the script type, like P2WPKH. The second one contains data specific to that script type, usually the address:

        $transaction->add_output(
                locking_script => [P2WPKH => 'bc1qr9htu5sy02q6kv6mx7axz2zdg3k9nrh8pe4l47'],
        );

You may also leave address detection up to the module by using the string address:

        # the same thing
        $transaction->add_output(
                locking_script => [address => 'bc1qr9htu5sy02q6kv6mx7axz2zdg3k9nrh8pe4l47'],
        );

Note that the script created like this always belongs to the currently set default network (bitcoin by default). Passing address from the wrong network will result in an exception.

See "from_standard" in Bitcoin::Crypto::Script for more details.

Support for other cryptocurrencies

Bitcoin::Crypto has limited support for other cryptocurrency networks through Bitcoin::Crypto::Network. The network must be fully compatible with Bitcoin (a Bitcoin clone or a strict subset of Bitcoin features) - no extra cases will exist in the code, only constant values can differ between the networks. Lack of certain constant values can be used to mark feature unsupported in the given network, for example lack of segwit_hrp network constant will be interpreted as network not supporting Segregated Witness. Note that Bitcoin testnet is also considered a separate network.

Networks can be set globally through default network or on per-object basis for basic and extended keys, and scripts. Other parts of the system does not support per-object setting of networks and rely on the currently set default network. Setting keys for every object separately is considered an outdated feature which is kept for backwards compatibility (and convenience, sometimes). If you want to make sure all your keys are in the same, default network, see single-network mode in "single_network" in Bitcoin::Crypto::Network. This mode will disallow creation of objects with other network than the default. It can come handy if you are de-serializing keys from non-constant strings, for example fetched from the Internet or entered by the user.

DISCLAIMER

Although the module was written with an extra care and appropriate tests are in place asserting compatibility with many Bitcoin standards, due to complexity of the subject some bugs may still be present. In the world of digital money, a single bug may lead to losing funds. I encourage anyone to test the module themselves, review the test cases and use the module with care. Suggestions for improvements and more edge cases to test will be gladly accepted, but there is no warranty on funds manipulated by this module.

TODO

I will gladly accept help working on these: