# (c) Copyright 2020 by Coinkite Inc. This file is covered by license found in COPYING-CC. # # psbt.py - yet another PSBT parser/serializer but used only for test cases. # import io, struct from binascii import b2a_hex as _b2a_hex from binascii import a2b_hex as _a2b_hex from collections import namedtuple from base64 import b64encode from pycoin.tx.Tx import Tx from pycoin.tx.script.check_signature import parse_signature_blob from binascii import b2a_hex, a2b_hex from base64 import b64decode b2a_hex = lambda a: str(_b2a_hex(a), 'ascii') # BIP-174 aka PSBT defined values # PSBT_GLOBAL_UNSIGNED_TX = (0) PSBT_GLOBAL_XPUB = (1) PSBT_IN_NON_WITNESS_UTXO = (0) PSBT_IN_WITNESS_UTXO = (1) PSBT_IN_PARTIAL_SIG = (2) PSBT_IN_SIGHASH_TYPE = (3) PSBT_IN_REDEEM_SCRIPT = (4) PSBT_IN_WITNESS_SCRIPT = (5) PSBT_IN_BIP32_DERIVATION = (6) PSBT_IN_FINAL_SCRIPTSIG = (7) PSBT_IN_FINAL_SCRIPTWITNESS = (8) PSBT_OUT_REDEEM_SCRIPT = (0) PSBT_OUT_WITNESS_SCRIPT = (1) PSBT_OUT_BIP32_DERIVATION = (2) # Serialization/deserialization tools def ser_compact_size(l): r = b"" if l < 253: r = struct.pack("B", l) elif l < 0x10000: r = struct.pack(" val=(path) self.xpubs.append( (key, val) ) else: raise ValueError('unknown global key type: 0x%02x' % kt) assert self.txn, 'missing reqd section' self.inputs = [BasicPSBTInput(fd, idx) for idx in range(num_ins)] self.outputs = [BasicPSBTOutput(fd, idx) for idx in range(num_outs)] sep = fd.read(1) assert sep == b'' return self def serialize(self, fd): def wr(ktype, val, key=b''): fd.write(ser_compact_size(1 + len(key))) fd.write(bytes([ktype]) + key) fd.write(ser_compact_size(len(val))) fd.write(val) fd.write(b'psbt\xff') wr(PSBT_GLOBAL_UNSIGNED_TX, self.txn) for k,v in self.xpubs: wr(PSBT_GLOBAL_XPUB, v, key=k) # sep fd.write(b'\0') for idx, inp in enumerate(self.inputs): inp.serialize(fd, idx) for idx, outp in enumerate(self.outputs): outp.serialize(fd, idx) def as_bytes(self): with io.BytesIO() as fd: self.serialize(fd) return fd.getvalue() def test_my_psbt(): import glob, io for fn in glob.glob('data/*.psbt'): if 'missing_txn.psbt' in fn: continue if 'unknowns-ins.psbt' in fn: continue raw = open(fn, 'rb').read() print("\n\nFILE: %s" % fn) p = BasicPSBT().parse(raw) fd = io.BytesIO() p.serialize(fd) assert p.txn in fd.getvalue() chk = BasicPSBT().parse(fd.getvalue()) assert chk == p # EOF