psbt.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2022 The Limenka developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 import base64
7
8 from .messages import (
9 CTransaction,
10 deser_string,
11 from_binary,
12 ser_compact_size,
13 )
14
15
16 # global types
17 PSBT_GLOBAL_UNSIGNED_TX = 0x00
18 PSBT_GLOBAL_XPUB = 0x01
19 PSBT_GLOBAL_TX_VERSION = 0x02
20 PSBT_GLOBAL_FALLBACK_LOCKTIME = 0x03
21 PSBT_GLOBAL_INPUT_COUNT = 0x04
22 PSBT_GLOBAL_OUTPUT_COUNT = 0x05
23 PSBT_GLOBAL_TX_MODIFIABLE = 0x06
24 PSBT_GLOBAL_VERSION = 0xfb
25 PSBT_GLOBAL_PROPRIETARY = 0xfc
26
27 # per-input types
28 PSBT_IN_NON_WITNESS_UTXO = 0x00
29 PSBT_IN_WITNESS_UTXO = 0x01
30 PSBT_IN_PARTIAL_SIG = 0x02
31 PSBT_IN_SIGHASH_TYPE = 0x03
32 PSBT_IN_REDEEM_SCRIPT = 0x04
33 PSBT_IN_WITNESS_SCRIPT = 0x05
34 PSBT_IN_BIP32_DERIVATION = 0x06
35 PSBT_IN_FINAL_SCRIPTSIG = 0x07
36 PSBT_IN_FINAL_SCRIPTWITNESS = 0x08
37 PSBT_IN_POR_COMMITMENT = 0x09
38 PSBT_IN_RIPEMD160 = 0x0a
39 PSBT_IN_SHA256 = 0x0b
40 PSBT_IN_HASH160 = 0x0c
41 PSBT_IN_HASH256 = 0x0d
42 PSBT_IN_PREVIOUS_TXID = 0x0e
43 PSBT_IN_OUTPUT_INDEX = 0x0f
44 PSBT_IN_SEQUENCE = 0x10
45 PSBT_IN_REQUIRED_TIME_LOCKTIME = 0x11
46 PSBT_IN_REQUIRED_HEIGHT_LOCKTIME = 0x12
47 PSBT_IN_TAP_KEY_SIG = 0x13
48 PSBT_IN_TAP_SCRIPT_SIG = 0x14
49 PSBT_IN_TAP_LEAF_SCRIPT = 0x15
50 PSBT_IN_TAP_BIP32_DERIVATION = 0x16
51 PSBT_IN_TAP_INTERNAL_KEY = 0x17
52 PSBT_IN_TAP_MERKLE_ROOT = 0x18
53 PSBT_IN_PROPRIETARY = 0xfc
54
55 # per-output types
56 PSBT_OUT_REDEEM_SCRIPT = 0x00
57 PSBT_OUT_WITNESS_SCRIPT = 0x01
58 PSBT_OUT_BIP32_DERIVATION = 0x02
59 PSBT_OUT_AMOUNT = 0x03
60 PSBT_OUT_SCRIPT = 0x04
61 PSBT_OUT_TAP_INTERNAL_KEY = 0x05
62 PSBT_OUT_TAP_TREE = 0x06
63 PSBT_OUT_TAP_BIP32_DERIVATION = 0x07
64 PSBT_OUT_PROPRIETARY = 0xfc
65
66
67 class PSBTMap:
68 """Class for serializing and deserializing PSBT maps"""
69
70 def __init__(self, map=None):
71 self.map = map if map is not None else {}
72
73 def deserialize(self, f):
74 m = {}
75 while True:
76 k = deser_string(f)
77 if len(k) == 0:
78 break
79 v = deser_string(f)
80 if len(k) == 1:
81 k = k[0]
82 assert k not in m
83 m[k] = v
84 self.map = m
85
86 def serialize(self):
87 m = b""
88 for k,v in self.map.items():
89 if isinstance(k, int) and 0 <= k and k <= 255:
90 k = bytes([k])
91 m += ser_compact_size(len(k)) + k
92 m += ser_compact_size(len(v)) + v
93 m += b"\x00"
94 return m
95
96 class PSBT:
97 """Class for serializing and deserializing PSBTs"""
98
99 def __init__(self, *, g=None, i=None, o=None):
100 self.g = g if g is not None else PSBTMap()
101 self.i = i if i is not None else []
102 self.o = o if o is not None else []
103 self.tx = None
104
105 def deserialize(self, f):
106 assert f.read(5) == b"psbt\xff"
107 self.g = from_binary(PSBTMap, f)
108 assert PSBT_GLOBAL_UNSIGNED_TX in self.g.map
109 self.tx = from_binary(CTransaction, self.g.map[PSBT_GLOBAL_UNSIGNED_TX])
110 self.i = [from_binary(PSBTMap, f) for _ in self.tx.vin]
111 self.o = [from_binary(PSBTMap, f) for _ in self.tx.vout]
112 return self
113
114 def serialize(self):
115 assert isinstance(self.g, PSBTMap)
116 assert isinstance(self.i, list) and all(isinstance(x, PSBTMap) for x in self.i)
117 assert isinstance(self.o, list) and all(isinstance(x, PSBTMap) for x in self.o)
118 assert PSBT_GLOBAL_UNSIGNED_TX in self.g.map
119 tx = from_binary(CTransaction, self.g.map[PSBT_GLOBAL_UNSIGNED_TX])
120 assert len(tx.vin) == len(self.i)
121 assert len(tx.vout) == len(self.o)
122
123 psbt = [x.serialize() for x in [self.g] + self.i + self.o]
124 return b"psbt\xff" + b"".join(psbt)
125
126 def make_blank(self):
127 """
128 Remove all fields except for PSBT_GLOBAL_UNSIGNED_TX
129 """
130 for m in self.i + self.o:
131 m.map.clear()
132
133 self.g = PSBTMap(map={PSBT_GLOBAL_UNSIGNED_TX: self.g.map[PSBT_GLOBAL_UNSIGNED_TX]})
134
135 def to_base64(self):
136 return base64.b64encode(self.serialize()).decode("utf8")
137
138 @classmethod
139 def from_base64(cls, b64psbt):
140 return from_binary(cls, base64.b64decode(b64psbt))
141