Skip to content

Commit eb7a85f

Browse files
committed
Add an aggregated script pubkey builder
1 parent 382f2b0 commit eb7a85f

File tree

2 files changed

+105
-54
lines changed

2 files changed

+105
-54
lines changed

pabtc/core.py

Lines changed: 102 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,100 @@ def sec_decode(cls, data: bytearray) -> PubKey:
189189
y = -y % pabtc.secp256k1.P
190190
return PubKey(x, y)
191191

192+
193+
class ScriptPubKey:
194+
# The Script pubkey is the locking code for an output. It's made up of script, which is a mini programming language
195+
# that allows you to place different types of locks on your outputs.
196+
197+
@classmethod
198+
def p2pk(cls, pubkey: PubKey) -> bytearray:
199+
data = bytearray()
200+
data.extend(pabtc.opcode.op_pushdata(pubkey.sec()))
201+
data.append(pabtc.opcode.op_checksig)
202+
return data
203+
204+
@classmethod
205+
def p2pkh(cls, pubkey_hash: bytearray) -> bytearray:
206+
assert len(pubkey_hash) == 20
207+
data = bytearray()
208+
data.append(pabtc.opcode.op_dup)
209+
data.append(pabtc.opcode.op_hash160)
210+
data.extend(pabtc.opcode.op_pushdata(pubkey_hash))
211+
data.append(pabtc.opcode.op_equalverify)
212+
data.append(pabtc.opcode.op_checksig)
213+
return data
214+
215+
@classmethod
216+
def p2ms(cls, m: int, pubkey: typing.List[PubKey]) -> bytearray:
217+
data = bytearray()
218+
data.append(pabtc.opcode.op_n(m))
219+
for e in pubkey:
220+
data.extend(pabtc.opcode.op_pushdata(e.sec()))
221+
data.append(pabtc.opcode.op_n(len(pubkey)))
222+
data.append(pabtc.opcode.op_checkmultisig)
223+
return data
224+
225+
@classmethod
226+
def p2sh(cls, redeem_hash: bytearray) -> bytearray:
227+
assert len(redeem_hash) == 20
228+
data = bytearray()
229+
data.append(pabtc.opcode.op_hash160)
230+
data.extend(pabtc.opcode.op_pushdata(redeem_hash))
231+
data.append(pabtc.opcode.op_equal)
232+
return data
233+
234+
@classmethod
235+
def p2sh_p2ms(cls, m: int, pubkey: typing.List[PubKey]) -> bytearray:
236+
redeem = cls.p2ms(m, pubkey)
237+
redeem_hash = hash160(redeem)
238+
return cls.p2sh(redeem_hash)
239+
240+
@classmethod
241+
def p2sh_p2wpkh(cls, pubkey_hash: bytearray) -> bytearray:
242+
assert len(pubkey_hash) == 20
243+
redeem = cls.p2wpkh(pubkey_hash)
244+
redeem_hash = hash160(redeem)
245+
return cls.p2sh(redeem_hash)
246+
247+
@classmethod
248+
def p2sh_p2wsh(cls, redeem_hash: bytearray) -> bytearray:
249+
assert len(redeem_hash) == 32
250+
redeem = cls.p2wsh(redeem_hash)
251+
redeem_hash = hash160(redeem)
252+
return cls.p2sh(redeem_hash)
253+
254+
@classmethod
255+
def p2wpkh(cls, pubkey_hash: bytearray) -> bytearray:
256+
assert len(pubkey_hash) == 20
257+
data = bytearray()
258+
data.append(pabtc.opcode.op_0)
259+
data.extend(pabtc.opcode.op_pushdata(pubkey_hash))
260+
return data
261+
262+
@classmethod
263+
def p2wsh(cls, redeem_hash: bytearray) -> bytearray:
264+
# The script hash within a p2wsh is a single sha-256. It is not a double sha-256 hash (i.e. hash256) as is
265+
# commonly used everywhere else in bitcoin, nor is it the hash160 of a script like in p2sh.
266+
assert len(redeem_hash) == 32
267+
data = bytearray()
268+
data.append(pabtc.opcode.op_0)
269+
data.extend(pabtc.opcode.op_pushdata(redeem_hash))
270+
return data
271+
272+
@classmethod
273+
def p2wsh_p2ms(cls, m: int, pubkey: typing.List[PubKey]) -> bytearray:
274+
redeem = cls.p2ms(m, pubkey)
275+
redeem_hash = hashwsh(redeem)
276+
return cls.p2wsh(redeem_hash)
277+
278+
@classmethod
279+
def p2tr(cls, root: bytearray) -> bytearray:
280+
assert len(root) == 32
281+
data = bytearray()
282+
data.append(pabtc.opcode.op_1)
283+
data.extend(pabtc.opcode.op_pushdata(root))
284+
return data
285+
192286
# Bitcoin address prefix: https://en.bitcoin.it/wiki/List_of_address_prefixes
193287

194288

@@ -210,22 +304,15 @@ def address_p2sh(redeem: bytearray) -> str:
210304

211305

212306
def address_p2sh_p2ms(n: int, pubkey: typing.List[PubKey]) -> str:
213-
redeem_script = []
214-
redeem_script.append(pabtc.opcode.op_n(n))
215-
for e in pubkey:
216-
redeem_script.append(pabtc.opcode.op_pushdata(e.sec()))
217-
redeem_script.append(pabtc.opcode.op_n(len(pubkey)))
218-
redeem_script.append(pabtc.opcode.op_checkmultisig)
219-
redeem_script = script(redeem_script)
220-
return address_p2sh(redeem_script)
307+
redeem = ScriptPubKey.p2ms(n, pubkey)
308+
return address_p2sh(redeem)
221309

222310

223311
def address_p2sh_p2wpkh(pubkey: PubKey) -> str:
224312
# Nested Segwit.
225313
# See https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
226-
pubkey_hash = hash160(pubkey.sec())
227-
redeem_script = script([pabtc.opcode.op_0, pabtc.opcode.op_pushdata(pubkey_hash)])
228-
return address_p2sh(redeem_script)
314+
redeem = ScriptPubKey.p2wpkh(pubkey.hash())
315+
return address_p2sh(redeem)
229316

230317

231318
def address_p2wpkh(pubkey: PubKey) -> str:
@@ -737,41 +824,25 @@ def script_pubkey_p2pkh(addr: str) -> bytearray:
737824
assert data[0] == pabtc.config.current.prefix.base58.p2pkh
738825
hash = data[0x01:0x15]
739826
assert pabtc.core.hash256(data[0x00:0x15])[:4] == data[0x15:0x19]
740-
return script([
741-
pabtc.opcode.op_dup,
742-
pabtc.opcode.op_hash160,
743-
pabtc.opcode.op_pushdata(hash),
744-
pabtc.opcode.op_equalverify,
745-
pabtc.opcode.op_checksig,
746-
])
827+
return ScriptPubKey.p2pkh(hash)
747828

748829

749830
def script_pubkey_p2sh(addr: str) -> bytearray:
750831
data = pabtc.base58.decode(addr)
751832
assert data[0] == pabtc.config.current.prefix.base58.p2sh
752833
hash = data[0x01:0x15]
753834
assert pabtc.core.hash256(data[0x00:0x15])[:4] == data[0x15:0x19]
754-
return script([
755-
pabtc.opcode.op_hash160,
756-
pabtc.opcode.op_pushdata(hash),
757-
pabtc.opcode.op_equal,
758-
])
835+
return ScriptPubKey.p2sh(hash)
759836

760837

761838
def script_pubkey_p2wpkh(addr: str) -> bytearray:
762839
hash = pabtc.bech32.decode_segwit_addr(pabtc.config.current.prefix.bech32, 0, addr)
763-
return script([
764-
pabtc.opcode.op_0,
765-
pabtc.opcode.op_pushdata(hash),
766-
])
840+
return ScriptPubKey.p2wpkh(hash)
767841

768842

769843
def script_pubkey_p2tr(addr: str) -> bytearray:
770844
pubx = pabtc.bech32.decode_segwit_addr(pabtc.config.current.prefix.bech32, 1, addr)
771-
return script([
772-
pabtc.opcode.op_1,
773-
pabtc.opcode.op_pushdata(pubx),
774-
])
845+
return ScriptPubKey.p2tr(pubx)
775846

776847

777848
def script_pubkey(addr: str) -> bytearray:

pabtc/wallet.py

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,7 @@ class Tp2shp2ms:
147147
def __init__(self, pubkey: typing.List[pabtc.core.PubKey], prikey: typing.List[int]) -> None:
148148
self.prikey = [pabtc.core.PriKey(e) for e in prikey]
149149
self.pubkey = pubkey
150-
script_asts = []
151-
script_asts.append(pabtc.opcode.op_n(len(prikey)))
152-
for e in self.pubkey:
153-
script_asts.append(pabtc.opcode.op_pushdata(e.sec()))
154-
script_asts.append(pabtc.opcode.op_n(len(pubkey)))
155-
script_asts.append(pabtc.opcode.op_checkmultisig)
156-
self.redeem = pabtc.core.script(script_asts)
150+
self.redeem = pabtc.core.ScriptPubKey.p2ms(len(prikey), pubkey)
157151
self.addr = pabtc.core.address_p2sh(self.redeem)
158152
self.script = pabtc.core.script_pubkey_p2sh(self.addr)
159153

@@ -209,14 +203,7 @@ def json(self) -> typing.Dict:
209203
def sign(self, tx: pabtc.core.Transaction) -> None:
210204
# See: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wpkh-nested-in-bip16-p2sh
211205
pubkey_hash = pabtc.core.hash160(self.pubkey.sec())
212-
script_code = pabtc.core.script([
213-
pabtc.opcode.op_pushdata(pabtc.core.script([
214-
pabtc.opcode.op_dup,
215-
pabtc.opcode.op_hash160,
216-
pabtc.opcode.op_pushdata(pubkey_hash),
217-
pabtc.opcode.op_equalverify,
218-
pabtc.opcode.op_checksig,
219-
]))])
206+
script_code = pabtc.opcode.op_pushdata(pabtc.core.ScriptPubKey.p2pkh(pubkey_hash))
220207
script_sig = pabtc.core.script([pabtc.opcode.op_pushdata(pabtc.core.script([
221208
pabtc.opcode.op_0,
222209
pabtc.opcode.op_pushdata(pubkey_hash)
@@ -253,14 +240,7 @@ def json(self) -> typing.Dict:
253240
def sign(self, tx: pabtc.core.Transaction) -> None:
254241
# See: https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wpkh
255242
pubkey_hash = pabtc.core.hash160(self.pubkey.sec())
256-
script_code = pabtc.core.script([
257-
pabtc.opcode.op_pushdata(pabtc.core.script([
258-
pabtc.opcode.op_dup,
259-
pabtc.opcode.op_hash160,
260-
pabtc.opcode.op_pushdata(pubkey_hash),
261-
pabtc.opcode.op_equalverify,
262-
pabtc.opcode.op_checksig,
263-
]))])
243+
script_code = pabtc.opcode.op_pushdata(pabtc.core.ScriptPubKey.p2pkh(pubkey_hash))
264244
for i, e in enumerate(tx.vin):
265245
s = self.prikey.sign_ecdsa_der(tx.digest_segwit_v0(i, pabtc.core.sighash_all, script_code))
266246
s.append(pabtc.core.sighash_all)

0 commit comments

Comments
 (0)