1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-02-12 23:52:39 +00:00
trezor-firmware/src/apps/wallet/sign_tx/scripts.py

268 lines
7.2 KiB
Python
Raw Normal View History

2018-06-05 18:21:31 +00:00
from trezor.messages.MultisigRedeemScriptType import MultisigRedeemScriptType
from trezor.utils import ensure
2018-07-03 14:20:26 +00:00
from apps.common.coininfo import CoinInfo
2018-08-23 12:39:30 +00:00
from apps.common.writers import empty_bytearray
2018-02-12 08:19:38 +00:00
from apps.wallet.sign_tx.multisig import multisig_get_pubkeys
2018-07-03 14:20:26 +00:00
from apps.wallet.sign_tx.writers import (
write_bytes,
write_op_push,
write_scriptnum,
write_varint,
)
class ScriptsError(ValueError):
pass
2018-02-24 20:42:54 +00:00
# P2PKH, P2SH
# ===
2018-02-24 20:42:54 +00:00
# https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
2018-07-03 14:20:26 +00:00
def input_script_p2pkh_or_p2sh(
pubkey: bytes, signature: bytes, sighash: int
) -> bytearray:
2018-08-23 12:39:30 +00:00
w = empty_bytearray(5 + len(signature) + 1 + 5 + len(pubkey))
2018-01-29 13:41:56 +00:00
append_signature(w, signature, sighash)
append_pubkey(w, pubkey)
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
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-03 14:20:58 +00:00
def script_replay_protection_bip115(
block_hash: bytes, block_height: bytes
) -> bytearray:
if block_hash is None or block_height is None:
return bytearray()
ensure(len(block_hash) == 32)
s = bytearray(33)
s[0] = 0x20 # 32 bytes for block hash
s[1:33] = block_hash # block hash
write_scriptnum(s, block_height)
s.append(0xB4) # OP_CHECKBLOCKATHEIGHT
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.
def input_script_native_p2wpkh_or_p2wsh() -> bytearray:
2018-02-24 20:42:54 +00:00
# Completely replaced by the witness and therefore empty.
return bytearray(0)
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>
2018-08-23 12:39:30 +00:00
w = empty_bytearray(3 + len(witprog))
w.append(0x00) # witness version byte
w.append(len(witprog)) # pub key hash length is 20 (P2WPKH) or 32 (P2WSH) bytes
write_bytes(w, witprog) # pub key hash
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.
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.
2018-08-23 12:39:30 +00:00
w = empty_bytearray(3 + len(pubkeyhash))
2018-02-12 08:19:38 +00:00
w.append(0x16) # length of the data
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-07-03 14:20:26 +00:00
raise ScriptsError("Redeem script hash should be 32 bytes long")
2018-02-12 08:19:38 +00:00
2018-08-23 12:39:30 +00:00
w = empty_bytearray(3 + len(script_hash))
2018-02-12 08:19:38 +00:00
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-08-23 12:39:30 +00:00
w = empty_bytearray(1 + 5 + len(signature) + 1 + 5 + len(pubkey))
2018-02-12 08:19:38 +00:00
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-07-03 14:20:26 +00:00
def witness_p2wsh(
multisig: MultisigRedeemScriptType,
signature: bytes,
signature_index: int,
sighash: int,
):
signatures = multisig.signatures # other signatures
if len(signatures[signature_index]) > 0:
2018-07-03 14:20:26 +00:00
raise ScriptsError("Invalid multisig parameters")
signatures[signature_index] = signature # our signature
2018-02-12 08:19:38 +00:00
# filter empty
signatures = [s for s in multisig.signatures if len(s) > 0]
2018-02-12 08:19:38 +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
for s in signatures:
2018-02-12 08:19:38 +00:00
append_signature(w, s, sighash) # size of the witness included
# redeem script
pubkeys = multisig_get_pubkeys(multisig)
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-07-03 14:20:26 +00:00
def input_script_multisig(
multisig: MultisigRedeemScriptType,
signature: bytes,
signature_index: int,
sighash: int,
coin: CoinInfo,
2018-07-03 14:20:26 +00:00
):
signatures = multisig.signatures # other signatures
if len(signatures[signature_index]) > 0:
2018-07-03 14:20:26 +00:00
raise ScriptsError("Invalid multisig parameters")
signatures[signature_index] = signature # our signature
2018-01-29 13:41:56 +00:00
w = bytearray()
if not coin.decred:
# 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
w.append(0x00)
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
pubkeys = multisig_get_pubkeys(multisig)
redeem_script = output_script_multisig(pubkeys, multisig.m)
2018-01-29 13:41:56 +00:00
write_op_push(w, len(redeem_script))
write_bytes(w, redeem_script)
2018-01-29 13:41:56 +00:00
return w
2018-01-29 13:41:56 +00:00
def output_script_multisig(pubkeys, m: int) -> bytearray:
n = len(pubkeys)
2018-02-24 20:42:54 +00:00
if n < 1 or n > 15 or m < 1 or m > 15:
2018-07-03 14:20:26 +00:00
raise ScriptsError("Invalid multisig parameters")
for pubkey in pubkeys:
if len(pubkey) != 33:
2018-07-03 14:20:26 +00:00
raise ScriptsError("Invalid multisig parameters")
2018-01-29 13:41:56 +00:00
w = bytearray()
w.append(0x50 + m) # numbers 1 to 16 are pushed as 0x50 + value
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-02-24 20:42:54 +00:00
# OP_RETURN
# ===
def output_script_paytoopreturn(data: bytes) -> bytearray:
2018-08-23 12:39:30 +00:00
w = empty_bytearray(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
# ===
2018-01-29 13:41:56 +00:00
def append_signature(w: bytearray, signature: bytes, sighash: int) -> bytearray:
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:
write_op_push(w, len(pubkey))
write_bytes(w, pubkey)
return w