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:
parent
e6d693d18a
commit
805593c1ac
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user