# SPDX-FileCopyrightText: 2018 Coinkite, Inc. # SPDX-License-Identifier: MIT # # SPDX-FileCopyrightText: Copyright (c) 2010 ArtForz -- public domain half-a-node # SPDX-License-Identifier: MIT # # SPDX-FileCopyrightText: Copyright (c) 2012 Jeff Garzik # SPDX-License-Identifier: MIT # # SPDX-FileCopyrightText: Copyright (c) 2010-2016 The Bitcoin Core developers # SPDX-License-Identifier: MIT # # Additions Copyright 2018 by Coinkite Inc. # Copyright (c) 2010 ArtForz -- public domain half-a-node # Copyright (c) 2012 Jeff Garzik # Copyright (c) 2010-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Bitcoin Object Python Serializations Modified from the test/test_framework/mininode.py file from the Bitcoin repository CTransaction,CTxIn, CTxOut, etc....: data structures that should map to corresponding structures in bitcoin/primitives for transactions only ser_*, deser_*: functions that handle serialization/deserialization """ from uio import BytesIO from ubinascii import hexlify as b2a_hex from ubinascii import unhexlify as a2b_hex from ucollections import OrderedDict import ustruct as struct import trezorcrypto from opcodes import * from utils import bytes_to_hex_str def sha256(s): return trezorcrypto.sha256(s).digest() def ripemd160(s): return trezorcrypto.ripemd160(s).digest() def hash256(s): return sha256(sha256(s)) def hash160(s): return ripemd160(sha256(s)) SIGHASH_ALL = 1 SIGHASH_NONE = 2 SIGHASH_SINGLE = 3 SIGHASH_ANYONECANPAY = 0x80 # Serialization/deserialization tools def ser_compact_size(l): if l < 253: return struct.pack("B", l) elif l < 0x10000: return struct.pack(">= 32 return rs def uint256_from_str(s): r = 0 t = struct.unpack("> 24) & 0xFF v = (c & 0xFFFFFF) << (8 * (nbytes - 3)) return v def deser_vector(f, c): nit = deser_compact_size(f) r = [] for i in range(nit): t = c() t.deserialize(f) r.append(t) return r # ser_function_name: Allow for an alternate serialization function on the # entries in the vector (we use this for serializing the vector of transactions # for a witness block). def ser_vector(l, ser_function_name=None): r = ser_compact_size(len(l)) for i in l: if ser_function_name: r += getattr(i, ser_function_name)() else: r += i.serialize() return r def deser_uint256_vector(f): nit = deser_compact_size(f) r = [] for i in range(nit): t = deser_uint256(f) r.append(t) return r def ser_uint256_vector(l): r = ser_compact_size(len(l)) for i in l: r += ser_uint256(i) return r def deser_string_vector(f): nit = deser_compact_size(f) r = [] for i in range(nit): t = deser_string(f) r.append(t) return r def ser_string_vector(l): r = ser_compact_size(len(l)) for sv in l: r += ser_string(sv) return r def deser_int_vector(f): nit = deser_compact_size(f) r = [] for i in range(nit): t = struct.unpack(" OP_PUSHDATA1 + size + data def ser_push_int(n): # push a small integer onto the stack from opcodes import OP_0, OP_1, OP_16, OP_PUSHDATA1 if n == 0: return bytes([OP_0]) elif 1 <= n <= 16: return bytes([OP_1 + n - 1]) elif n <= 255: return bytes([1, n]) raise ValueError(n) def disassemble(script): # Very limited script disassembly # yeilds (int / bytes, opcode) for each part of the script # see try: offset = 0 while 1: if offset >= len(script): #print('dis %d done' % offset) return c = script[offset] offset += 1 if 1 <= c <= 75: #print('dis %d: bytes=%s' % (offset, b2a_hex(script[offset:offset+c]))) yield (script[offset:offset+c], None) offset += c elif OP_1 <= c <= OP_16: # OP_1 thru OP_16 #print('dis %d: number=%d' % (offset, (c - OP_1 + 1))) yield (c - OP_1 + 1, None) elif c == OP_PUSHDATA1: cnt = script[offset]; offset += 1 yield (script[offset:offset+cnt], None) offset += cnt elif c == OP_PUSHDATA2: cnt = struct.unpack_from("H", script, offset) offset += 2 yield (script[offset:offset+cnt], None) offset += cnt elif c == OP_PUSHDATA4: # no where to put so much data raise NotImplementedError elif c == OP_1NEGATE: yield (-1, None) else: # OP_0 included here #print('dis %d: opcode=%d' % (offset, c)) yield (None, c) except: raise ValueError("bad script") # Deserialize from a hex string representation (eg from RPC) def FromHex(obj, hex_string): obj.deserialize(BytesIO(hex_str_to_bytes(hex_string))) return obj # Convert a binary-serializable object to hex (eg for submission via RPC) def ToHex(obj): return bytes_to_hex_str(obj.serialize()) def ser_sig_der(r, s, sighash_type=1): sig = b"\x30" # Make r and s as short as possible ri = 0 for b in r: if b == 0: ri += 1 else: break r = r[ri:] si = 0 for b in s: if b == 0: si += 1 else: break s = s[si:] # Make positive of neg if r[0] & (1 << 7) != 0: r = b"\x00" + r if s[0] & (1 << 7) != 0: s = b"\x00" + s # Write total length total_len = len(r) + len(s) + 4 sig += struct.pack("B", total_len) # write r sig += b"\x02" sig += struct.pack("B", len(r)) sig += r # write s sig += b"\x02" sig += struct.pack("B", len(s)) sig += s sig += struct.pack("B", sighash_type) return sig def ser_sig_compact(r, s, recid): rec = struct.unpack("B", recid)[0] prefix = struct.pack("B", 27 + 4 +rec) sig = prefix sig += r + s return sig # Objects that map to bitcoind objects, which can be serialized/deserialized MSG_WITNESS_FLAG = 1<<30 class COutPoint(object): def __init__(self, hash=0, n=0): self.hash = hash self.n = n def deserialize(self, f): self.hash = deser_uint256(f) self.n = struct.unpack(" 21000000 * COIN: return False return True def __repr__(self): return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \ % (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime) # EOF