2018-02-24 20:42:54 +00:00
|
|
|
from trezor.crypto.hashlib import ripemd160, sha256
|
2018-06-05 18:21:31 +00:00
|
|
|
from trezor.messages.MultisigRedeemScriptType import MultisigRedeemScriptType
|
2018-02-12 08:19:38 +00:00
|
|
|
from apps.wallet.sign_tx.multisig import multisig_get_pubkeys
|
2018-06-05 18:21:31 +00:00
|
|
|
from apps.wallet.sign_tx.writers import bytearray_with_cap, write_bytes, write_varint, write_op_push
|
2017-11-10 13:10:19 +00:00
|
|
|
|
2018-02-12 08:47:32 +00:00
|
|
|
|
|
|
|
class ScriptsError(ValueError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
# P2PKH, P2SH
|
2017-11-10 13:10:19 +00:00
|
|
|
# ===
|
2018-02-24 20:42:54 +00:00
|
|
|
# https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
|
2017-11-10 13:10:19 +00:00
|
|
|
|
|
|
|
|
2018-02-08 17:59:51 +00:00
|
|
|
def input_script_p2pkh_or_p2sh(pubkey: bytes, signature: bytes, sighash: int) -> bytearray:
|
2017-11-10 13:10:19 +00:00
|
|
|
w = bytearray_with_cap(5 + len(signature) + 1 + 5 + len(pubkey))
|
2018-01-29 13:41:56 +00:00
|
|
|
append_signature(w, signature, sighash)
|
|
|
|
append_pubkey(w, pubkey)
|
2017-11-10 13:10:19 +00:00
|
|
|
return w
|
|
|
|
|
|
|
|
|
|
|
|
def output_script_p2pkh(pubkeyhash: bytes) -> bytearray:
|
|
|
|
s = bytearray(25)
|
|
|
|
s[0] = 0x76 # OP_DUP
|
|
|
|
s[1] = 0xA9 # OP_HASH_160
|
|
|
|
s[2] = 0x14 # pushing 20 bytes
|
|
|
|
s[3:23] = pubkeyhash
|
|
|
|
s[23] = 0x88 # OP_EQUALVERIFY
|
|
|
|
s[24] = 0xAC # OP_CHECKSIG
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
|
|
def output_script_p2sh(scripthash: bytes) -> bytearray:
|
2018-02-24 20:42:54 +00:00
|
|
|
# A9 14 <scripthash> 87
|
|
|
|
|
2017-11-10 13:10:19 +00:00
|
|
|
s = bytearray(23)
|
|
|
|
s[0] = 0xA9 # OP_HASH_160
|
|
|
|
s[1] = 0x14 # pushing 20 bytes
|
|
|
|
s[2:22] = scripthash
|
|
|
|
s[22] = 0x87 # OP_EQUAL
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
2018-07-02 12:16:26 +00:00
|
|
|
def script_replay_protection_bip115(block_hash: bytes, block_height: bytes) -> bytearray:
|
2018-07-04 10:58:44 +00:00
|
|
|
if block_hash is None or block_height is None:
|
|
|
|
return bytearray()
|
2018-07-02 12:29:58 +00:00
|
|
|
block_height_size = len(block_height) # size in bytes of block_height (should be 3)
|
2018-07-02 12:16:26 +00:00
|
|
|
s = bytearray(38)
|
|
|
|
s[0] = 0x20 # 32 bytes for block hash
|
|
|
|
s[1:33] = block_hash # block hash
|
|
|
|
s[33] = 0x03 # 3 bytes for block height
|
2018-07-02 12:29:58 +00:00
|
|
|
if block_height_size == 3:
|
|
|
|
s[34:37] = block_height
|
|
|
|
else:
|
|
|
|
s[34:37] = block_height[:3 - block_height_size] # must be only 3 bytes
|
2018-07-02 12:16:26 +00:00
|
|
|
s[37] = 0xB4 # OP_CHECKBLOCKHIGHT
|
|
|
|
return s
|
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
# SegWit: Native P2WPKH or P2WSH
|
|
|
|
# ===
|
|
|
|
# https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wpkh
|
|
|
|
# https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh
|
|
|
|
#
|
|
|
|
# P2WPKH (Pay-to-Witness-Public-Key-Hash) is the segwit native P2PKH.
|
|
|
|
# Not backwards compatible.
|
|
|
|
#
|
|
|
|
# P2WSH (Pay-to-Witness-Script-Hash) is segwit native P2SH.
|
|
|
|
# Not backwards compatible.
|
2017-11-10 13:10:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
def input_script_native_p2wpkh_or_p2wsh() -> bytearray:
|
2018-02-24 20:42:54 +00:00
|
|
|
# Completely replaced by the witness and therefore empty.
|
2017-11-10 13:10:19 +00:00
|
|
|
return bytearray(0)
|
|
|
|
|
|
|
|
|
2017-11-20 11:47:39 +00:00
|
|
|
def output_script_native_p2wpkh_or_p2wsh(witprog: bytes) -> bytearray:
|
2018-02-24 20:42:54 +00:00
|
|
|
# Either:
|
|
|
|
# 00 14 <20-byte-key-hash>
|
|
|
|
# 00 20 <32-byte-script-hash>
|
|
|
|
|
2017-11-20 11:47:39 +00:00
|
|
|
w = bytearray_with_cap(3 + len(witprog))
|
2017-11-10 13:10:19 +00:00
|
|
|
w.append(0x00) # witness version byte
|
2017-11-20 11:47:39 +00:00
|
|
|
w.append(len(witprog)) # pub key hash length is 20 (P2WPKH) or 32 (P2WSH) bytes
|
|
|
|
write_bytes(w, witprog) # pub key hash
|
2017-11-10 13:10:19 +00:00
|
|
|
return w
|
|
|
|
|
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
# SegWit: P2WPKH nested in P2SH
|
|
|
|
# ===
|
|
|
|
# https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program
|
|
|
|
#
|
|
|
|
# P2WPKH is nested in P2SH to be backwards compatible.
|
|
|
|
# Uses normal P2SH output scripts.
|
|
|
|
|
2017-11-10 13:10:19 +00:00
|
|
|
|
|
|
|
def input_script_p2wpkh_in_p2sh(pubkeyhash: bytes) -> bytearray:
|
2018-02-24 20:42:54 +00:00
|
|
|
# 16 00 14 <pubkeyhash>
|
|
|
|
# Signature is moved to the witness.
|
|
|
|
|
2017-11-10 13:10:19 +00:00
|
|
|
w = bytearray_with_cap(3 + len(pubkeyhash))
|
2018-02-12 08:19:38 +00:00
|
|
|
w.append(0x16) # length of the data
|
2017-11-10 13:10:19 +00:00
|
|
|
w.append(0x00) # witness version byte
|
|
|
|
w.append(0x14) # P2WPKH witness program (pub key hash length)
|
|
|
|
write_bytes(w, pubkeyhash) # pub key hash
|
|
|
|
return w
|
|
|
|
|
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
# SegWit: P2WSH nested in P2SH
|
|
|
|
# ===
|
|
|
|
# https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh-nested-in-bip16-p2sh
|
|
|
|
#
|
|
|
|
# P2WSH is nested in P2SH to be backwards compatible.
|
|
|
|
# Uses normal P2SH output scripts.
|
2018-02-12 08:19:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
def input_script_p2wsh_in_p2sh(script_hash: bytes) -> bytearray:
|
2018-02-24 20:42:54 +00:00
|
|
|
# 22 00 20 <redeem script hash>
|
|
|
|
# Signature is moved to the witness.
|
|
|
|
|
2018-02-12 08:19:38 +00:00
|
|
|
if len(script_hash) != 32:
|
2018-02-12 08:47:32 +00:00
|
|
|
raise ScriptsError('Redeem script hash should be 32 bytes long')
|
2018-02-12 08:19:38 +00:00
|
|
|
|
|
|
|
w = bytearray_with_cap(3 + len(script_hash))
|
|
|
|
w.append(0x22) # length of the data
|
|
|
|
w.append(0x00) # witness version byte
|
|
|
|
w.append(0x20) # P2WSH witness program (redeem script hash length)
|
|
|
|
write_bytes(w, script_hash)
|
|
|
|
return w
|
|
|
|
|
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
# SegWit: Witness getters
|
|
|
|
# ===
|
2018-02-12 08:19:38 +00:00
|
|
|
|
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
def witness_p2wpkh(signature: bytes, pubkey: bytes, sighash: int):
|
2018-02-12 08:19:38 +00:00
|
|
|
w = bytearray_with_cap(1 + 5 + len(signature) + 1 + 5 + len(pubkey))
|
|
|
|
write_varint(w, 0x02) # num of segwit items, in P2WPKH it's always 2
|
|
|
|
append_signature(w, signature, sighash)
|
|
|
|
append_pubkey(w, pubkey)
|
|
|
|
return w
|
|
|
|
|
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
def witness_p2wsh(multisig: MultisigRedeemScriptType, signature: bytes, signature_index: int, sighash: int):
|
2018-02-13 11:10:31 +00:00
|
|
|
signatures = multisig.signatures # other signatures
|
|
|
|
if len(signatures[signature_index]) > 0:
|
2018-02-24 20:42:54 +00:00
|
|
|
raise ScriptsError('Invalid multisig parameters')
|
2018-02-13 11:10:31 +00:00
|
|
|
signatures[signature_index] = signature # our signature
|
|
|
|
|
2018-02-12 08:19:38 +00:00
|
|
|
# filter empty
|
2018-02-13 11:10:31 +00:00
|
|
|
signatures = [s for s in multisig.signatures if len(s) > 0]
|
2018-02-12 08:19:38 +00:00
|
|
|
|
2018-02-13 11:10:31 +00:00
|
|
|
# witness program + signatures + redeem script
|
|
|
|
num_of_witness_items = 1 + len(signatures) + 1
|
2018-02-12 08:19:38 +00:00
|
|
|
|
|
|
|
w = bytearray()
|
|
|
|
write_varint(w, num_of_witness_items)
|
|
|
|
write_varint(w, 0) # version 0 witness program
|
|
|
|
|
2018-02-13 11:10:31 +00:00
|
|
|
for s in signatures:
|
2018-02-12 08:19:38 +00:00
|
|
|
append_signature(w, s, sighash) # size of the witness included
|
|
|
|
|
2018-02-24 17:18:29 +00:00
|
|
|
# redeem script
|
|
|
|
pubkeys = multisig_get_pubkeys(multisig)
|
2018-02-24 22:23:19 +00:00
|
|
|
redeem_script = output_script_multisig(pubkeys, multisig.m)
|
2018-02-12 08:19:38 +00:00
|
|
|
write_varint(w, len(redeem_script))
|
|
|
|
write_bytes(w, redeem_script)
|
|
|
|
return w
|
|
|
|
|
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
# Multisig
|
|
|
|
# ===
|
|
|
|
#
|
|
|
|
# Used either as P2SH, P2WSH, or P2WSH nested in P2SH.
|
|
|
|
|
2018-01-24 15:12:54 +00:00
|
|
|
|
2018-02-24 17:18:29 +00:00
|
|
|
def input_script_multisig(multisig: MultisigRedeemScriptType, signature: bytes, signature_index: int, sighash: int):
|
|
|
|
signatures = multisig.signatures # other signatures
|
|
|
|
if len(signatures[signature_index]) > 0:
|
2018-02-24 20:42:54 +00:00
|
|
|
raise ScriptsError('Invalid multisig parameters')
|
2018-02-24 17:18:29 +00:00
|
|
|
signatures[signature_index] = signature # our signature
|
|
|
|
|
2018-01-29 13:41:56 +00:00
|
|
|
w = bytearray()
|
2018-02-24 20:42:54 +00:00
|
|
|
# Starts with OP_FALSE because of an old OP_CHECKMULTISIG bug, which
|
|
|
|
# consumes one additional item on the stack:
|
|
|
|
# https://bitcoin.org/en/developer-guide#standard-transactions
|
2018-01-29 13:41:56 +00:00
|
|
|
w.append(0x00)
|
2018-02-24 17:18:29 +00:00
|
|
|
|
|
|
|
for s in signatures:
|
2018-01-29 13:41:56 +00:00
|
|
|
if len(s):
|
2018-02-09 12:21:22 +00:00
|
|
|
append_signature(w, s, sighash)
|
2018-01-29 13:41:56 +00:00
|
|
|
|
|
|
|
# redeem script
|
2018-02-24 17:18:29 +00:00
|
|
|
pubkeys = multisig_get_pubkeys(multisig)
|
2018-02-24 22:23:19 +00:00
|
|
|
redeem_script = output_script_multisig(pubkeys, multisig.m)
|
2018-01-29 13:41:56 +00:00
|
|
|
write_op_push(w, len(redeem_script))
|
2018-02-24 17:18:29 +00:00
|
|
|
write_bytes(w, redeem_script)
|
2018-01-29 13:41:56 +00:00
|
|
|
|
2018-02-24 22:23:19 +00:00
|
|
|
return w
|
2018-01-29 13:41:56 +00:00
|
|
|
|
|
|
|
|
2018-02-24 22:23:19 +00:00
|
|
|
def output_script_multisig(pubkeys, m: int) -> bytearray:
|
2018-01-24 15:12:54 +00:00
|
|
|
n = len(pubkeys)
|
2018-02-24 20:42:54 +00:00
|
|
|
if n < 1 or n > 15 or m < 1 or m > 15:
|
|
|
|
raise ScriptsError('Invalid multisig parameters')
|
2018-02-24 22:23:19 +00:00
|
|
|
for pubkey in pubkeys:
|
|
|
|
if len(pubkey) != 33:
|
|
|
|
raise ScriptsError('Invalid multisig parameters')
|
2018-01-24 15:12:54 +00:00
|
|
|
|
2018-01-29 13:41:56 +00:00
|
|
|
w = bytearray()
|
|
|
|
w.append(0x50 + m) # numbers 1 to 16 are pushed as 0x50 + value
|
2018-01-24 15:12:54 +00:00
|
|
|
for p in pubkeys:
|
2018-01-29 13:41:56 +00:00
|
|
|
append_pubkey(w, p)
|
|
|
|
w.append(0x50 + n)
|
|
|
|
w.append(0xAE) # OP_CHECKMULTISIG
|
|
|
|
return w
|
2018-01-24 15:12:54 +00:00
|
|
|
|
2017-11-10 13:10:19 +00:00
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
# OP_RETURN
|
|
|
|
# ===
|
2017-11-10 13:10:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
def output_script_paytoopreturn(data: bytes) -> bytearray:
|
|
|
|
w = bytearray_with_cap(1 + 5 + len(data))
|
|
|
|
w.append(0x6A) # OP_RETURN
|
|
|
|
write_op_push(w, len(data))
|
|
|
|
w.extend(data)
|
|
|
|
return w
|
|
|
|
|
|
|
|
|
2018-02-24 20:42:54 +00:00
|
|
|
# Helpers
|
|
|
|
# ===
|
|
|
|
|
2017-11-10 13:10:19 +00:00
|
|
|
|
2018-01-29 13:41:56 +00:00
|
|
|
def append_signature(w: bytearray, signature: bytes, sighash: int) -> bytearray:
|
2017-11-10 13:10:19 +00:00
|
|
|
write_op_push(w, len(signature) + 1)
|
|
|
|
write_bytes(w, signature)
|
2018-02-08 17:59:51 +00:00
|
|
|
w.append(sighash)
|
2018-01-29 13:41:56 +00:00
|
|
|
return w
|
|
|
|
|
|
|
|
|
|
|
|
def append_pubkey(w: bytearray, pubkey: bytes) -> bytearray:
|
2017-11-10 13:10:19 +00:00
|
|
|
write_op_push(w, len(pubkey))
|
|
|
|
write_bytes(w, pubkey)
|
|
|
|
return w
|
2018-02-24 22:23:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
def sha256_ripemd160_digest(b: bytes) -> bytes:
|
|
|
|
h = sha256(b).digest()
|
|
|
|
h = ripemd160(h).digest()
|
|
|
|
return h
|