Bitcoin Transaction: Java parsing library

KS implementation details

License: CC-BY-SA-4.0

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

Parse a local file and get structure in memory:

BitcoinTransaction data = BitcoinTransaction.fromFile("path/to/local/file.Bitcoin Transaction");

Or parse structure from a byte array:

byte[] someArray = new byte[] { ... };
BitcoinTransaction data = new BitcoinTransaction(new ByteBufferKaitaiStream(someArray));

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

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

Java source code to parse Bitcoin Transaction

BitcoinTransaction.java

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

import io.kaitai.struct.ByteBufferKaitaiStream;
import io.kaitai.struct.KaitaiStruct;
import io.kaitai.struct.KaitaiStream;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;


/**
 * @see <a href="https://bitcoin.org/en/developer-guide#transactions
 * https://en.bitcoin.it/wiki/Transaction
 * ">Source</a>
 */
public class BitcoinTransaction extends KaitaiStruct {
    public static BitcoinTransaction fromFile(String fileName) throws IOException {
        return new BitcoinTransaction(new ByteBufferKaitaiStream(fileName));
    }

    public enum SighashType {
        SIGHASH_ALL(1),
        SIGHASH_NONE(2),
        SIGHASH_SINGLE(3),
        SIGHASH_ANYONECANPAY(80);

        private final long id;
        SighashType(long id) { this.id = id; }
        public long id() { return id; }
        private static final Map<Long, SighashType> byId = new HashMap<Long, SighashType>(4);
        static {
            for (SighashType e : SighashType.values())
                byId.put(e.id(), e);
        }
        public static SighashType byId(long id) { return byId.get(id); }
    }

    public BitcoinTransaction(KaitaiStream _io) {
        this(_io, null, null);
    }

    public BitcoinTransaction(KaitaiStream _io, KaitaiStruct _parent) {
        this(_io, _parent, null);
    }

    public BitcoinTransaction(KaitaiStream _io, KaitaiStruct _parent, BitcoinTransaction _root) {
        super(_io);
        this._parent = _parent;
        this._root = _root == null ? this : _root;
        _read();
    }
    private void _read() {
        this.version = this._io.readU4le();
        this.numVins = this._io.readU1();
        vins = new ArrayList<Vin>((int) (numVins()));
        for (int i = 0; i < numVins(); i++) {
            this.vins.add(new Vin(this._io, this, _root));
        }
        this.numVouts = this._io.readU1();
        vouts = new ArrayList<Vout>((int) (numVouts()));
        for (int i = 0; i < numVouts(); i++) {
            this.vouts.add(new Vout(this._io, this, _root));
        }
        this.locktime = this._io.readU4le();
    }
    public static class Vout extends KaitaiStruct {
        public static Vout fromFile(String fileName) throws IOException {
            return new Vout(new ByteBufferKaitaiStream(fileName));
        }

        public Vout(KaitaiStream _io) {
            this(_io, null, null);
        }

        public Vout(KaitaiStream _io, BitcoinTransaction _parent) {
            this(_io, _parent, null);
        }

        public Vout(KaitaiStream _io, BitcoinTransaction _parent, BitcoinTransaction _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.amount = this._io.readU8le();
            this.scriptLen = this._io.readU1();
            this.scriptPubKey = this._io.readBytes(scriptLen());
        }
        private long amount;
        private int scriptLen;
        private byte[] scriptPubKey;
        private BitcoinTransaction _root;
        private BitcoinTransaction _parent;

        /**
         * Number of Satoshis to be transfered.
         */
        public long amount() { return amount; }

        /**
         * ScriptPubKey's length.
         */
        public int scriptLen() { return scriptLen; }

        /**
         * ScriptPubKey.
         * @see <a href="https://en.bitcoin.it/wiki/Transaction#Output
         * https://en.bitcoin.it/wiki/Script
         * ">Source</a>
         */
        public byte[] scriptPubKey() { return scriptPubKey; }
        public BitcoinTransaction _root() { return _root; }
        public BitcoinTransaction _parent() { return _parent; }
    }
    public static class PublicKey extends KaitaiStruct {
        public static PublicKey fromFile(String fileName) throws IOException {
            return new PublicKey(new ByteBufferKaitaiStream(fileName));
        }

        public PublicKey(KaitaiStream _io) {
            this(_io, null, null);
        }

        public PublicKey(KaitaiStream _io, BitcoinTransaction.ScriptSignature _parent) {
            this(_io, _parent, null);
        }

        public PublicKey(KaitaiStream _io, BitcoinTransaction.ScriptSignature _parent, BitcoinTransaction _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.type = this._io.readU1();
            this.x = this._io.readBytes(32);
            this.y = this._io.readBytes(32);
        }
        private int type;
        private byte[] x;
        private byte[] y;
        private BitcoinTransaction _root;
        private BitcoinTransaction.ScriptSignature _parent;
        public int type() { return type; }

        /**
         * 'x' coordinate of the public key on the elliptic curve.
         */
        public byte[] x() { return x; }

        /**
         * 'y' coordinate of the public key on the elliptic curve.
         */
        public byte[] y() { return y; }
        public BitcoinTransaction _root() { return _root; }
        public BitcoinTransaction.ScriptSignature _parent() { return _parent; }
    }
    public static class Vin extends KaitaiStruct {
        public static Vin fromFile(String fileName) throws IOException {
            return new Vin(new ByteBufferKaitaiStream(fileName));
        }

        public Vin(KaitaiStream _io) {
            this(_io, null, null);
        }

        public Vin(KaitaiStream _io, BitcoinTransaction _parent) {
            this(_io, _parent, null);
        }

        public Vin(KaitaiStream _io, BitcoinTransaction _parent, BitcoinTransaction _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.txid = this._io.readBytes(32);
            this.outputId = this._io.readU4le();
            this.scriptLen = this._io.readU1();
            this._raw_scriptSig = this._io.readBytes(scriptLen());
            KaitaiStream _io__raw_scriptSig = new ByteBufferKaitaiStream(_raw_scriptSig);
            this.scriptSig = new ScriptSignature(_io__raw_scriptSig, this, _root);
            this.endOfVin = this._io.ensureFixedContents(new byte[] { -1, -1, -1, -1 });
        }
        private byte[] txid;
        private long outputId;
        private int scriptLen;
        private ScriptSignature scriptSig;
        private byte[] endOfVin;
        private BitcoinTransaction _root;
        private BitcoinTransaction _parent;
        private byte[] _raw_scriptSig;

        /**
         * Previous transaction hash.
         */
        public byte[] txid() { return txid; }

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

        /**
         * ScriptSig's length.
         */
        public int scriptLen() { return scriptLen; }

        /**
         * ScriptSig.
         * @see <a href="https://en.bitcoin.it/wiki/Transaction#Input
         * https://en.bitcoin.it/wiki/Script
         * ">Source</a>
         */
        public ScriptSignature scriptSig() { return scriptSig; }

        /**
         * Magic number indicating the end of the current input.
         */
        public byte[] endOfVin() { return endOfVin; }
        public BitcoinTransaction _root() { return _root; }
        public BitcoinTransaction _parent() { return _parent; }
        public byte[] _raw_scriptSig() { return _raw_scriptSig; }
    }
    public static class ScriptSignature extends KaitaiStruct {
        public static ScriptSignature fromFile(String fileName) throws IOException {
            return new ScriptSignature(new ByteBufferKaitaiStream(fileName));
        }

        public ScriptSignature(KaitaiStream _io) {
            this(_io, null, null);
        }

        public ScriptSignature(KaitaiStream _io, BitcoinTransaction.Vin _parent) {
            this(_io, _parent, null);
        }

        public ScriptSignature(KaitaiStream _io, BitcoinTransaction.Vin _parent, BitcoinTransaction _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.sigStackLen = this._io.readU1();
            this.derSig = new DerSignature(this._io, this, _root);
            this.sigType = BitcoinTransaction.SighashType.byId(this._io.readU1());
            this.pubkeyStackLen = this._io.readU1();
            this.pubkey = new PublicKey(this._io, this, _root);
        }
        private int sigStackLen;
        private DerSignature derSig;
        private SighashType sigType;
        private int pubkeyStackLen;
        private PublicKey pubkey;
        private BitcoinTransaction _root;
        private BitcoinTransaction.Vin _parent;
        public int sigStackLen() { return sigStackLen; }

        /**
         * DER-encoded ECDSA signature.
         * @see <a href="https://en.wikipedia.org/wiki/X.690#DER_encoding
         * https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
         * ">Source</a>
         */
        public DerSignature derSig() { return derSig; }

        /**
         * Type of signature.
         */
        public SighashType sigType() { return sigType; }
        public int pubkeyStackLen() { return pubkeyStackLen; }

        /**
         * Public key (bitcoin address of the recipient).
         */
        public PublicKey pubkey() { return pubkey; }
        public BitcoinTransaction _root() { return _root; }
        public BitcoinTransaction.Vin _parent() { return _parent; }
    }
    public static class DerSignature extends KaitaiStruct {
        public static DerSignature fromFile(String fileName) throws IOException {
            return new DerSignature(new ByteBufferKaitaiStream(fileName));
        }

        public DerSignature(KaitaiStream _io) {
            this(_io, null, null);
        }

        public DerSignature(KaitaiStream _io, BitcoinTransaction.ScriptSignature _parent) {
            this(_io, _parent, null);
        }

        public DerSignature(KaitaiStream _io, BitcoinTransaction.ScriptSignature _parent, BitcoinTransaction _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
            _read();
        }
        private void _read() {
            this.sequence = this._io.ensureFixedContents(new byte[] { 48 });
            this.sigLen = this._io.readU1();
            this.sep1 = this._io.ensureFixedContents(new byte[] { 2 });
            this.sigRLen = this._io.readU1();
            this.sigR = this._io.readBytes(sigRLen());
            this.sep2 = this._io.ensureFixedContents(new byte[] { 2 });
            this.sigSLen = this._io.readU1();
            this.sigS = this._io.readBytes(sigSLen());
        }
        private byte[] sequence;
        private int sigLen;
        private byte[] sep1;
        private int sigRLen;
        private byte[] sigR;
        private byte[] sep2;
        private int sigSLen;
        private byte[] sigS;
        private BitcoinTransaction _root;
        private BitcoinTransaction.ScriptSignature _parent;
        public byte[] sequence() { return sequence; }
        public int sigLen() { return sigLen; }
        public byte[] sep1() { return sep1; }

        /**
         * 'r' value's length.
         */
        public int sigRLen() { return sigRLen; }

        /**
         * 'r' value of the ECDSA signature.
         * @see <a href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm">Source</a>
         */
        public byte[] sigR() { return sigR; }
        public byte[] sep2() { return sep2; }

        /**
         * 's' value's length.
         */
        public int sigSLen() { return sigSLen; }

        /**
         * 's' value of the ECDSA signature.
         * @see <a href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm">Source</a>
         */
        public byte[] sigS() { return sigS; }
        public BitcoinTransaction _root() { return _root; }
        public BitcoinTransaction.ScriptSignature _parent() { return _parent; }
    }
    private long version;
    private int numVins;
    private ArrayList<Vin> vins;
    private int numVouts;
    private ArrayList<Vout> vouts;
    private long locktime;
    private BitcoinTransaction _root;
    private KaitaiStruct _parent;

    /**
     * Version number.
     */
    public long version() { return version; }

    /**
     * Number of input transactions.
     */
    public int numVins() { return numVins; }

    /**
     * Input transactions.
     * An input refers to an output from a previous transaction.
     */
    public ArrayList<Vin> vins() { return vins; }

    /**
     * Number of output transactions.
     */
    public int numVouts() { return numVouts; }

    /**
     * Output transactions.
     */
    public ArrayList<Vout> vouts() { return vouts; }
    public long locktime() { return locktime; }
    public BitcoinTransaction _root() { return _root; }
    public KaitaiStruct _parent() { return _parent; }
}