diff --git a/src/apps/wallet/sign_tx/scripts.py b/src/apps/wallet/sign_tx/scripts.py index 751605eb43..7d5457b19e 100644 --- a/src/apps/wallet/sign_tx/scripts.py +++ b/src/apps/wallet/sign_tx/scripts.py @@ -1,6 +1,7 @@ from apps.wallet.sign_tx.writers import * from trezor.crypto.hashlib import sha256, ripemd160 +from apps.wallet.sign_tx.multisig import multisig_get_pubkeys # TX Scripts # === @@ -69,7 +70,7 @@ def output_script_native_p2wpkh_or_p2wsh(witprog: bytes) -> bytearray: return w -# =============== Native P2WPKH nested in P2SH =============== +# =============== P2WPKH nested in P2SH =============== # P2WPKH is nested in P2SH to be backwards compatible # see https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program @@ -77,7 +78,7 @@ def output_script_native_p2wpkh_or_p2wsh(witprog: bytes) -> bytearray: # signature is moved to the witness def input_script_p2wpkh_in_p2sh(pubkeyhash: bytes) -> bytearray: w = bytearray_with_cap(3 + len(pubkeyhash)) - w.append(0x16) # 0x16 - length of the redeemScript + 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 @@ -98,7 +99,63 @@ def input_script_p2wpkh_in_p2sh(pubkeyhash: bytes) -> bytearray: # output script consists of 00 20 <32-byte-key-hash> # same as output_script_native_p2wpkh_or_p2wsh (only different length) -# =============== Multisig =============== + +# =============== P2WSH nested in P2SH =============== +# P2WSH is nested in P2SH to be backwards compatible +# see https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#p2wsh-nested-in-bip16-p2sh + +# input script (scriptSig) is 22 00 20 +# P2WSH script hash is always equal to 32 bytes (Ox20) +# signature is moved to the witness +def input_script_p2wsh_in_p2sh(script_hash: bytes) -> bytearray: + if len(script_hash) != 32: + raise Exception('Redeem script hash should be 32 bytes long') + + 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 + +# output script (scriptPubKey) is A9 14 87 +# which is same as the output_script_p2sh + + +# =============== SegWit Witness getters =============== + +def get_p2wpkh_witness(signature: bytes, pubkey: bytes, sighash: int): + 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 + + +def get_p2wsh_witness(multisig: MultisigRedeemScriptType, current_signature: bytes, other_signatures, sighash: int): + # filter empty + other_signatures = [s for s in other_signatures if len(s) > 0] + + # witness program + other signatures + current signature + redeem script + num_of_witness_items = 1 + len(other_signatures) + 1 + 1 + + w = bytearray() + write_varint(w, num_of_witness_items) + write_varint(w, 0) # version 0 witness program + + for s in other_signatures: + append_signature(w, s, sighash) # size of the witness included + + append_signature(w, current_signature, sighash) + + redeem_script = script_multisig(multisig_get_pubkeys(multisig), multisig.m) + + write_varint(w, len(redeem_script)) + write_bytes(w, redeem_script) + return w + + +# -------------------------- Multisig -------------------------- def input_script_multisig(current_signature, other_signatures, pubkeys, m: int, sighash: int): w = bytearray() @@ -126,7 +183,7 @@ def output_script_multisig_p2sh(pubkeys, m) -> bytes: return ripemd160(h).digest() -# returns a sha256() hash of a multisig script used in native P2WSH +# returns a sha256() hash of a multisig script used in P2WSH def output_script_multisig_p2wsh(pubkeys, m) -> bytes: for pubkey in pubkeys: if len(pubkey) != 33: diff --git a/src/apps/wallet/sign_tx/segwit_bip143.py b/src/apps/wallet/sign_tx/segwit_bip143.py index 796a6e65d9..9621e831a1 100644 --- a/src/apps/wallet/sign_tx/segwit_bip143.py +++ b/src/apps/wallet/sign_tx/segwit_bip143.py @@ -3,7 +3,8 @@ from trezor.messages.SignTx import SignTx from trezor.messages import InputScriptType, FailureType from apps.wallet.sign_tx.writers import * -from apps.wallet.sign_tx.scripts import output_script_p2pkh +from apps.wallet.sign_tx.scripts import output_script_p2pkh, script_multisig +from apps.wallet.sign_tx.multisig import multisig_get_pubkeys from apps.common.hash_writer import HashWriter @@ -62,9 +63,9 @@ class Bip143: # see https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#specification # item 5 for details def derive_script_code(self, txi: TxInputType, pubkeyhash: bytes) -> bytearray: - # p2wsh multisig to be implemented + if txi.multisig: - raise Bip143Error(FailureType.DataError, 'Bip143 multisig support to be implemented') + return script_multisig(multisig_get_pubkeys(txi.multisig), txi.multisig.m) p2pkh = (txi.script_type == InputScriptType.SPENDWITNESS or txi.script_type == InputScriptType.SPENDP2SHWITNESS or diff --git a/src/apps/wallet/sign_tx/signing.py b/src/apps/wallet/sign_tx/signing.py index 71e67a783b..6d5b22ed12 100644 --- a/src/apps/wallet/sign_tx/signing.py +++ b/src/apps/wallet/sign_tx/signing.py @@ -103,7 +103,7 @@ async def check_tx_fee(tx: SignTx, root): segwit[i] = False total_in += await get_prevtx_output_value( tx_req, txi.prev_hash, txi.prev_index) - if txi.script_type == InputScriptType.SPENDMULTISIG: + if txi.multisig: fp = multisig_fingerprint(txi.multisig) if not len(multisig_fp): multisig_fp = fp @@ -345,7 +345,10 @@ async def sign_tx(tx: SignTx, root): tx, txi, ecdsa_hash_pubkey(key_sign_pub), get_hash_type(coin)) signature = ecdsa_sign(key_sign, bip143_hash) - witness = get_p2wpkh_witness(coin, signature, key_sign_pub) + if txi.multisig: + witness = get_p2wsh_witness(txi.multisig, signature, txi.multisig.signatures, get_hash_type(coin)) + else: + witness = get_p2wpkh_witness(signature, key_sign_pub, get_hash_type(coin)) tx_ser.serialized_tx = witness tx_ser.signature_index = i @@ -426,14 +429,6 @@ def get_tx_header(tx: SignTx, segwit=False): return w_txi -def get_p2wpkh_witness(coin: CoinType, signature: bytes, pubkey: bytes): - 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, get_hash_type(coin)) - append_pubkey(w, pubkey) - return w - - # TX Outputs # === @@ -506,10 +501,17 @@ def output_is_change(o: TxOutputType, wallet_path: list, segwit_in: int) -> bool def input_derive_script(coin: CoinType, i: TxInputType, pubkey: bytes, signature: bytes=None) -> bytes: if i.script_type == InputScriptType.SPENDADDRESS: return input_script_p2pkh_or_p2sh(pubkey, signature, get_hash_type(coin)) # p2pkh or p2sh - if i.script_type == InputScriptType.SPENDP2SHWITNESS: # p2wpkh using p2sh + + if i.script_type == InputScriptType.SPENDP2SHWITNESS: # p2wpkh or p2wsh using p2sh + if i.multisig: # p2wsh in p2sh + return input_script_p2wsh_in_p2sh(output_script_multisig_p2wsh(multisig_get_pubkeys(i.multisig), + i.multisig.m)) + # p2wpkh in p2sh return input_script_p2wpkh_in_p2sh(ecdsa_hash_pubkey(pubkey)) + elif i.script_type == InputScriptType.SPENDWITNESS: # native p2wpkh or p2wsh return input_script_native_p2wpkh_or_p2wsh() + # mutlisig elif i.script_type == InputScriptType.SPENDMULTISIG: return input_script_multisig(signature, i.multisig.signatures, multisig_get_pubkeys(i.multisig), i.multisig.m,