src/apps/wallet/sign_tx: cleanup and split scripts/addresses

pull/25/head
Jan Pochyla 6 years ago
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()
return w
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…
Cancel
Save