mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-23 14:58:09 +00:00
signing: multisig addresses (p2sh, p2wsh in p2sh)
This commit is contained in:
parent
8fbf89b7f3
commit
e4423567cb
@ -8,6 +8,8 @@ from trezor.messages.CoinType import CoinType
|
||||
from trezor.messages import FailureType
|
||||
from trezor.messages import InputScriptType
|
||||
|
||||
from apps.wallet.sign_tx.scripts import *
|
||||
|
||||
# supported witness version for bech32 addresses
|
||||
_BECH32_WITVER = const(0x00)
|
||||
|
||||
@ -38,6 +40,45 @@ def get_address(script_type: InputScriptType, coin: CoinType, node) -> str:
|
||||
'Invalid script type')
|
||||
|
||||
|
||||
def address_multisig_p2sh(pubkeys: bytes, m: int, addrtype):
|
||||
digest = multisig_p2sh_script(pubkeys, m)
|
||||
if addrtype is None:
|
||||
raise AddressError(FailureType.ProcessError,
|
||||
'Multisig not enabled on this coin')
|
||||
return address_p2sh(digest, addrtype)
|
||||
|
||||
|
||||
def address_multisig_p2wsh_in_p2sh(pubkeys: bytes, m: int, addrtype):
|
||||
digest = multisig_p2wsh_script(pubkeys, m)
|
||||
if addrtype is None:
|
||||
raise AddressError(FailureType.ProcessError,
|
||||
'Multisig not enabled on this coin')
|
||||
return address_p2wsh_in_p2sh(digest, addrtype)
|
||||
|
||||
|
||||
def address_multisig_p2wsh(pubkeys: bytes, m: int, addrtype):
|
||||
digest = multisig_p2wsh_script(pubkeys, m)
|
||||
if addrtype is None:
|
||||
raise AddressError(FailureType.ProcessError,
|
||||
'Multisig not enabled on this coin')
|
||||
return address_p2sh(digest, addrtype)
|
||||
|
||||
|
||||
# Returns ripemd160(sha256(multisig script)) as per P2SH definition
|
||||
def multisig_p2sh_script(pubkeys, m) -> bytes:
|
||||
script = multisig_script(pubkeys, m)
|
||||
return ripemd160(script.get_digest()).digest()
|
||||
|
||||
|
||||
# Returns sha256(multisig script) as per P2WSH definition
|
||||
def multisig_p2wsh_script(pubkeys, m) -> bytes:
|
||||
for pubkey in pubkeys:
|
||||
if len(pubkey) != 33:
|
||||
raise Exception # only compressed public keys are allowed for P2WSH
|
||||
script = multisig_script(pubkeys, m)
|
||||
return script.get_digest()
|
||||
|
||||
|
||||
def address_p2sh(redeem_script_hash: bytes, addrtype: int) -> str:
|
||||
s = bytearray(21)
|
||||
s[0] = addrtype
|
||||
|
@ -1,4 +1,6 @@
|
||||
from apps.wallet.sign_tx.multisig import *
|
||||
from apps.wallet.sign_tx.writers import *
|
||||
from apps.common.hash_writer import HashWriter
|
||||
|
||||
|
||||
# TX Scripts
|
||||
@ -96,6 +98,24 @@ 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 ===============
|
||||
|
||||
def multisig_script(pubkeys, m) -> HashWriter:
|
||||
n = len(pubkeys)
|
||||
if n < 1 or n > 15:
|
||||
raise Exception
|
||||
if m < 1 or m > 15:
|
||||
raise Exception
|
||||
|
||||
h = HashWriter(sha256)
|
||||
h.append(0x50 + m)
|
||||
for p in pubkeys:
|
||||
h.append(len(p)) # OP_PUSH length (33 for compressed)
|
||||
write_bytes(h, p)
|
||||
h.append(0x50 + n)
|
||||
h.append(0xAE) # OP_CHECKMULTISIG
|
||||
return h
|
||||
|
||||
|
||||
# -------------------------- Others --------------------------
|
||||
|
||||
|
@ -43,7 +43,7 @@ class TestAddress(unittest.TestCase):
|
||||
self.assertEqual(address, '2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp')
|
||||
|
||||
def test_p2wpkh_address(self):
|
||||
# data from https://bc-2.jp/tools/bech32demo/index.html
|
||||
# test data from https://bc-2.jp/tools/bech32demo/index.html
|
||||
coin = coins.by_name('Testnet')
|
||||
address = address_p2wpkh(
|
||||
unhexlify('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'),
|
||||
@ -77,7 +77,7 @@ class TestAddress(unittest.TestCase):
|
||||
def test_p2wsh_in_p2sh_address(self):
|
||||
coin = coins.by_name('Bitcoin')
|
||||
|
||||
# data from Mastering Bitcoin
|
||||
# test data from Mastering Bitcoin
|
||||
address = address_p2wsh_in_p2sh(
|
||||
unhexlify('9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73'),
|
||||
coin.address_type_p2sh
|
||||
@ -90,6 +90,38 @@ class TestAddress(unittest.TestCase):
|
||||
)
|
||||
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')
|
||||
|
||||
coin = coins.by_name('Bitcoin')
|
||||
pubkeys = [
|
||||
unhexlify('02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f'),
|
||||
unhexlify('02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8'),
|
||||
]
|
||||
address = address_multisig_p2sh(pubkeys, 2, coin.address_type_p2sh)
|
||||
self.assertEqual(address, '39bgKC7RFbpoCRbtD5KEdkYKtNyhpsNa3Z')
|
||||
|
||||
def test_multisig_address_p2wsh_in_p2sh(self):
|
||||
# test data from
|
||||
# https://bitcoin.stackexchange.com/questions/62656/generate-a-p2sh-p2wsh-address-and-spend-output-sent-to-it
|
||||
coin = coins.by_name('Testnet')
|
||||
pubkeys = [
|
||||
unhexlify('020b020e27e49f049eac10010506499a84e1d59a500cd3680e9ded580df9a107b0'),
|
||||
unhexlify('0320ce424c6d61f352ccfea60d209651672cfb03b2dc77d1d64d3ba519aec756ae'),
|
||||
]
|
||||
|
||||
address = address_multisig_p2wsh_in_p2sh(pubkeys, 2, coin.address_type_p2sh)
|
||||
self.assertEqual(address, '2MsZ2fpGKUydzY62v6trPHR8eCx5JTy1Dpa')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
Reference in New Issue
Block a user