Bitcoin Transaction: C++98/STL parsing library

KS implementation details

License: MIT

This page hosts a formal specification of Bitcoin Transaction using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for C++98/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.bin", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    bitcoin_transaction_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.version() // => Version number.

C++98/STL source code to parse Bitcoin Transaction

bitcoin_transaction.h

#ifndef BITCOIN_TRANSACTION_H_
#define BITCOIN_TRANSACTION_H_

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <vector>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif

/**
 * \sa https://bitcoin.org/en/developer-guide#transactions
 * https://en.bitcoin.it/wiki/Transaction
 *  Source
 */

class bitcoin_transaction_t : public kaitai::kstruct {

public:
    class vin_t;
    class vout_t;

    bitcoin_transaction_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, bitcoin_transaction_t* p__root = 0);

private:
    void _read();
    void _clean_up();

public:
    ~bitcoin_transaction_t();

    class vin_t : public kaitai::kstruct {

    public:
        class script_signature_t;

        vin_t(kaitai::kstream* p__io, bitcoin_transaction_t* p__parent = 0, bitcoin_transaction_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~vin_t();

        class script_signature_t : public kaitai::kstruct {

        public:
            class der_signature_t;
            class public_key_t;

            enum sighash_type_t {
                SIGHASH_TYPE_SIGHASH_ALL = 1,
                SIGHASH_TYPE_SIGHASH_NONE = 2,
                SIGHASH_TYPE_SIGHASH_SINGLE = 3,
                SIGHASH_TYPE_SIGHASH_ANYONECANPAY = 80
            };

            script_signature_t(kaitai::kstream* p__io, bitcoin_transaction_t::vin_t* p__parent = 0, bitcoin_transaction_t* p__root = 0);

        private:
            void _read();
            void _clean_up();

        public:
            ~script_signature_t();

            class der_signature_t : public kaitai::kstruct {

            public:

                der_signature_t(kaitai::kstream* p__io, bitcoin_transaction_t::vin_t::script_signature_t* p__parent = 0, bitcoin_transaction_t* p__root = 0);

            private:
                void _read();
                void _clean_up();

            public:
                ~der_signature_t();

            private:
                std::string m_sequence;
                uint8_t m_len_sig;
                std::string m_sep_1;
                uint8_t m_len_sig_r;
                std::string m_sig_r;
                std::string m_sep_2;
                uint8_t m_len_sig_s;
                std::string m_sig_s;
                bitcoin_transaction_t* m__root;
                bitcoin_transaction_t::vin_t::script_signature_t* m__parent;

            public:
                std::string sequence() const { return m_sequence; }
                uint8_t len_sig() const { return m_len_sig; }
                std::string sep_1() const { return m_sep_1; }

                /**
                 * 'r' value's length.
                 */
                uint8_t len_sig_r() const { return m_len_sig_r; }

                /**
                 * 'r' value of the ECDSA signature.
                 * \sa https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm Source
                 */
                std::string sig_r() const { return m_sig_r; }
                std::string sep_2() const { return m_sep_2; }

                /**
                 * 's' value's length.
                 */
                uint8_t len_sig_s() const { return m_len_sig_s; }

                /**
                 * 's' value of the ECDSA signature.
                 * \sa https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm Source
                 */
                std::string sig_s() const { return m_sig_s; }
                bitcoin_transaction_t* _root() const { return m__root; }
                bitcoin_transaction_t::vin_t::script_signature_t* _parent() const { return m__parent; }
            };

            class public_key_t : public kaitai::kstruct {

            public:

                public_key_t(kaitai::kstream* p__io, bitcoin_transaction_t::vin_t::script_signature_t* p__parent = 0, bitcoin_transaction_t* p__root = 0);

            private:
                void _read();
                void _clean_up();

            public:
                ~public_key_t();

            private:
                uint8_t m_type;
                std::string m_x;
                std::string m_y;
                bitcoin_transaction_t* m__root;
                bitcoin_transaction_t::vin_t::script_signature_t* m__parent;

            public:
                uint8_t type() const { return m_type; }

                /**
                 * 'x' coordinate of the public key on the elliptic curve.
                 */
                std::string x() const { return m_x; }

                /**
                 * 'y' coordinate of the public key on the elliptic curve.
                 */
                std::string y() const { return m_y; }
                bitcoin_transaction_t* _root() const { return m__root; }
                bitcoin_transaction_t::vin_t::script_signature_t* _parent() const { return m__parent; }
            };

        private:
            uint8_t m_len_sig_stack;
            der_signature_t* m_der_sig;
            sighash_type_t m_sig_type;
            uint8_t m_len_pubkey_stack;
            public_key_t* m_pubkey;
            bitcoin_transaction_t* m__root;
            bitcoin_transaction_t::vin_t* m__parent;

        public:
            uint8_t len_sig_stack() const { return m_len_sig_stack; }

            /**
             * DER-encoded ECDSA signature.
             * \sa https://en.wikipedia.org/wiki/X.690#DER_encoding
             * https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
             *  Source
             */
            der_signature_t* der_sig() const { return m_der_sig; }

            /**
             * Type of signature.
             */
            sighash_type_t sig_type() const { return m_sig_type; }
            uint8_t len_pubkey_stack() const { return m_len_pubkey_stack; }

            /**
             * Public key (bitcoin address of the recipient).
             */
            public_key_t* pubkey() const { return m_pubkey; }
            bitcoin_transaction_t* _root() const { return m__root; }
            bitcoin_transaction_t::vin_t* _parent() const { return m__parent; }
        };

    private:
        std::string m_txid;
        uint32_t m_output_id;
        uint8_t m_len_script;
        script_signature_t* m_script_sig;
        std::string m_end_of_vin;
        bitcoin_transaction_t* m__root;
        bitcoin_transaction_t* m__parent;
        std::string m__raw_script_sig;
        kaitai::kstream* m__io__raw_script_sig;

    public:

        /**
         * Previous transaction hash.
         */
        std::string txid() const { return m_txid; }

        /**
         * ID indexing an ouput of the transaction refered by txid.
         * This output will be used as an input in the present transaction.
         */
        uint32_t output_id() const { return m_output_id; }

        /**
         * ScriptSig's length.
         */
        uint8_t len_script() const { return m_len_script; }

        /**
         * ScriptSig.
         * \sa https://en.bitcoin.it/wiki/Transaction#Input
         * https://en.bitcoin.it/wiki/Script
         *  Source
         */
        script_signature_t* script_sig() const { return m_script_sig; }

        /**
         * Magic number indicating the end of the current input.
         */
        std::string end_of_vin() const { return m_end_of_vin; }
        bitcoin_transaction_t* _root() const { return m__root; }
        bitcoin_transaction_t* _parent() const { return m__parent; }
        std::string _raw_script_sig() const { return m__raw_script_sig; }
        kaitai::kstream* _io__raw_script_sig() const { return m__io__raw_script_sig; }
    };

    class vout_t : public kaitai::kstruct {

    public:

        vout_t(kaitai::kstream* p__io, bitcoin_transaction_t* p__parent = 0, bitcoin_transaction_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~vout_t();

    private:
        uint64_t m_amount;
        uint8_t m_len_script;
        std::string m_script_pub_key;
        bitcoin_transaction_t* m__root;
        bitcoin_transaction_t* m__parent;

    public:

        /**
         * Number of Satoshis to be transfered.
         */
        uint64_t amount() const { return m_amount; }

        /**
         * ScriptPubKey's length.
         */
        uint8_t len_script() const { return m_len_script; }

        /**
         * ScriptPubKey.
         * \sa https://en.bitcoin.it/wiki/Transaction#Output
         * https://en.bitcoin.it/wiki/Script
         *  Source
         */
        std::string script_pub_key() const { return m_script_pub_key; }
        bitcoin_transaction_t* _root() const { return m__root; }
        bitcoin_transaction_t* _parent() const { return m__parent; }
    };

private:
    uint32_t m_version;
    uint8_t m_num_vins;
    std::vector<vin_t*>* m_vins;
    uint8_t m_num_vouts;
    std::vector<vout_t*>* m_vouts;
    uint32_t m_locktime;
    bitcoin_transaction_t* m__root;
    kaitai::kstruct* m__parent;

public:

    /**
     * Version number.
     */
    uint32_t version() const { return m_version; }

    /**
     * Number of input transactions.
     */
    uint8_t num_vins() const { return m_num_vins; }

    /**
     * Input transactions.
     * An input refers to an output from a previous transaction.
     */
    std::vector<vin_t*>* vins() const { return m_vins; }

    /**
     * Number of output transactions.
     */
    uint8_t num_vouts() const { return m_num_vouts; }

    /**
     * Output transactions.
     */
    std::vector<vout_t*>* vouts() const { return m_vouts; }
    uint32_t locktime() const { return m_locktime; }
    bitcoin_transaction_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

#endif  // BITCOIN_TRANSACTION_H_

bitcoin_transaction.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "bitcoin_transaction.h"
#include "kaitai/exceptions.h"

bitcoin_transaction_t::bitcoin_transaction_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, bitcoin_transaction_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_vins = 0;
    m_vouts = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void bitcoin_transaction_t::_read() {
    m_version = m__io->read_u4le();
    m_num_vins = m__io->read_u1();
    m_vins = new std::vector<vin_t*>();
    const int l_vins = num_vins();
    for (int i = 0; i < l_vins; i++) {
        m_vins->push_back(new vin_t(m__io, this, m__root));
    }
    m_num_vouts = m__io->read_u1();
    m_vouts = new std::vector<vout_t*>();
    const int l_vouts = num_vouts();
    for (int i = 0; i < l_vouts; i++) {
        m_vouts->push_back(new vout_t(m__io, this, m__root));
    }
    m_locktime = m__io->read_u4le();
}

bitcoin_transaction_t::~bitcoin_transaction_t() {
    _clean_up();
}

void bitcoin_transaction_t::_clean_up() {
    if (m_vins) {
        for (std::vector<vin_t*>::iterator it = m_vins->begin(); it != m_vins->end(); ++it) {
            delete *it;
        }
        delete m_vins; m_vins = 0;
    }
    if (m_vouts) {
        for (std::vector<vout_t*>::iterator it = m_vouts->begin(); it != m_vouts->end(); ++it) {
            delete *it;
        }
        delete m_vouts; m_vouts = 0;
    }
}

bitcoin_transaction_t::vin_t::vin_t(kaitai::kstream* p__io, bitcoin_transaction_t* p__parent, bitcoin_transaction_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_script_sig = 0;
    m__io__raw_script_sig = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void bitcoin_transaction_t::vin_t::_read() {
    m_txid = m__io->read_bytes(32);
    m_output_id = m__io->read_u4le();
    m_len_script = m__io->read_u1();
    m__raw_script_sig = m__io->read_bytes(len_script());
    m__io__raw_script_sig = new kaitai::kstream(m__raw_script_sig);
    m_script_sig = new script_signature_t(m__io__raw_script_sig, this, m__root);
    m_end_of_vin = m__io->read_bytes(4);
    if (!(end_of_vin() == std::string("\xFF\xFF\xFF\xFF", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\xFF\xFF\xFF\xFF", 4), end_of_vin(), _io(), std::string("/types/vin/seq/4"));
    }
}

bitcoin_transaction_t::vin_t::~vin_t() {
    _clean_up();
}

void bitcoin_transaction_t::vin_t::_clean_up() {
    if (m__io__raw_script_sig) {
        delete m__io__raw_script_sig; m__io__raw_script_sig = 0;
    }
    if (m_script_sig) {
        delete m_script_sig; m_script_sig = 0;
    }
}

bitcoin_transaction_t::vin_t::script_signature_t::script_signature_t(kaitai::kstream* p__io, bitcoin_transaction_t::vin_t* p__parent, bitcoin_transaction_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_der_sig = 0;
    m_pubkey = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void bitcoin_transaction_t::vin_t::script_signature_t::_read() {
    m_len_sig_stack = m__io->read_u1();
    m_der_sig = new der_signature_t(m__io, this, m__root);
    m_sig_type = static_cast<bitcoin_transaction_t::vin_t::script_signature_t::sighash_type_t>(m__io->read_u1());
    m_len_pubkey_stack = m__io->read_u1();
    m_pubkey = new public_key_t(m__io, this, m__root);
}

bitcoin_transaction_t::vin_t::script_signature_t::~script_signature_t() {
    _clean_up();
}

void bitcoin_transaction_t::vin_t::script_signature_t::_clean_up() {
    if (m_der_sig) {
        delete m_der_sig; m_der_sig = 0;
    }
    if (m_pubkey) {
        delete m_pubkey; m_pubkey = 0;
    }
}

bitcoin_transaction_t::vin_t::script_signature_t::der_signature_t::der_signature_t(kaitai::kstream* p__io, bitcoin_transaction_t::vin_t::script_signature_t* p__parent, bitcoin_transaction_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void bitcoin_transaction_t::vin_t::script_signature_t::der_signature_t::_read() {
    m_sequence = m__io->read_bytes(1);
    if (!(sequence() == std::string("\x30", 1))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x30", 1), sequence(), _io(), std::string("/types/vin/types/script_signature/types/der_signature/seq/0"));
    }
    m_len_sig = m__io->read_u1();
    m_sep_1 = m__io->read_bytes(1);
    if (!(sep_1() == std::string("\x02", 1))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x02", 1), sep_1(), _io(), std::string("/types/vin/types/script_signature/types/der_signature/seq/2"));
    }
    m_len_sig_r = m__io->read_u1();
    m_sig_r = m__io->read_bytes(len_sig_r());
    m_sep_2 = m__io->read_bytes(1);
    if (!(sep_2() == std::string("\x02", 1))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x02", 1), sep_2(), _io(), std::string("/types/vin/types/script_signature/types/der_signature/seq/5"));
    }
    m_len_sig_s = m__io->read_u1();
    m_sig_s = m__io->read_bytes(len_sig_s());
}

bitcoin_transaction_t::vin_t::script_signature_t::der_signature_t::~der_signature_t() {
    _clean_up();
}

void bitcoin_transaction_t::vin_t::script_signature_t::der_signature_t::_clean_up() {
}

bitcoin_transaction_t::vin_t::script_signature_t::public_key_t::public_key_t(kaitai::kstream* p__io, bitcoin_transaction_t::vin_t::script_signature_t* p__parent, bitcoin_transaction_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void bitcoin_transaction_t::vin_t::script_signature_t::public_key_t::_read() {
    m_type = m__io->read_u1();
    m_x = m__io->read_bytes(32);
    m_y = m__io->read_bytes(32);
}

bitcoin_transaction_t::vin_t::script_signature_t::public_key_t::~public_key_t() {
    _clean_up();
}

void bitcoin_transaction_t::vin_t::script_signature_t::public_key_t::_clean_up() {
}

bitcoin_transaction_t::vout_t::vout_t(kaitai::kstream* p__io, bitcoin_transaction_t* p__parent, bitcoin_transaction_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void bitcoin_transaction_t::vout_t::_read() {
    m_amount = m__io->read_u8le();
    m_len_script = m__io->read_u1();
    m_script_pub_key = m__io->read_bytes(len_script());
}

bitcoin_transaction_t::vout_t::~vout_t() {
    _clean_up();
}

void bitcoin_transaction_t::vout_t::_clean_up() {
}