diff --git a/tools/deserialize_tx.py b/tools/deserialize_tx.py new file mode 100755 index 0000000000..7d91b36d4d --- /dev/null +++ b/tools/deserialize_tx.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +import binascii +import os +import sys + +try: + import construct as c +except ImportError: + sys.stderr.write("This tool requires Construct. Install it with 'pip install Construct'.\n") + sys.exit(1) + +from construct import this, len_ + +if os.isatty(sys.stdin.fileno()): + tx_hex = input("Enter transaction in hex format: ") +else: + tx_hex = sys.stdin.read().strip() + +tx_bin = binascii.unhexlify(tx_hex) + + +CompactUintStruct = c.Struct( + "base" / c.Int8ul, + "ext" / c.Switch(this.base, {0xfd: c.Int16ul, 0xfe: c.Int32ul, 0xff: c.Int64ul}), +) + + +class CompactUintAdapter(c.Adapter): + def _encode(self, obj, context, path): + if obj < 0xfd: + return {"base": obj} + if obj < 2 ** 16: + return {"base": 0xfd, "ext": obj} + if obj < 2 ** 32: + return {"base": 0xfe, "ext": obj} + if obj < 2 ** 64: + return {"base": 0xff, "ext": obj} + raise ValueError("Value too big for compact uint") + + def _decode(self, obj, context, path): + return obj["ext"] or obj["base"] + + +class ConstFlag(c.Adapter): + def __init__(self, const): + self.const = const + super().__init__(c.Optional(c.Const(const))) + + def _encode(self, obj, context, path): + return self.const if obj else None + + def _decode(self, obj, context, path): + return obj is not None + + +CompactUint = CompactUintAdapter(CompactUintStruct) + +TxInput = c.Struct( + "tx" / c.Bytes(32), + "index" / c.Int32ul, + # TODO coinbase tx + "script" / c.Prefixed(CompactUint, c.GreedyBytes), + "sequence" / c.Int32ul, +) + +TxOutput = c.Struct( + "value" / c.Int64ul, + "pk_script" / c.Prefixed(CompactUint, c.GreedyBytes), +) + +StackItem = c.Prefixed(CompactUint, c.GreedyBytes) +TxInputWitness = c.PrefixedArray(CompactUint, StackItem) + +Transaction = c.Struct( + "version" / c.Int32ul, + "segwit" / ConstFlag(b"\x00\x01"), + "inputs" / c.PrefixedArray(CompactUint, TxInput), + "outputs" / c.PrefixedArray(CompactUint, TxOutput), + "witness" / c.If(this.segwit, TxInputWitness[len_(this.inputs)]), + "lock_time" / c.Int32ul, + c.Terminated, +) + +print(Transaction.parse(tx_bin))