1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-23 06:48:16 +00:00

signing/multisig: segwit support

This commit is contained in:
Tomas Susanka 2018-02-12 09:19:38 +01:00 committed by Jan Pochyla
parent e6d693d18a
commit 805593c1ac
3 changed files with 78 additions and 18 deletions

View File

@ -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 <redeem script hash>
# 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 <scripthash> 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:

View File

@ -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

View File

@ -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,