mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-11 16:00:57 +00:00
src/apps/wallet/sign_tx: cleanup and split scripts/addresses
This commit is contained in:
parent
72aa5e5204
commit
fa5dbadc93
@ -32,7 +32,8 @@ def get_address(script_type: InputScriptType, coin: CoinType, node, multisig=Non
|
||||
raise AddressError(FailureType.ProcessError,
|
||||
'Multisig not enabled on this coin')
|
||||
|
||||
return address_multisig_p2sh(multisig_get_pubkeys(multisig), multisig.m, coin.address_type_p2sh)
|
||||
pubkeys = multisig_get_pubkeys(multisig)
|
||||
return address_multisig_p2sh(pubkeys, multisig.m, coin.address_type_p2sh)
|
||||
if script_type == InputScriptType.SPENDMULTISIG:
|
||||
raise AddressError(FailureType.ProcessError,
|
||||
'Multisig details required')
|
||||
@ -46,7 +47,8 @@ def get_address(script_type: InputScriptType, coin: CoinType, node, multisig=Non
|
||||
'Segwit not enabled on this coin')
|
||||
# native p2wsh multisig
|
||||
if multisig is not None:
|
||||
return address_multisig_p2wsh(multisig_get_pubkeys(multisig), multisig.m, coin.bech32_prefix)
|
||||
pubkeys = multisig_get_pubkeys(multisig)
|
||||
return address_multisig_p2wsh(pubkeys, multisig.m, coin.bech32_prefix)
|
||||
|
||||
# native p2wpkh
|
||||
return address_p2wpkh(node.public_key(), coin.bech32_prefix)
|
||||
@ -57,7 +59,8 @@ def get_address(script_type: InputScriptType, coin: CoinType, node, multisig=Non
|
||||
'Segwit not enabled on this coin')
|
||||
# p2wsh multisig nested in p2sh
|
||||
if multisig is not None:
|
||||
return address_multisig_p2wsh_in_p2sh(multisig_get_pubkeys(multisig), multisig.m, coin.address_type_p2sh)
|
||||
pubkeys = multisig_get_pubkeys(multisig)
|
||||
return address_multisig_p2wsh_in_p2sh(pubkeys, multisig.m, coin.address_type_p2sh)
|
||||
|
||||
# p2wpkh nested in p2sh
|
||||
return address_p2wpkh_in_p2sh(node.public_key(), coin.address_type_p2sh)
|
||||
@ -67,25 +70,31 @@ def get_address(script_type: InputScriptType, coin: CoinType, node, multisig=Non
|
||||
'Invalid script type')
|
||||
|
||||
|
||||
def address_multisig_p2sh(pubkeys: bytes, m: int, addrtype):
|
||||
digest = output_script_multisig_p2sh(pubkeys, m)
|
||||
def address_multisig_p2sh(pubkeys: bytes, m: int, addrtype: int):
|
||||
if addrtype is None:
|
||||
raise AddressError(FailureType.ProcessError,
|
||||
'Multisig not enabled on this coin')
|
||||
return address_p2sh(digest, addrtype)
|
||||
redeem_script = output_script_multisig(pubkeys, m)
|
||||
redeem_script_hash = sha256_ripemd160_digest(redeem_script)
|
||||
return address_p2sh(redeem_script_hash, addrtype)
|
||||
|
||||
|
||||
def address_multisig_p2wsh_in_p2sh(pubkeys: bytes, m: int, addrtype):
|
||||
digest = output_script_multisig_p2wsh(pubkeys, m)
|
||||
def address_multisig_p2wsh_in_p2sh(pubkeys: bytes, m: int, addrtype: int):
|
||||
if addrtype is None:
|
||||
raise AddressError(FailureType.ProcessError,
|
||||
'Multisig not enabled on this coin')
|
||||
return address_p2wsh_in_p2sh(digest, addrtype)
|
||||
witness_script = output_script_multisig(pubkeys, m)
|
||||
witness_script_hash = sha256(witness_script).digest()
|
||||
return address_p2wsh_in_p2sh(witness_script_hash, addrtype)
|
||||
|
||||
|
||||
def address_multisig_p2wsh(pubkeys: bytes, m: int, hrp: str):
|
||||
digest = output_script_multisig_p2wsh(pubkeys, m)
|
||||
return address_p2wsh(digest, hrp)
|
||||
if not hrp:
|
||||
raise AddressError(FailureType.ProcessError,
|
||||
'Multisig not enabled on this coin')
|
||||
witness_script = output_script_multisig(pubkeys, m)
|
||||
witness_script_hash = sha256(witness_script).digest()
|
||||
return address_p2wsh(witness_script_hash, hrp)
|
||||
|
||||
|
||||
def address_p2sh(redeem_script_hash: bytes, addrtype: int) -> str:
|
||||
@ -95,43 +104,19 @@ def address_p2sh(redeem_script_hash: bytes, addrtype: int) -> str:
|
||||
return base58.encode_check(bytes(s))
|
||||
|
||||
|
||||
# P2WPKH nested in P2SH. The P2SH redeem script hash is created using the
|
||||
# `raw` function
|
||||
def address_p2wpkh_in_p2sh(pubkey: bytes, addrtype: int) -> str:
|
||||
redeem_script_hash = address_p2wpkh_in_p2sh_script(pubkey)
|
||||
pubkey_hash = ecdsa_hash_pubkey(pubkey)
|
||||
redeem_script = output_script_native_p2wpkh_or_p2wsh(pubkey_hash)
|
||||
redeem_script_hash = sha256_ripemd160_digest(redeem_script)
|
||||
return address_p2sh(redeem_script_hash, addrtype)
|
||||
|
||||
|
||||
# Generates a P2SH redeem script based on a public key hash in a P2WPKH manner
|
||||
def address_p2wpkh_in_p2sh_script(pubkey: bytes) -> bytes:
|
||||
s = bytearray(22)
|
||||
s[0] = 0x00 # OP_0
|
||||
s[1] = 0x14 # pushing 20 bytes
|
||||
s[2:22] = ecdsa_hash_pubkey(pubkey)
|
||||
h = sha256(s).digest()
|
||||
h = ripemd160(h).digest()
|
||||
return h
|
||||
|
||||
|
||||
# P2WSH nested in P2SH. The P2SH redeem script hash is created using the
|
||||
# `raw` function
|
||||
def address_p2wsh_in_p2sh(witness_script_hash: bytes, addrtype: int) -> str:
|
||||
redeem_script_hash = address_p2wsh_in_p2sh_script(witness_script_hash)
|
||||
redeem_script = output_script_native_p2wpkh_or_p2wsh(witness_script_hash)
|
||||
redeem_script_hash = sha256_ripemd160_digest(redeem_script)
|
||||
return address_p2sh(redeem_script_hash, addrtype)
|
||||
|
||||
|
||||
# Generates a P2SH redeem script based on a hash of a witness redeem script hash
|
||||
def address_p2wsh_in_p2sh_script(script_hash: bytes) -> bytes:
|
||||
s = bytearray(34)
|
||||
s[0] = 0x00 # OP_0
|
||||
s[1] = 0x20 # pushing 32 bytes
|
||||
s[2:34] = script_hash
|
||||
h = sha256(s).digest()
|
||||
h = ripemd160(h).digest()
|
||||
return h
|
||||
|
||||
|
||||
# Native Bech32 P2WPKH
|
||||
def address_p2wpkh(pubkey: bytes, hrp: str) -> str:
|
||||
pubkeyhash = ecdsa_hash_pubkey(pubkey)
|
||||
address = bech32.encode(hrp, _BECH32_WITVER, pubkeyhash)
|
||||
@ -141,9 +126,8 @@ def address_p2wpkh(pubkey: bytes, hrp: str) -> str:
|
||||
return address
|
||||
|
||||
|
||||
# Native Bech32 P2WSH, script_hash is 32-byte SHA256(script)
|
||||
def address_p2wsh(script_hash: bytes, hrp: str) -> str:
|
||||
address = bech32.encode(hrp, _BECH32_WITVER, script_hash)
|
||||
def address_p2wsh(witness_script_hash: bytes, hrp: str) -> str:
|
||||
address = bech32.encode(hrp, _BECH32_WITVER, witness_script_hash)
|
||||
if address is None:
|
||||
raise AddressError(FailureType.ProcessError,
|
||||
'Invalid address')
|
||||
|
@ -146,7 +146,7 @@ def witness_p2wsh(multisig: MultisigRedeemScriptType, signature: bytes, signatur
|
||||
|
||||
# redeem script
|
||||
pubkeys = multisig_get_pubkeys(multisig)
|
||||
redeem_script = script_multisig(pubkeys, multisig.m)
|
||||
redeem_script = output_script_multisig(pubkeys, multisig.m)
|
||||
write_varint(w, len(redeem_script))
|
||||
write_bytes(w, redeem_script)
|
||||
return w
|
||||
@ -176,32 +176,20 @@ def input_script_multisig(multisig: MultisigRedeemScriptType, signature: bytes,
|
||||
|
||||
# redeem script
|
||||
pubkeys = multisig_get_pubkeys(multisig)
|
||||
redeem_script = script_multisig(pubkeys, multisig.m)
|
||||
redeem_script = output_script_multisig(pubkeys, multisig.m)
|
||||
write_op_push(w, len(redeem_script))
|
||||
write_bytes(w, redeem_script)
|
||||
|
||||
return w
|
||||
|
||||
|
||||
# returns a ripedm(sha256()) hash of a multisig script used in P2SH
|
||||
def output_script_multisig_p2sh(pubkeys, m) -> bytes:
|
||||
script = script_multisig(pubkeys, m)
|
||||
h = sha256(script).digest()
|
||||
return ripemd160(h).digest()
|
||||
|
||||
|
||||
# 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:
|
||||
raise ScriptsError('Only compressed public keys are allowed for P2WSH')
|
||||
script = script_multisig(pubkeys, m)
|
||||
return sha256(script).digest()
|
||||
|
||||
|
||||
def script_multisig(pubkeys, m) -> bytearray:
|
||||
def output_script_multisig(pubkeys, m: int) -> bytearray:
|
||||
n = len(pubkeys)
|
||||
if n < 1 or n > 15 or m < 1 or m > 15:
|
||||
raise ScriptsError('Invalid multisig parameters')
|
||||
for pubkey in pubkeys:
|
||||
if len(pubkey) != 33:
|
||||
raise ScriptsError('Invalid multisig parameters')
|
||||
|
||||
w = bytearray()
|
||||
w.append(0x50 + m) # numbers 1 to 16 are pushed as 0x50 + value
|
||||
@ -239,3 +227,9 @@ def append_pubkey(w: bytearray, pubkey: bytes) -> bytearray:
|
||||
write_op_push(w, len(pubkey))
|
||||
write_bytes(w, pubkey)
|
||||
return w
|
||||
|
||||
|
||||
def sha256_ripemd160_digest(b: bytes) -> bytes:
|
||||
h = sha256(b).digest()
|
||||
h = ripemd160(h).digest()
|
||||
return h
|
||||
|
@ -3,7 +3,7 @@ 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, script_multisig
|
||||
from apps.wallet.sign_tx.scripts import output_script_p2pkh, output_script_multisig
|
||||
from apps.wallet.sign_tx.multisig import multisig_get_pubkeys
|
||||
from apps.common.hash_writer import HashWriter
|
||||
|
||||
@ -65,7 +65,7 @@ class Bip143:
|
||||
def derive_script_code(self, txi: TxInputType, pubkeyhash: bytes) -> bytearray:
|
||||
|
||||
if txi.multisig:
|
||||
return script_multisig(multisig_get_pubkeys(txi.multisig), txi.multisig.m)
|
||||
return output_script_multisig(multisig_get_pubkeys(txi.multisig), txi.multisig.m)
|
||||
|
||||
p2pkh = (txi.script_type == InputScriptType.SPENDWITNESS or
|
||||
txi.script_type == InputScriptType.SPENDP2SHWITNESS or
|
||||
|
@ -250,7 +250,7 @@ async def sign_tx(tx: SignTx, root):
|
||||
# for the signing process the script_sig is equal
|
||||
# to the previous tx's scriptPubKey (P2PKH) or a redeem script (P2SH)
|
||||
if txi_sign.script_type == InputScriptType.SPENDMULTISIG:
|
||||
txi_sign.script_sig = script_multisig(
|
||||
txi_sign.script_sig = output_script_multisig(
|
||||
multisig_get_pubkeys(txi_sign.multisig),
|
||||
txi_sign.multisig.m)
|
||||
elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
|
||||
@ -512,8 +512,10 @@ def input_derive_script(coin: CoinType, i: TxInputType, pubkey: bytes, signature
|
||||
if i.script_type == InputScriptType.SPENDP2SHWITNESS: # p2wpkh or p2wsh using p2sh
|
||||
if i.multisig: # p2wsh in p2sh
|
||||
pubkeys = multisig_get_pubkeys(i.multisig)
|
||||
script_hash = output_script_multisig_p2wsh(pubkeys, i.multisig.m)
|
||||
return input_script_p2wsh_in_p2sh(script_hash)
|
||||
witness_script = output_script_multisig(pubkeys, i.multisig.m)
|
||||
witness_script_hash = sha256(witness_script).digest()
|
||||
return input_script_p2wsh_in_p2sh(witness_script_hash)
|
||||
|
||||
# p2wpkh in p2sh
|
||||
return input_script_p2wpkh_in_p2sh(ecdsa_hash_pubkey(pubkey))
|
||||
|
||||
|
@ -16,12 +16,6 @@ class TestAddress(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2')
|
||||
|
||||
def test_p2wpkh_in_p2sh_script_address(self):
|
||||
raw = address_p2wpkh_in_p2sh_script(
|
||||
unhexlify('03a1af804ac108a8a51782198c2d034b28bf90c8803f5a53f76276fa69a4eae77f')
|
||||
)
|
||||
self.assertEqual(raw, unhexlify('336caa13e08b96080a32b5d818d59b4ab3b36742'))
|
||||
|
||||
def test_p2wpkh_in_p2sh_node_derive_address(self):
|
||||
coin = coins.by_name('Testnet')
|
||||
seed = bip39.seed(' '.join(['all'] * 12), '')
|
||||
@ -84,23 +78,18 @@ class TestAddress(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(address, '3Dwz1MXhM6EfFoJChHCxh1jWHb8GQqRenG')
|
||||
|
||||
def test_p2wsh_in_p2sh_script_address(self):
|
||||
raw = address_p2wsh_in_p2sh_script(
|
||||
unhexlify('1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262')
|
||||
)
|
||||
self.assertEqual(raw, unhexlify('e4300531190587e3880d4c3004f5355d88ff928d'))
|
||||
|
||||
def test_multisig_address_p2sh(self):
|
||||
# test data from
|
||||
# http://www.soroushjp.com/2014/12/20/bitcoin-multisig-the-hard-way-understanding-raw-multisignature-bitcoin-transactions/
|
||||
coin = coins.by_name('Bitcoin')
|
||||
pubkeys = [
|
||||
unhexlify('04a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd'),
|
||||
unhexlify('046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187'),
|
||||
unhexlify('0411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e83'),
|
||||
]
|
||||
address = address_multisig_p2sh(pubkeys, 2, coin.address_type_p2sh)
|
||||
self.assertEqual(address, '347N1Thc213QqfYCz3PZkjoJpNv5b14kBd')
|
||||
# # test data from
|
||||
# # http://www.soroushjp.com/2014/12/20/bitcoin-multisig-the-hard-way-understanding-raw-multisignature-bitcoin-transactions/
|
||||
# # commented out because uncompressed public keys are not supported
|
||||
# coin = coins.by_name('Bitcoin')
|
||||
# pubkeys = [
|
||||
# unhexlify('04a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd'),
|
||||
# unhexlify('046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187'),
|
||||
# unhexlify('0411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e83'),
|
||||
# ]
|
||||
# address = address_multisig_p2sh(pubkeys, 2, coin.address_type_p2sh)
|
||||
# self.assertEqual(address, '347N1Thc213QqfYCz3PZkjoJpNv5b14kBd')
|
||||
|
||||
coin = coins.by_name('Bitcoin')
|
||||
pubkeys = [
|
||||
|
Loading…
Reference in New Issue
Block a user