1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-27 07:40:59 +00:00

feat(core): add support for Zcash unified addresses

This commit is contained in:
Tomas Krnak 2022-08-12 08:45:42 +02:00 committed by Andrew Kozlik
parent 054b5456d2
commit 031bac4a9b
11 changed files with 394 additions and 0 deletions

View File

@ -0,0 +1 @@
Add support for Zcash unified addresses

View File

@ -609,6 +609,7 @@ if FROZEN:
SOURCE_PY_DIR + 'trezor/enums/Ripple*.py', SOURCE_PY_DIR + 'trezor/enums/Ripple*.py',
SOURCE_PY_DIR + 'trezor/enums/Stellar*.py', SOURCE_PY_DIR + 'trezor/enums/Stellar*.py',
SOURCE_PY_DIR + 'trezor/enums/Tezos*.py', SOURCE_PY_DIR + 'trezor/enums/Tezos*.py',
SOURCE_PY_DIR + 'trezor/enums/Zcash*.py',
]) ])
) )
@ -675,6 +676,7 @@ if FROZEN:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/decred.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/decred.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/bitcoinlike.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/bitcoinlike.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/zcash_v4.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/zcash_v4.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Zcash*.py'))
source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY) source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY)

View File

@ -565,6 +565,7 @@ if FROZEN:
SOURCE_PY_DIR + 'trezor/enums/Ripple*.py', SOURCE_PY_DIR + 'trezor/enums/Ripple*.py',
SOURCE_PY_DIR + 'trezor/enums/Stellar*.py', SOURCE_PY_DIR + 'trezor/enums/Stellar*.py',
SOURCE_PY_DIR + 'trezor/enums/Tezos*.py', SOURCE_PY_DIR + 'trezor/enums/Tezos*.py',
SOURCE_PY_DIR + 'trezor/enums/Zcash*.py',
]) ])
) )
@ -631,6 +632,7 @@ if FROZEN:
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/decred.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/decred.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/bitcoinlike.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/bitcoinlike.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/zcash_v4.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/bitcoin/sign_tx/zcash_v4.py'))
SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'trezor/enums/Zcash*.py'))
source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY) source_mpy = env.FrozenModule(source=SOURCE_PY, source_dir=SOURCE_PY_DIR, bitcoin_only=BITCOIN_ONLY)

View File

@ -775,10 +775,14 @@ if not utils.BITCOIN_ONLY:
import apps.webauthn.resident_credentials import apps.webauthn.resident_credentials
apps.zcash apps.zcash
import apps.zcash import apps.zcash
apps.zcash.f4jumble
import apps.zcash.f4jumble
apps.zcash.hasher apps.zcash.hasher
import apps.zcash.hasher import apps.zcash.hasher
apps.zcash.signer apps.zcash.signer
import apps.zcash.signer import apps.zcash.signer
apps.zcash.unified_addresses
import apps.zcash.unified_addresses
# generate full alphabet # generate full alphabet
a a

View File

@ -0,0 +1,58 @@
"""
Memory-optimized implementation of F4jumble permutation specified in ZIP-316.
specification: https://zips.z.cash/zip-0316#jumbling
reference implementation: https://github.com/zcash/librustzcash/blob/main/components/f4jumble/src/lib.rs
"""
from micropython import const
from trezor.crypto.hashlib import blake2b
HASH_LENGTH = const(64)
def xor(target: memoryview, mask: bytes) -> None:
for i in range(len(target)):
target[i] ^= mask[i]
def G_round(i: int, left: memoryview, right: memoryview) -> None:
for j in range((len(right) + HASH_LENGTH - 1) // HASH_LENGTH):
mask = blake2b(
personal=b"UA_F4Jumble_G" + bytes([i]) + j.to_bytes(2, "little"),
data=bytes(left),
).digest()
xor(right[j * HASH_LENGTH : (j + 1) * HASH_LENGTH], mask)
def H_round(i: int, left: memoryview, right: memoryview) -> None:
mask = blake2b(
personal=b"UA_F4Jumble_H" + bytes([i, 0, 0]),
outlen=len(left),
data=bytes(right),
).digest()
xor(left, mask)
def f4jumble(message: memoryview) -> None:
assert 48 <= len(message) <= 4194368
left_length = min(HASH_LENGTH, len(message) // 2)
left = message[:left_length]
right = message[left_length:]
G_round(0, left, right)
H_round(0, left, right)
G_round(1, left, right)
H_round(1, left, right)
def f4unjumble(message: memoryview) -> None:
assert 48 <= len(message) <= 4194368
left_length = min(HASH_LENGTH, len(message) // 2)
left = message[:left_length]
right = message[left_length:]
H_round(1, left, right)
G_round(1, left, right)
H_round(0, left, right)
G_round(0, left, right)

View File

@ -1,15 +1,19 @@
from micropython import const from micropython import const
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from trezor.enums import OutputScriptType
from trezor.messages import SignTx from trezor.messages import SignTx
from trezor.utils import ensure from trezor.utils import ensure
from trezor.wire import DataError, ProcessError from trezor.wire import DataError, ProcessError
from apps.bitcoin import scripts
from apps.bitcoin.common import ecdsa_sign from apps.bitcoin.common import ecdsa_sign
from apps.bitcoin.sign_tx.bitcoinlike import Bitcoinlike from apps.bitcoin.sign_tx.bitcoinlike import Bitcoinlike
from apps.common.writers import write_compact_size, write_uint32_le from apps.common.writers import write_compact_size, write_uint32_le
from . import unified_addresses
from .hasher import ZcashHasher from .hasher import ZcashHasher
from .unified_addresses import Typecode
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Sequence from typing import Sequence
@ -21,6 +25,7 @@ if TYPE_CHECKING:
from trezor.messages import ( from trezor.messages import (
PrevTx, PrevTx,
TxInput, TxInput,
TxOutput,
) )
from apps.bitcoin.keychain import Keychain from apps.bitcoin.keychain import Keychain
@ -110,3 +115,20 @@ class Zcash(Bitcoinlike):
write_compact_size(w, 0) # nOutputsSapling write_compact_size(w, 0) # nOutputsSapling
# serialize Orchard bundle # serialize Orchard bundle
write_compact_size(w, 0) # nActionsOrchard write_compact_size(w, 0) # nActionsOrchard
def output_derive_script(self, txo: TxOutput) -> bytes:
# unified addresses
if txo.address is not None and txo.address[0] == "u":
assert txo.script_type is OutputScriptType.PAYTOADDRESS
receivers = unified_addresses.decode(txo.address, self.coin)
if Typecode.P2PKH in receivers:
pubkeyhash = receivers[Typecode.P2PKH]
return scripts.output_script_p2pkh(pubkeyhash)
if Typecode.P2SH in receivers:
scripthash = receivers[Typecode.P2SH]
return scripts.output_script_p2sh(scripthash)
raise DataError("Unified address does not include a transparent receiver.")
# transparent addresses
return super().output_derive_script(txo)

View File

@ -0,0 +1,137 @@
"""
Implementation of encoding and decoding of Zcash
unified addresses according to the ZIP-316.
see: https://zips.z.cash/zip-0316
"""
from typing import TYPE_CHECKING
from trezor.crypto.bech32 import Encoding, bech32_decode, bech32_encode, convertbits
from trezor.utils import BufferReader, empty_bytearray
from trezor.wire import DataError
from apps.common.coininfo import CoinInfo
from apps.common.readers import read_compact_size
from apps.common.writers import write_bytes_fixed, write_compact_size
from .f4jumble import f4jumble, f4unjumble
if TYPE_CHECKING:
from enum import IntEnum
else:
IntEnum = object
class Typecode(IntEnum):
P2PKH = 0x00
P2SH = 0x01
SAPLING = 0x02
ORCHARD = 0x03
def receiver_length(typecode: int) -> int | None:
"""Byte length of a receiver."""
if typecode in (Typecode.P2PKH, Typecode.P2SH):
return 20
if typecode in (Typecode.SAPLING, Typecode.ORCHARD):
return 43
return None
def prefix(coin: CoinInfo) -> str:
"""Prefix for a unified address."""
if coin.coin_name == "Zcash":
return "u"
if coin.coin_name == "Zcash Testnet":
return "utest"
raise ValueError
def padding(hrp: str) -> bytes:
assert len(hrp) <= 16
return hrp.encode() + bytes(16 - len(hrp))
def encode(receivers: dict[Typecode, bytes], coin: CoinInfo) -> str:
# multiple transparent receivers forbidden
assert not (Typecode.P2PKH in receivers and Typecode.P2SH in receivers)
# at least one shielded address must be present
assert Typecode.SAPLING in receivers or Typecode.ORCHARD in receivers
length = 16 # 16 bytes for padding
for receiver_bytes in receivers.values():
length += 2 # typecode (1 byte) + length (1 byte)
length += len(receiver_bytes)
w = empty_bytearray(length)
# receivers in ascending order
receivers_list = list(receivers.items())
receivers_list.sort()
for (typecode, raw_bytes) in receivers_list:
length = receiver_length(typecode) or len(raw_bytes)
write_compact_size(w, typecode)
write_compact_size(w, length)
write_bytes_fixed(w, raw_bytes, length)
hrp = prefix(coin)
write_bytes_fixed(w, padding(hrp), 16)
f4jumble(memoryview(w))
converted = convertbits(w, 8, 5)
return bech32_encode(hrp, converted, Encoding.BECH32M)
def decode(addr_str: str, coin: CoinInfo) -> dict[int, bytes]:
(hrp, data, encoding) = bech32_decode(addr_str, max_bech_len=1000)
if (hrp, data, encoding) == (None, None, None):
raise DataError("Bech32m decoding failed.")
assert hrp is not None # to satisfy typecheckers
assert data is not None # to satisfy typecheckers
assert encoding is not None # to satisfy typecheckers
if hrp != prefix(coin):
raise DataError("Unexpected address prefix.")
if encoding != Encoding.BECH32M:
raise DataError("Bech32m encoding required.")
decoded = bytearray(convertbits(data, 5, 8, False))
f4unjumble(memoryview(decoded))
# check trailing padding bytes
if decoded[-16:] != padding(hrp):
raise DataError("Invalid padding bytes")
r = BufferReader(decoded[:-16])
last_typecode = None
receivers: dict[int, bytes] = dict()
while r.remaining_count() > 0:
typecode = read_compact_size(r)
if typecode in receivers:
raise DataError("Duplicated typecode")
if typecode > 0x02000000:
raise DataError("Invalid typecode")
if last_typecode is not None and typecode < last_typecode:
raise DataError("Invalid receivers order")
last_typecode = typecode
length = read_compact_size(r)
# if the typecode of the receiver is known, then verify receiver length
expected_length = receiver_length(typecode)
if expected_length is not None and length != expected_length:
raise DataError("Unexpected receiver length")
if r.remaining_count() < length:
raise DataError("Invalid receiver length")
receivers[typecode] = r.read(length)
if Typecode.P2PKH in receivers and Typecode.P2SH in receivers:
raise DataError("Multiple transparent receivers")
if len(receivers) == 1:
the_receiver = list(receivers.keys())[0]
if the_receiver in (Typecode.P2PKH, Typecode.P2SH):
raise DataError("Only transparent receiver")
return receivers

View File

@ -0,0 +1,30 @@
from common import *
from apps.zcash.f4jumble import f4jumble, f4unjumble
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
class TestZcashF4jumble(unittest.TestCase):
def test_f4jumble(self):
#source: https://github.com/zcash/librustzcash/blob/main/components/f4jumble/src/test_vectors.rs
TEST_VECTORS = [
{'jumbled': unhexlify('0304d029141b995da5387c125970673504d6c764d91ea6c082123770c7139ccd88ee27368cd0c0921a0444c8e5858d22'),
'normal': unhexlify('5d7a8f739a2d9e945b0ce152a8049e294c4d6e66b164939daffa2ef6ee6921481cdd86b3cc4318d9614fc820905d042b')},
{'jumbled': unhexlify('5271fa3321f3adbcfb075196883d542b438ec6339176537daf859841fe6a56222bff76d1662b5509a9e1079e446eeedd2e683c31aae3ee1851d7954328526be1'),
'normal': unhexlify('b1ef9ca3f24988c7b3534201cfb1cd8dbf69b8250c18ef41294ca97993db546c1fe01f7e9c8e36d6a5e29d4e30a73594bf5098421c69378af1e40f64e125946f')},
{'jumbled': unhexlify('498cf1b1ba6f4577effe64151d67469adc30acc325e326207e7d78487085b4162669f82f02f9774c0cc26ae6e1a76f1e266c6a9a8a2f4ffe8d2d676b1ed71cc47195a3f19208998f7d8cdfc0b74d2a96364d733a62b4273c77d9828aa1fa061588a7c4c88dd3d3dde02239557acfaad35c55854f4541e1a1b3bc8c17076e7316'),
'normal': unhexlify('62c2fa7b2fecbcb64b6968912a6381ce3dc166d56a1d62f5a8d7551db5fd9313e8c7203d996af7d477083756d59af80d06a745f44ab023752cb5b406ed8985e18130ab33362697b0e4e4c763ccb8f676495c222f7fba1e31defa3d5a57efc2e1e9b01a035587d5fb1a38e01d94903d3c3e0ad3360c1d3710acd20b183e31d49f')},
{'jumbled': unhexlify('7508a3a146714f229db91b543e240633ed57853f6451c9db6d64c6e86af1b88b28704f608582c53c51ce7d5b8548827a971d2b98d41b7f6258655902440cd66ee11e84dbfac7d2a43696fd0468810a3d9637c3fa58e7d2d341ef250fa09b9fb71a78a41d389370138a55ea58fcde779d714a04e0d30e61dc2d8be0da61cd684509'),
'normal': unhexlify('25c9a138f49b1a537edcf04be34a9851a7af9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f5431e61ddf658d24ae67c22c8d1309131fc00fe7f235734276d38d47f1e191e00c7a1d48af046827591e9733a97fa6b679f3dc601d008285edcbdae69ce8fc1be4aac00ff2711ebd931de518856878f7')},
{'jumbled': unhexlify('5139912fe8b95492c12731995a0f4478dbeb81ec36653a21bc80d673f3c6a0feef70b6c566f9d34bb726c098648382d105afb19b2b8486b73cbd47a17a0d2d1fd593b14bb9826c5d114b850c6f0cf3083a6f61e38e42713a37ef7997ebd2b376c8a410d797b3932e5a6e39e726b2894ce79604b4ae3c00acaea3be2c1dfe697fa644755102cf9ad78794d0594585494fe38ab56fa6ef3271a68a33481015adf3944c115311421a7dc3ce73ef2abf47e18a6aca7f9dd25a85ce8dbd6f1ad89c8d'),
'normal': unhexlify('3476f21a482ec9378365c8f7393c94e2885315eb4671098b79535e790fe53e29fef2b3766697ac32b4f473f468a008e72389fc03880d780cb07fcfaabe3f1a84b27db59a4a153d882d2b2103596555ed9494c6ac893c49723833ec8926c1039586a7afcf4a0d9c731e985d99589c8bb838e8aaf745533ed9e8ae3a1cd074a51a20da8aba18d1dbebbc862ded42435e92476930d069896cff30eb414f727b89e001afa2fb8dc3436d75a4a6f26572504b192232ecb9f0c02411e52596bc5e9045')}
]
for tv in TEST_VECTORS:
message = memoryview(bytearray(tv["normal"]))
f4jumble(message)
self.assertEqual(bytes(message), tv["jumbled"])
f4unjumble(message)
self.assertEqual(bytes(message), tv["normal"])
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,80 @@
from common import *
from apps.common import coininfo
from apps.zcash import unified_addresses
P2PKH = unified_addresses.Typecode.P2PKH
P2SH = unified_addresses.Typecode.P2SH
SAPLING = unified_addresses.Typecode.SAPLING
ORCHARD = unified_addresses.Typecode.ORCHARD
TESTVECTORS = [
["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/unified_address.py"],
["p2pkh_bytes, p2sh_bytes, sapling_raw_addr, orchard_raw_addr, unknown_typecode, unknown_bytes, unified_addr, root_seed, account, diversifier_index"],
["e6cabf813929132d772d04b03ae85223d03b9be8", None, None, "d4714ee761d1ae823b6972152e20957fefa3f6e3129ea4dfb0a9e98703a63dab929589d6dc51c970f935b3", 65533, "f6ee6921481cdd86b3cc4318d9614fc820905d042bb1ef9ca3f24988c7b3534201cfb1cd8dbf69b8250c18ef41294ca97993db546c1fe0", "753179793677386e336a6d6a73676a39777663656e7238723570366833387679636c686d71307767396b7a70786c7534367a387636346b3567737a72387966777a346a7672796c76766733673633337a30326c756b38356e6d73636b366432736578336e3564376b6e3638687a7a3574763475647439703673793770676c6565756c76676c767832363237646666353771396665703577676478386d3065737832386d307a767578706d7779617a74336a756e3272707177386e75366a326663657167686b353563656436366a73366b366a786e387932787475653866337061716a726b3871366e70746e6e", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0, 0],
["7bec9de217c04f7ce1a86f1fb458aa881c8f39e4", None, None, "d8e5ecb4e005c28718e61a5c336a4f369e771ccdb3363f4f7a04b02a966901a4c05da662d5fd75678f7fb4", 65530, None, "75317a35677538783364766b7677636d726a30716b3568727839706361646c3536683834663777647970366e7635337233643563636365646563686d77393835746765357733633272353639716137326c676775753578727178683739616a7a63376b716d65733230706b747a71726a6c707835367168676d716d3536686e39777432686379787064616d616b", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 1, 0],
["aa6d43480fd9d91375ce6c4a020706361bd296de", None, "88533c398a49c2513dc85162bf220abaf47dc983f14e908ddaaa7322dba16531bc62efe750fe575c8d149b", None, 65530, None, "7531343367706a3772643934766d39356d7a73757537746a74716161677934706d6678386c6b77656d70786a7463777a33357a746361383530796e6c7a323932307477617a6171703270367168787878337a357178616b6e73716372676c7578716a337070757367776635757963686c61677938376b376874613768773965793336776d7930367065776c6470", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 2, 0],
[None, "a8d7551db5fd9313e8c7203d996af7d477083756", "52fd6aedefbf401633c2e4532515ebcf95bcc2b4b8e4d676dfad7e17925c6dfb8671e52544dc2ca075e261", None, 65534, None, "753178797970646a307a7978637466666b6878796d766a6e6b376e383371666c376e7365356c3071726b346e3266376465376c3733727a79787970347463727975356d6b7875617a6c646e633279306479747a7567797a79636739373034616a66786173376b63757761776d706877776e383839743938743735376579716667346a766566746b687672337167", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 3, 0],
[None, "f44ab023752cb5b406ed8985e18130ab33362697", None, "165082de84f2ad7204426ffafd6b6c7de9cab6d25c13846a1786715268c415948db788f4a5e0daa03d699e", 65533, None, "7531706a336c72656d6e7175737368393878667161336a66647077303872726b35377330346b6c32366865707a7133746a72736e78653574367371716567653976716d776c63366c786373746e6333306e3575357232776b6b7a687039367a3564306a797530716137746b686378366663386a35396b616b387a35636570363261716d61336d36343566683863", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 4, 0],
[None, None, None, "ea9df83fbee07d6f7895ebb2ea41ec7c4ba682b863e069b4a438e31c9571c83126c305d75456412aeaef1b", 65531, None, "753132787567643930666c726b646b6575336e6c6e6e337565736b793533707175356d323479366170786d38386d34387637333734636c7335367a7039336e61796c617864636866307161796678747267653034376d393533717a3376326772346c74737232736b3372", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 5, 0],
[None, None, None, "3c40246912b6efefab9a55244ac2c174e1a9f8c0bc0fd526933963c6ecb9b84ec8b0f6b40dc858fa23c72b", 65530, None, "75317370757467353667736a763233637435346d7277646c616e7a7665716337747a73356d78786e616135636465676d303368673778363661797079647336356d39327674397561786c3637327375687063367a3768747776657079686b727066757376617a71756539", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 6, 0],
[None, "defa3d5a57efc2e1e9b01a035587d5fb1a38e01d", None, "cc099cc214e56b1192c7b5b17e958c3413e27fefd553380700aca81b24b2918cac951a1a68017fac525a18", 65535, None, "75317667736b636d3939783567687561757668337978713777747037756e366130793663617964736e6e33357032647577707773356873367079676a6877703738326a716e65727a6c6878773370343971666d713237383339716a7472667976686b377964393877396e3064366a6e7336756834666333687364663736366b6e74716e6c6a646b64353667636e", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 7, 0],
[None, None, None, "5f09a9807a56323b263b05df368dc28391b21a64a0e1b40f9a6803b7e68f3905923f35cb01f119b223f493", 65530, None, "75316378636379656d6d3038747964776d743968703273356e6638776a766c757575366c32653861396a666c6c647861736e7a6b6438667665727170636a30786e767261637a71673235356377356e767936783977727566666d703975657a727a72376763783535396b", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 8, 0],
[None, "10acd20b183e31d49f25c9a138f49b1a537edcf0", "9b60ae3d302248b349d601567e3d7795bfb334ea1fd1a7e71402169ebbe14bd2ceaa244ccd6e5aa2245613", "e340636542ece1c81285ed4eab448adbb5a8c0f4d386eeff337e88e6915f6c3ec1b6ea835a88d56612d2bd", 65531, None, "75317a656b68686d686b353478356365356333367274376e63323735676570376e6176326e73783473683061666c6c75703976726835687338367a38736b6a746436646e736c7667736d6174743068386832343763676e666b73646c776c39786d617275797570666c743064716673637830647979656d3266616139776571653378616b397736656672353437636a3832397232746e7974613032687866647873646a6d76397a72356b746b70323066706378656164686672683032616b346136686e7876357336377267717272766670646a7435", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 9, 0],
[None, "af9db6990ed83dd64af3597c04323ea51b0052ad", None, "cdf7fed0d0822fd849cffb20a4d5ee701ad8141e66d81ddfabf87875117c05092240603c546b8dc187cd8c", 65532, None, "753165353471636e30746570796c33307a7a326672677a37713461366d736e326530326e7076326e6666736433683532336d747838643232616a7666767371757235736a7a3876666e6d77327973363730387170386b6139306a3561343330757938763833616c6a63306330357a6a7535347879356e7677336d66686b376e7737366b6b7964796c713466656c", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 10, 0],
[None, None, None, "24fd59f32b2d39dde66e46c39206a31bc04fa5c6847976ea6bbd3163ee14f58f584acc131479ea558d3f84", 65530, None, "75317a38777372686d66366d3967766136766c33737a636b303670393730783577686d36336a666a3266726d6d63396e39756d34796373387975746a37673833387672676832306c667879353279306832367474386e6776643267796370797176396b793032716b6373", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 11, 0],
[None, None, "78d85bd0db639043377987cdd814c6390016964b684016faf1ad4f166c5f72399a5e8d469ec6beb873d55d", None, 65535, None, "75317861686a333570376d7639756c6b3337327333766465687172663438753077646633786c3772787a7270653461307468753864306d396d7961617078376b35767836747a357074636a76637675346472667137753771777a6d667565336b74387376736333736535", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 12, 0],
["33a6dd87b4d872a4895d345761e4ec423b77928d", None, None, "5178924f7067eac261044ca27ba3cf52f798486973af0795e61587aa1b1ecad333dc520497edc61df88980", 65533, "91e00c7a1d48af046827591e9733a97fa6b679f3dc601d008285edcbdae69ce8fc1be4aac00ff2711ebd931de518856878f73476f21a482ec9378365c8f7393c94e2885315eb4671098b79535e790fe53e29fef2b3766697ac32b4f473f468a008e72389fc03880d780cb07fcfaabe3f1a84b27db59a4a153d882d2b2103596555ed9494c6ac893c49723833ec8926c1", "7531687970706c733364776d616c783373756c746b72397564763237376679716a6478307378716c746638676a6e777976343968743575327270336c6c767632756e796d7330383675616a6b6638393837636175616a7136383670356638687276393474616336663078796637796d7a3636747279366b7936726179336d6a633567786661683030637370766b3564676d67736e3737663274336775763270307861366b6c6138717479376d6b6e6b6d337a68303932306c77733633326166743071686b3532363579736c337067323237747866373461736d7075656e326c746533616a6330667a376b34736878797a656d6e7035773770336b746c6874643030366d6b61787979306d746637646a73646175397a666b657332616e387661687a6737647173677938326330707830396d39683061657a736e7936786c66706767667268656d7661786a3578747871356a6e67763076306167726c3073757079676639636574656a35323779727a7a6574386471747164616771", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 13, 0],
["a56c057ef71dab58aa90e47025695c5faaea5123", None, "a75a6de421d2ad1ee8f4b25e398adda9c0aaa6ab1f2518981a9ddb1de6a3957d77842332d6289dbe94e832", "b208c9235c8d40e49b76100b2d010f3783f12c66e7d3beb117b2c96321b7f6562adb4efc144e39d909e728", 65533, None, "7531646670723876647335683361756e79657a7a7877726d38756461353273743837733876726c676732746730357430713070783336686368783974676b786b6c77747370753332786a6135617271336b7470326e387a613470773779776a30676d68713372776539353072386b3973756e736a76773734743538716c3333347065673464766b616c6b746d6e676e716b7077723332353837653779747932376e6d673636747371377976723779343639776570366b7077346a3530786e6c6d78306a78786737766c6735796c6671387566657664", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 14, 0],
[None, None, None, "9e5445d6cd3cb9f98b0df1062bda47adffd5a66c0c2c483c8bf15c3176d755914a3576496b5c35fee28a88", 65531, None, "75316a676c686a326d617936646674777a39753271796e786a717a6e75743637343768617375306d646d6c63303266636173756178756764797a776a326c38346d6a3966677a6a3779306b396663706a373336736c6d6a38676b37377567386c6c61766367326c666d6d", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 15, 0],
["b02aec10f6fa02a08667bf9b924c3d0574a1334f", None, None, "2598d84dffb34f5908b90732490f3881399150d4c694fce9bf30d1560b2c56f09829fe123b9add20e5d71c", 65534, None, "7531397163617a647761793438707566366a77616a78307732386d307871756d746d6e6435677974796c6c6e79676867396c76393978356d3872387439673566396a307a30786e34787a6d6e7866747a3772746633756164786b79367178706e6b7438666b66686c78386b63396d6e72646c6e7874733536786378656a7a6472776c65787a7637377876797634", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 16, 0],
[None, None, "d3a803803feee7a032a24adfaa8f6a94cecb9671c1333d0d5d1a3d79d82bc310727c665364d71022559c50", "7c98b8f613f9ff02746bea2a167cfd1bd3a1862af9631bf61d9d604e0824e2cb8467a1e549db87a76e7a8a", 65535, None, "75316136346c303971727378756c666a7a6e6d366b326735333575737968746166386564363076346a726a6d6b77766b757834743770647963336e6b7a7265666467746e77383432306c6a3873686d30356a6139667878676e68726139326e6873713536677838633270757a33666b6b676e726b7166357975716664746637743672616e343767646366357676646661637a7766337575793466797368336d7a7538686435746b6c30356d76726765396e38", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 17, 0],
["26c061d67beb8bad48c6b4774a156551e30e4fe2", None, None, "a80405d5568ab8ab8f8546163d951ab297fd5e6f43e7fcebcb664feacfab5afd80aaf7f354c07a9901788c", 65535, None, "7531787a757764386163686667776d336577793976326d6a3537373268726b6e6d6578777a6339346d7a6133356d78363863656e767877727a3973396670306e39767a753872756a357a71666d6d376c65387775366c363275346c6d30376e75717865656d383733677838366a766e776c70787379636c397576366b786b72686d30726c677037307830357366", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 18, 0],
[None, None, "8660070e3757ff6507060791fd694f6a631b8495a2b74ffa39236cf653caea5575b86af3200b010e513bab", "63b7b706d991169986aee56133f0a50b2a0c8225fba6dae95176007b1f023a1e97c1aa366e99bf970fda82", 65534, None, "7531766736326d676a64646e6c763577366c646b793278653063387465746d633832747539766c7a7a6b75796e783439666e75716a76786a743564676e33636d3874356e38357a6371356c6a727467377a6d77686b3730683672646d636c6637736378786e67756b35666c76663261707037367875393037636d6a796c787673656e3235786539763776336b727378613975793076326a6a7133376b6834796d6c61666e3870657671616c716134646d3637", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 19, 5]
]
class ZcashTestVector:
def __init__(self, inner):
self.inner = inner
def __getattr__(self, name):
index = TESTVECTORS[1][0].split(", ").index(name)
return self.inner[index]
def get_receivers(tv: ZcashTestVector):
receivers = dict()
if tv.p2pkh_bytes is not None:
receivers[P2PKH] = unhexlify(tv.p2pkh_bytes)
if tv.p2sh_bytes is not None:
receivers[P2SH] = unhexlify(tv.p2sh_bytes)
if tv.sapling_raw_addr is not None:
receivers[SAPLING] = unhexlify(tv.sapling_raw_addr)
if tv.orchard_raw_addr is not None:
receivers[ORCHARD] = unhexlify(tv.orchard_raw_addr)
if tv.unknown_bytes is not None:
receivers[tv.unknown_typecode] = unhexlify(tv.unknown_bytes)
return receivers
COIN = coininfo.by_name("Zcash")
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
class TestZcashAddress(unittest.TestCase):
def test_encode_unified(self):
for tv in map(ZcashTestVector, TESTVECTORS[2:]):
receivers = get_receivers(tv)
ua = unified_addresses.encode(receivers, COIN)
self.assertEqual(ua, unhexlify(tv.unified_addr).decode())
def test_decode_unified(self):
for tv in map(ZcashTestVector, TESTVECTORS[2:]):
address = unhexlify(tv.unified_addr).decode()
receivers = unified_addresses.decode(address, COIN)
self.assertEqual(receivers, get_receivers(tv))
if __name__ == '__main__':
unittest.main()

View File

@ -271,6 +271,63 @@ def test_one_two(client: Client):
) )
@pytest.mark.skip_t1
def test_unified_address(client: Client):
# identical to the test_one_two
# but receiver address is unified with an orchard address
inp1 = messages.TxInputType(
# tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu
address_n=parse_path("m/44h/1h/0h/0/0"),
amount=4_134_720,
prev_hash=TXHASH_c5309b,
prev_index=0,
)
out1 = messages.TxOutputType(
# p2pkh: m/44h/1h/0h/0/8 + orchard: m/32h/1h/0h
address="utest1xt8k2akcdnncjfz8sfxkm49quc4w627skp3qpggkwp8c8ay3htftjf7tur9kftcw0w4vu4scwfg93ckfag84khy9k40yanl5k0qkanh9cyhddgws786qeqn37rtyf6rx4eflz09zk06",
amount=1_000_000,
script_type=messages.OutputScriptType.PAYTOADDRESS,
)
out2 = messages.TxOutputType(
address_n=parse_path("m/44h/1h/0h/0/0"),
amount=4_134_720 - 1_000_000 - 2_000,
script_type=messages.OutputScriptType.PAYTOADDRESS,
)
with client:
client.set_expected_responses(
[
request_input(0),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
request_output(1),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_output(0),
request_output(1),
request_finished(),
]
)
_, serialized_tx = btc.sign_tx(
client,
"Zcash Testnet",
[inp1],
[out1, out2],
version=5,
version_group_id=VERSION_GROUP_ID,
branch_id=BRANCH_ID,
)
# Accepted by network: txid = d8cfa377012ca0b8d856586693b530835bf2fa14add0380e24ec6755bed5b931
assert (
serialized_tx.hex()
== "050000800a27a726b4d0d6c2000000000000000001bf79ce8a0403de4775d3538c670de802af72e8961c8b9174f36b8fa1d69b30c5000000006b483045022100be78eccf801dda4dd33f9d4e04c2aae01022869d1d506d51669204ec269d71a90220394a51838faf40176058cf45fe7032be9c5c942e21aff35d7dbe4b96ab5e0a500121030e669acac1f280d1ddf441cd2ba5e97417bf2689e4bbec86df4f831bf9f7ffd0ffffffff0240420f00000000001976a9141efeae5c937bfc7f095a06aabdb5476a5d6d19db88ac30cd2f00000000001976a914a579388225827d9f2fe9014add644487808c695d88ac000000"
)
@pytest.mark.skip_t1 @pytest.mark.skip_t1
def test_external_presigned(client: Client): def test_external_presigned(client: Client):
inp1 = messages.TxInputType( inp1 = messages.TxInputType(

View File

@ -1659,6 +1659,7 @@
"TT_zcash-test_sign_tx.py::test_spend_multisig": "1f7cfe70831cffb4c076d0b4778704a982b37be4cc114e2cd9de85ee3173430e", "TT_zcash-test_sign_tx.py::test_spend_multisig": "1f7cfe70831cffb4c076d0b4778704a982b37be4cc114e2cd9de85ee3173430e",
"TT_zcash-test_sign_tx.py::test_spend_v4_input": "235910c3aa36150bb7012d21d52acdca347a2a216bb09a0588910329e464c8f0", "TT_zcash-test_sign_tx.py::test_spend_v4_input": "235910c3aa36150bb7012d21d52acdca347a2a216bb09a0588910329e464c8f0",
"TT_zcash-test_sign_tx.py::test_spend_v5_input": "2ec2223bdd77dc9e4cbfe1c85ed91dec77b7e6f1f37dd1cb160308808db9d9a6", "TT_zcash-test_sign_tx.py::test_spend_v5_input": "2ec2223bdd77dc9e4cbfe1c85ed91dec77b7e6f1f37dd1cb160308808db9d9a6",
"TT_zcash-test_sign_tx.py::test_unified_address": "5646679f6bb2a58446921e838fce39f116f8bd026bde2c453673b9361cdcd779",
"TT_zcash-test_sign_tx.py::test_version_group_id_missing": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "TT_zcash-test_sign_tx.py::test_version_group_id_missing": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1",
"TTui2_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "971ffb5a1a9a39e5308efd89f51be986e8143608d1c6638b1171c02011abb141", "TTui2_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "971ffb5a1a9a39e5308efd89f51be986e8143608d1c6638b1171c02011abb141",
"TTui2_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-1-bnb1egswqkszzfc2uq7-1adfb691": "2547b2dc528bc3090219f13286d832939f3a5a89c6f1f219a60658ba00b83bdd", "TTui2_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-1-bnb1egswqkszzfc2uq7-1adfb691": "2547b2dc528bc3090219f13286d832939f3a5a89c6f1f219a60658ba00b83bdd",