feat(core): add support for Zcash unified addresses

pull/2478/head
Tomas Krnak 2 years ago committed by Andrew Kozlik
parent 054b5456d2
commit 031bac4a9b

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

@ -609,6 +609,7 @@ if FROZEN:
SOURCE_PY_DIR + 'trezor/enums/Ripple*.py',
SOURCE_PY_DIR + 'trezor/enums/Stellar*.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/bitcoinlike.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)

@ -565,6 +565,7 @@ if FROZEN:
SOURCE_PY_DIR + 'trezor/enums/Ripple*.py',
SOURCE_PY_DIR + 'trezor/enums/Stellar*.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/bitcoinlike.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)

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

@ -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)

@ -1,15 +1,19 @@
from micropython import const
from typing import TYPE_CHECKING
from trezor.enums import OutputScriptType
from trezor.messages import SignTx
from trezor.utils import ensure
from trezor.wire import DataError, ProcessError
from apps.bitcoin import scripts
from apps.bitcoin.common import ecdsa_sign
from apps.bitcoin.sign_tx.bitcoinlike import Bitcoinlike
from apps.common.writers import write_compact_size, write_uint32_le
from . import unified_addresses
from .hasher import ZcashHasher
from .unified_addresses import Typecode
if TYPE_CHECKING:
from typing import Sequence
@ -21,6 +25,7 @@ if TYPE_CHECKING:
from trezor.messages import (
PrevTx,
TxInput,
TxOutput,
)
from apps.bitcoin.keychain import Keychain
@ -110,3 +115,20 @@ class Zcash(Bitcoinlike):
write_compact_size(w, 0) # nOutputsSapling
# serialize Orchard bundle
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)

@ -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

@ -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()

@ -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()

@ -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
def test_external_presigned(client: Client):
inp1 = messages.TxInputType(

@ -1659,6 +1659,7 @@
"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_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",
"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",

Loading…
Cancel
Save