From 59400e0fa074c346797294b9e00a63609ad6b1be Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 21:05:18 -0500 Subject: [PATCH 01/37] test(zcash): sign real note ciphertexts; refuse a corrupted one Firmware now decrypts each Orchard output, so the fixture is the zcash-test-vectors note 0 with its real ciphertext (Ironwood V3 too). --- tests/test_msg_zcash_sign_pczt_device.py | 125 +++++++++++++++++------ 1 file changed, 93 insertions(+), 32 deletions(-) diff --git a/tests/test_msg_zcash_sign_pczt_device.py b/tests/test_msg_zcash_sign_pczt_device.py index 0dfff364..888cd253 100644 --- a/tests/test_msg_zcash_sign_pczt_device.py +++ b/tests/test_msg_zcash_sign_pczt_device.py @@ -11,12 +11,11 @@ came from a shielded flow, which is how a confirm that could not physically fit its amount line shipped unnoticed. -The note fixtures are the known-answer vectors from -unittests/firmware/zcash.cpp (OrchardNoteCommitment_KnownVectorAndProgress, -IronwoodNoteCommitment_V3KnownVector, OrchardReceiverToUnifiedAddress_KnownVector), -so the device's own cmx recomputation accepts them. Same note under both pools, -with a different commitment each -- which is what lets us prove the device -actually honours shielded_pool instead of ignoring it. +The note fixture is note 0 of the zcash-test-vectors Orchard note-encryption +vectors, with a real ciphertext, so the device's cmx recomputation and its +note-ciphertext check both accept it. Same note under both pools, with a +different commitment and ciphertext each -- which is what lets us prove the +device actually honours shielded_pool instead of ignoring it. """ import hashlib @@ -34,26 +33,70 @@ H = 0x80000000 ADDRESS_N = [H + 32, H + 133, H] -# --- known-answer note, from unittests/firmware/zcash.cpp ------------------- +# --- note 0 of zcash-test-vectors orchard_note_encryption.py --------------- +# As carried by the orchard 0.15.4 crate (src/test_vectors/note_encryption.rs). +# The device recomputes cmx AND decrypts the note ciphertext, so every field +# below must be a real encryption of this note; rho is the vector's nf_old. +# The Ironwood (V3, lead byte 0x03) cmx and ciphertext re-encrypt the same note +# with orchard 0.15.4's IronwoodNoteEncryption; epk is unchanged. RECIPIENT = bytes.fromhex( - '3c150e6098b861716cc7f62835f69feb302193c92660444f26624fd13e00ea7a' - 'c774cd55074d6367efef37') # 43 bytes -RHO = bytes.fromhex( - '112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00') -RSEED = bytes.fromhex( - 'cafebabedeadbeef0102030405060708090a0b0c0d0e0f101112131415161718') -VALUE = 12345678 - -CMX_ORCHARD = bytes.fromhex( - '02defb39c8f2e1ecc945189373cf2a8e21d4e154398efa1621d5fb989e1deb36') -CMX_IRONWOOD = bytes.fromhex( - '896ee345d8b0409872172537666a482409661a22ad77c09896a3e71765f18633') - -# OrchardReceiverToUnifiedAddress_KnownVector. 106 characters -- three full -# body rows on their own, which is the entire reason the confirm needs two -# screens instead of one. -EXPECTED_UA = ('u1ut4h93zg5670tyqss7tneru3t7h6dk62r9hhyxyrpv3nwwe9dnyj5l0ruwygf' - '74gp5f3zklj5xly4h8h54un3asugt9mn6gwfqsq3wq7') + '56e84b1adc9423c3676c0463f7125df4836fd2816b024ee70efe09fb9a7b3863' + 'c6eacdf95e03894950692c') # d || pk_d, 43 bytes +RHO = bytes.fromhex('c596fbd32ebbcbadae60d285c7d75fa836f9d2fa86100ab858ea2de1f11c8306') +RSEED = bytes.fromhex('bf69b8250c18ef41294ca97993db546c1fe01f7e9c8e36d6a5e29d4e30a73594') +VALUE = 8567075990963576717 + +CMX_ORCHARD = bytes.fromhex('a5706f3d1b688e9dc634eee4e65b028a43eeaed2435bea2ae3d5160575c11a3b') +CMX_IRONWOOD = bytes.fromhex('07a8d0edab5130bcdb265d8efed333514c2c9b180df70ba3963ca8fc1166bd14') +EPK = bytes.fromhex('addb47b6ac5dfc16558923d3a8f376095c695c047c4e3266ae676987f7e31381') +# enc_ciphertext = compact (52) || memo (512) || AEAD tag (16). +C_ENC_ORCHARD = bytes.fromhex( + '1a9adb142498e3dcc76fed778614dd316c02fbb8ba9244ae4c2e32a07daeeca4' + '1226b98bfe74f9fcb228cfc100f3180f5775ece38be7ed45d94021f4401b2a4d' + '7582b428d49ec7f5b5a498973e60e38e74f5c3e577827c382857d8166b54e64f' + '66ef5c7e8c9baa2a3fa9e37d087717d5e96bc2f73d031450dc2432ba49d8b74d' + 'b213099ea9ba04eb63b6574d46c03ce7900d4ac4bb188ee9030d7f69c895a94f' + 'c182f225a94f0cde1b49886871a376341ea94171befd95a830fa18407097dca5' + '11025463d437e9695caa079a2f68cdc7f2c13267bff4195137fa8953252a81b2' + 'afa1582b9bfb4ac96037ed2991d3cbc7d54aff6e621b06a7b2b9caf2955efaf4' + 'ea8efcfd023a3c1748df3cbd43e0b9a8b0945688d52056c1d16eea37e798ba31' + 'dc3e5d4952bd51ec769d5788b6e35fe9042b95d4d21781400eaff58616ad5627' + '96636a50b8ed6c7f981dc7ba814eff152cb228a2ead2f832662fa4a4a50797b0' + 'f85b62d08b1dd2d8e43b4a5bfbb159ed578ef7475de0ada13e17ad87cc230567' + '2bcc55a8881317fdc1bfc459b68b2df70cad3770ed0fd02d64b96f2bbf6f8f63' + '2e866ca5d196d248ad05c3de644148a80b51ada95bd08d73cdbb45264f3bd113' + '835b46f9be7b6d23a43bddfe1e7408c97031e1a8214bab46391044b700d38f51' + '92c57fe6f87159b55512094e29d2cebab868c8f1adbad57077cbeb5e69658582' + 'bf98d19d64f44b0d50c7e2209ab3fc56b4f409123aaeb0263a22451bc14ed756' + 'd048385aedbb86a84677bb2d21c52cc9494147bf0fb102745282990909726228' + '186e02c8') +C_ENC_IRONWOOD = bytes.fromhex( + '1b9adb142498e3dcc76fed778614dd316c02fbb8ba9244ae4c2e32a07daeeca4' + '1226b98bfe74f9fcb228cfc100f3180f5775ece38be7ed45d94021f4401b2a4d' + '7582b428d49ec7f5b5a498973e60e38e74f5c3e577827c382857d8166b54e64f' + '66ef5c7e8c9baa2a3fa9e37d087717d5e96bc2f73d031450dc2432ba49d8b74d' + 'b213099ea9ba04eb63b6574d46c03ce7900d4ac4bb188ee9030d7f69c895a94f' + 'c182f225a94f0cde1b49886871a376341ea94171befd95a830fa18407097dca5' + '11025463d437e9695caa079a2f68cdc7f2c13267bff4195137fa8953252a81b2' + 'afa1582b9bfb4ac96037ed2991d3cbc7d54aff6e621b06a7b2b9caf2955efaf4' + 'ea8efcfd023a3c1748df3cbd43e0b9a8b0945688d52056c1d16eea37e798ba31' + 'dc3e5d4952bd51ec769d5788b6e35fe9042b95d4d21781400eaff58616ad5627' + '96636a50b8ed6c7f981dc7ba814eff152cb228a2ead2f832662fa4a4a50797b0' + 'f85b62d08b1dd2d8e43b4a5bfbb159ed578ef7475de0ada13e17ad87cc230567' + '2bcc55a8881317fdc1bfc459b68b2df70cad3770ed0fd02d64b96f2bbf6f8f63' + '2e866ca5d196d248ad05c3de644148a80b51ada95bd08d73cdbb45264f3bd113' + '835b46f9be7b6d23a43bddfe1e7408c97031e1a8214bab46391044b700d38f51' + '92c57fe6f87159b55512094e29d2cebab868c8f1adbad57077cbeb5e69658582' + 'bf98d19d64f44b0d50c7e2209ab3fc56b4f409123aaeb0263a22451bc14ed756' + 'd048385aedbb86a84677bb2d21c52cc9494147bfa197dd4326a43baa347044f2' + 'bdb49861') + +# RECIPIENT as an Orchard-only mainnet unified address, encoded with +# librustzcash's zcash_address 0.13.0. 106 characters -- three full body rows +# on their own, which is the entire reason the confirm needs two screens +# instead of one. +EXPECTED_UA = ('u17j4lvw84jd238ev9ukr0lvqhv4z32v98pxcglctaj3aqfqj7rr2wwvh73247ek' + 'czw4smyrvm2wf2v5nfxvn3sl0ycc6w4455yg49yf2m') ORCHARD_TX = dict(tx_version=5, version_group_id=0x26A7270A, branch_id=0x5437F330) IRONWOOD_TX = dict(tx_version=6, version_group_id=0xD884B698, branch_id=0x37A5165B) @@ -100,7 +143,8 @@ def bundle_digest(actions, ironwood, tx_version, return _b2b(pb, body) -def note_action(cmx, recipient=RECIPIENT, value=VALUE, rseed=RSEED): +def note_action(cmx, recipient=RECIPIENT, value=VALUE, rseed=RSEED, + c_enc=C_ENC_ORCHARD): """One action carrying real output metadata. Every field is size-checked by the firmware (fsm_msg_zcash.h:1068-1088) and @@ -112,10 +156,10 @@ def note_action(cmx, recipient=RECIPIENT, value=VALUE, rseed=RSEED): 'alpha': b'\x01' * 32, 'nullifier': RHO, # the firmware feeds this in as rho 'cmx': cmx, - 'epk': b'\x02' * 32, - 'enc_compact': b'\x03' * 52, - 'enc_memo': b'\x04' * 512, - 'enc_noncompact': b'\x05' * 16, + 'epk': EPK, + 'enc_compact': c_enc[:52], + 'enc_memo': c_enc[52:564], + 'enc_noncompact': c_enc[564:], 'cv_net': b'\x06' * 32, 'rk': b'\x07' * 32, 'out_ciphertext': b'\x08' * 80, @@ -293,6 +337,23 @@ def test_note_commitment_binds_the_recipient(self): self.client.zcash_sign_pczt(**sign_kwargs(actions)) self.assertIn('commitment mismatch', str(caught.exception)) + def test_corrupted_note_ciphertext_is_refused(self): + """A note the recipient cannot decrypt must not be signed. + + The commitment alone does not reach the recipient: a host could keep a + valid cmx and corrupt the ciphertext, recomputing the bundle digest to + match, and the payment would be invisible to normal wallet scanning. + The device decrypts with the key derived from rseed and rho and must + refuse before the output screens. + """ + c_enc = bytearray(C_ENC_ORCHARD) + c_enc[52 + 100] ^= 0x01 # one memo bit + actions = [note_action(CMX_ORCHARD, c_enc=bytes(c_enc))] + + with self.assertRaises(Exception) as caught: + self.client.zcash_sign_pczt(**sign_kwargs(actions)) + self.assertIn('ciphertext mismatch', str(caught.exception)) + # Canonical release/7.15 includes Ironwood handlers and the corresponding # native commitment vectors. These regressions apply to that product. IRONWOOD_FIRMWARE = "7.15.0" @@ -327,7 +388,7 @@ def test_ironwood_rejects_a_non_empty_orchard_bundle(self): Orchard bundle's valueBalance never enters the device's fee check. """ self.requires_firmware(self.IRONWOOD_FIRMWARE) - actions = [note_action(CMX_IRONWOOD)] + actions = [note_action(CMX_IRONWOOD, c_enc=C_ENC_IRONWOOD)] kwargs = sign_kwargs(actions, ironwood=True) # Anything but the ZIP-229 v6 empty-bundle digest must be refused. kwargs['orchard_digest'] = bytes([0x11]) * 32 @@ -343,7 +404,7 @@ def test_ironwood_note_is_accepted(self): selected by shielded_pool rather than one path serving both. """ self.requires_firmware(self.IRONWOOD_FIRMWARE) - actions = [note_action(CMX_IRONWOOD)] + actions = [note_action(CMX_IRONWOOD, c_enc=C_ENC_IRONWOOD)] screens = self._capture_button_screens() result = self.client.zcash_sign_pczt(**sign_kwargs(actions, ironwood=True)) From c0345cc2c476554823683aad4e4d49f41cb55a71 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 21:17:29 -0500 Subject: [PATCH 02/37] deps: device-protocol with the tron-trc20-review capability Bindings regenerated with protoc 3.5.1, as before; the descriptor differs only by Features.Capability CAPABILITY_TRON_TRC20_REVIEW = 25. --- device-protocol | 2 +- keepkeylib/messages_pb2.py | 286 +++++++++++++++++++------------------ 2 files changed, 146 insertions(+), 142 deletions(-) diff --git a/device-protocol b/device-protocol index 70bbbbf4..13ce81a4 160000 --- a/device-protocol +++ b/device-protocol @@ -1 +1 @@ -Subproject commit 70bbbbf4c1174245278dd4ce1dff8dbe41ca40dd +Subproject commit 13ce81a415e9d0023f4a7e6a4e4101eb9d1dd55e diff --git a/keepkeylib/messages_pb2.py b/keepkeylib/messages_pb2.py index 30aaf84b..f9365e9c 100644 --- a/keepkeylib/messages_pb2.py +++ b/keepkeylib/messages_pb2.py @@ -21,7 +21,7 @@ name='messages.proto', package='', syntax='proto2', - serialized_pb=_b('\n\x0emessages.proto\x1a\x0btypes.proto\"\x0c\n\nInitialize\"\r\n\x0bGetFeatures\"\xf1\x0c\n\x08\x46\x65\x61tures\x12\x0e\n\x06vendor\x18\x01 \x01(\t\x12\x15\n\rmajor_version\x18\x02 \x01(\r\x12\x15\n\rminor_version\x18\x03 \x01(\r\x12\x15\n\rpatch_version\x18\x04 \x01(\r\x12\x17\n\x0f\x62ootloader_mode\x18\x05 \x01(\x08\x12\x11\n\tdevice_id\x18\x06 \x01(\t\x12\x16\n\x0epin_protection\x18\x07 \x01(\x08\x12\x1d\n\x15passphrase_protection\x18\x08 \x01(\x08\x12\x10\n\x08language\x18\t \x01(\t\x12\r\n\x05label\x18\n \x01(\t\x12\x18\n\x05\x63oins\x18\x0b \x03(\x0b\x32\t.CoinType\x12\x13\n\x0binitialized\x18\x0c \x01(\x08\x12\x10\n\x08revision\x18\r \x01(\x0c\x12\x17\n\x0f\x62ootloader_hash\x18\x0e \x01(\x0c\x12\x10\n\x08imported\x18\x0f \x01(\x08\x12\x12\n\npin_cached\x18\x10 \x01(\x08\x12\x19\n\x11passphrase_cached\x18\x11 \x01(\x08\x12\x1d\n\x08policies\x18\x12 \x03(\x0b\x32\x0b.PolicyType\x12\r\n\x05model\x18\x15 \x01(\t\x12\x18\n\x10\x66irmware_variant\x18\x16 \x01(\t\x12\x15\n\rfirmware_hash\x18\x17 \x01(\x0c\x12\x11\n\tno_backup\x18\x18 \x01(\x08\x12\x1c\n\x14wipe_code_protection\x18\x19 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x1a \x01(\r\x12\x18\n\x10supports_taproot\x18\x1b \x01(\x08\x12\x1b\n\x13supports_dice_modes\x18\x1c \x01(\x08\x12\'\n\x1fsupports_solana_lut_attestation\x18\x1d \x01(\x08\x12.\n\x0c\x63\x61pabilities\x18\x1e \x03(\x0e\x32\x14.Features.CapabilityB\x02\x10\x01\"\xb4\x07\n\nCapability\x12\x1a\n\x16\x43\x41PABILITY_UNSPECIFIED\x10\x00\x12#\n\x1f\x43\x41PABILITY_ENTROPY_AUDIT_BUDGET\x10\x01\x12-\n)CAPABILITY_ERC20_UNLIMITED_APPROVE_REVIEW\x10\x02\x12,\n(CAPABILITY_ERC20_UNLIMITED_PERMIT_REVIEW\x10\x03\x12%\n!CAPABILITY_ERC7730_RUNTIME_REVIEW\x10\x04\x12#\n\x1f\x43\x41PABILITY_EVM_CERTIFIED_INTENT\x10\x05\x12$\n CAPABILITY_EVM_MAX_AMOUNT_REVIEW\x10\x06\x12\x1e\n\x1a\x43\x41PABILITY_EVM_TX_METADATA\x10\x07\x12\'\n#CAPABILITY_EVM_UNKNOWN_TOKEN_REVIEW\x10\x08\x12\"\n\x1e\x43\x41PABILITY_HIVE_RELEASE_REVIEW\x10\t\x12(\n$CAPABILITY_LEGACY_EVM_ROUTER_SIGNING\x10\n\x12\"\n\x1e\x43\x41PABILITY_MAYA_SINGLE_MESSAGE\x10\x0b\x12\"\n\x1e\x43\x41PABILITY_OSMOSIS_WIRE_GUARDS\x10\x0c\x12\x1d\n\x19\x43\x41PABILITY_PERMIT2_REVIEW\x10\r\x12%\n!CAPABILITY_PROMPT_WORKFLOW_UNWIND\x10\x0e\x12&\n\"CAPABILITY_PROTECTED_PING_PRESENCE\x10\x0f\x12!\n\x1d\x43\x41PABILITY_RIPPLE_MEMO_POLICY\x10\x10\x12%\n!CAPABILITY_SESSION_TRUST_LIFETIME\x10\x11\x12&\n\"CAPABILITY_SOLANA_CERTIFIED_REVIEW\x10\x12\x12%\n!CAPABILITY_SOLANA_LUT_ATTESTATION\x10\x13\x12$\n CAPABILITY_SOLANA_RUNTIME_REVIEW\x10\x14\x12\x1e\n\x1a\x43\x41PABILITY_STORAGE_V19_KDF\x10\x15\x12\"\n\x1e\x43\x41PABILITY_TENDERMINT_PROGRESS\x10\x16\x12\"\n\x1e\x43\x41PABILITY_SAFE_RESET_CEREMONY\x10\x17\x12\"\n\x1e\x43\x41PABILITY_THOR_DEPOSIT_REVIEW\x10\x18\"*\n\x0cGetCoinTable\x12\r\n\x05start\x18\x01 \x01(\r\x12\x0b\n\x03\x65nd\x18\x02 \x01(\r\"L\n\tCoinTable\x12\x18\n\x05table\x18\x01 \x03(\x0b\x32\t.CoinType\x12\x11\n\tnum_coins\x18\x02 \x01(\r\x12\x12\n\nchunk_size\x18\x03 \x01(\r\"\x0e\n\x0c\x43learSession\"y\n\rApplySettings\x12\x10\n\x08language\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x16\n\x0euse_passphrase\x18\x03 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x04 \x01(\r\x12\x13\n\x0bu2f_counter\x18\x05 \x01(\r\"\x1b\n\tChangePin\x12\x0e\n\x06remove\x18\x01 \x01(\x08\"\x87\x01\n\x04Ping\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x19\n\x11\x62utton_protection\x18\x02 \x01(\x08\x12\x16\n\x0epin_protection\x18\x03 \x01(\x08\x12\x1d\n\x15passphrase_protection\x18\x04 \x01(\x08\x12\x1c\n\x14wipe_code_protection\x18\x05 \x01(\x08\"\x1a\n\x07Success\x12\x0f\n\x07message\x18\x01 \x01(\t\"6\n\x07\x46\x61ilure\x12\x1a\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x0c.FailureType\x12\x0f\n\x07message\x18\x02 \x01(\t\"?\n\rButtonRequest\x12 \n\x04\x63ode\x18\x01 \x01(\x0e\x32\x12.ButtonRequestType\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x0b\n\tButtonAck\"7\n\x10PinMatrixRequest\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.PinMatrixRequestType\"\x1b\n\x0cPinMatrixAck\x12\x0b\n\x03pin\x18\x01 \x02(\t\"\x08\n\x06\x43\x61ncel\"\x13\n\x11PassphraseRequest\"#\n\rPassphraseAck\x12\x12\n\npassphrase\x18\x01 \x02(\t\"\x1a\n\nGetEntropy\x12\x0c\n\x04size\x18\x01 \x02(\r\"\x1a\n\x07\x45ntropy\x12\x0f\n\x07\x65ntropy\x18\x01 \x02(\x0c\"\xa2\x01\n\x0cGetPublicKey\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x18\n\x10\x65\x63\x64sa_curve_name\x18\x02 \x01(\t\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\x12\x1a\n\tcoin_name\x18\x04 \x01(\t:\x07\x42itcoin\x12\x33\n\x0bscript_type\x18\x05 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"4\n\tPublicKey\x12\x19\n\x04node\x18\x01 \x02(\x0b\x32\x0b.HDNodeType\x12\x0c\n\x04xpub\x18\x02 \x01(\t\"\xb3\x01\n\nGetAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x1a\n\tcoin_name\x18\x02 \x01(\t:\x07\x42itcoin\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\x12+\n\x08multisig\x18\x04 \x01(\x0b\x32\x19.MultisigRedeemScriptType\x12\x33\n\x0bscript_type\x18\x05 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"\x1a\n\x07\x41\x64\x64ress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x02(\t\"\x0c\n\nWipeDevice\"\xbb\x01\n\nLoadDevice\x12\x10\n\x08mnemonic\x18\x01 \x01(\t\x12\x19\n\x04node\x18\x02 \x01(\x0b\x32\x0b.HDNodeType\x12\x0b\n\x03pin\x18\x03 \x01(\t\x12\x1d\n\x15passphrase_protection\x18\x04 \x01(\x08\x12\x19\n\x08language\x18\x05 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x06 \x01(\t\x12\x15\n\rskip_checksum\x18\x07 \x01(\x08\x12\x13\n\x0bu2f_counter\x18\x08 \x01(\r\"\x8a\x02\n\x0bResetDevice\x12\x16\n\x0e\x64isplay_random\x18\x01 \x01(\x08\x12\x15\n\x08strength\x18\x02 \x01(\r:\x03\x32\x35\x36\x12\x1d\n\x15passphrase_protection\x18\x03 \x01(\x08\x12\x16\n\x0epin_protection\x18\x04 \x01(\x08\x12\x19\n\x08language\x18\x05 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x06 \x01(\t\x12\x11\n\tno_backup\x18\x07 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x08 \x01(\r\x12\x13\n\x0bu2f_counter\x18\t \x01(\r\x12\x14\n\x0c\x64ice_entropy\x18\n \x01(\x08\x12\x11\n\tdice_only\x18\x0b \x01(\x08\"\x10\n\x0e\x45ntropyRequest\"\x1d\n\nEntropyAck\x12\x0f\n\x07\x65ntropy\x18\x01 \x01(\x0c\"\xff\x01\n\x0eRecoveryDevice\x12\x12\n\nword_count\x18\x01 \x01(\r\x12\x1d\n\x15passphrase_protection\x18\x02 \x01(\x08\x12\x16\n\x0epin_protection\x18\x03 \x01(\x08\x12\x19\n\x08language\x18\x04 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x05 \x01(\t\x12\x18\n\x10\x65nforce_wordlist\x18\x06 \x01(\x08\x12\x1c\n\x14use_character_cipher\x18\x07 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x08 \x01(\r\x12\x13\n\x0bu2f_counter\x18\t \x01(\r\x12\x0f\n\x07\x64ry_run\x18\n \x01(\x08\"\r\n\x0bWordRequest\"\x17\n\x07WordAck\x12\x0c\n\x04word\x18\x01 \x02(\t\";\n\x10\x43haracterRequest\x12\x10\n\x08word_pos\x18\x01 \x02(\r\x12\x15\n\rcharacter_pos\x18\x02 \x02(\r\"?\n\x0c\x43haracterAck\x12\x11\n\tcharacter\x18\x01 \x01(\t\x12\x0e\n\x06\x64\x65lete\x18\x02 \x01(\x08\x12\x0c\n\x04\x64one\x18\x03 \x01(\x08\"\x82\x01\n\x0bSignMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07message\x18\x02 \x02(\x0c\x12\x1a\n\tcoin_name\x18\x03 \x01(\t:\x07\x42itcoin\x12\x33\n\x0bscript_type\x18\x04 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"`\n\rVerifyMessage\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\x12\x1a\n\tcoin_name\x18\x04 \x01(\t:\x07\x42itcoin\"6\n\x10MessageSignature\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"v\n\x0e\x45ncryptMessage\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\x12\x14\n\x0c\x64isplay_only\x18\x03 \x01(\x08\x12\x11\n\taddress_n\x18\x04 \x03(\r\x12\x1a\n\tcoin_name\x18\x05 \x01(\t:\x07\x42itcoin\"@\n\x10\x45ncryptedMessage\x12\r\n\x05nonce\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\x12\x0c\n\x04hmac\x18\x03 \x01(\x0c\"Q\n\x0e\x44\x65\x63ryptMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\r\n\x05nonce\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\x12\x0c\n\x04hmac\x18\x04 \x01(\x0c\"4\n\x10\x44\x65\x63ryptedMessage\x12\x0f\n\x07message\x18\x01 \x01(\x0c\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"\x8c\x01\n\x0e\x43ipherKeyValue\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\x0c\x12\x0f\n\x07\x65ncrypt\x18\x04 \x01(\x08\x12\x16\n\x0e\x61sk_on_encrypt\x18\x05 \x01(\x08\x12\x16\n\x0e\x61sk_on_decrypt\x18\x06 \x01(\x08\x12\n\n\x02iv\x18\x07 \x01(\x0c\"!\n\x10\x43ipheredKeyValue\x12\r\n\x05value\x18\x01 \x01(\x0c\"5\n\x10GetBip85Mnemonic\x12\x12\n\nword_count\x18\x01 \x02(\r\x12\r\n\x05index\x18\x02 \x02(\r\"!\n\rBip85Mnemonic\x12\x10\n\x08mnemonic\x18\x01 \x02(\t\"\xce\x01\n\x06SignTx\x12\x15\n\routputs_count\x18\x01 \x02(\r\x12\x14\n\x0cinputs_count\x18\x02 \x02(\r\x12\x1a\n\tcoin_name\x18\x03 \x01(\t:\x07\x42itcoin\x12\x12\n\x07version\x18\x04 \x01(\r:\x01\x31\x12\x14\n\tlock_time\x18\x05 \x01(\r:\x01\x30\x12\x0e\n\x06\x65xpiry\x18\x06 \x01(\r\x12\x14\n\x0coverwintered\x18\x07 \x01(\x08\x12\x18\n\x10version_group_id\x18\x08 \x01(\r\x12\x11\n\tbranch_id\x18\n \x01(\r\"\x85\x01\n\tTxRequest\x12\"\n\x0crequest_type\x18\x01 \x01(\x0e\x32\x0c.RequestType\x12&\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32\x15.TxRequestDetailsType\x12,\n\nserialized\x18\x03 \x01(\x0b\x32\x18.TxRequestSerializedType\"%\n\x05TxAck\x12\x1c\n\x02tx\x18\x01 \x01(\x0b\x32\x10.TransactionType\"+\n\x08RawTxAck\x12\x1f\n\x02tx\x18\x01 \x01(\x0b\x32\x13.RawTransactionType\"}\n\x0cSignIdentity\x12\x1f\n\x08identity\x18\x01 \x01(\x0b\x32\r.IdentityType\x12\x18\n\x10\x63hallenge_hidden\x18\x02 \x01(\x0c\x12\x18\n\x10\x63hallenge_visual\x18\x03 \x01(\t\x12\x18\n\x10\x65\x63\x64sa_curve_name\x18\x04 \x01(\t\"H\n\x0eSignedIdentity\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x12\n\npublic_key\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\",\n\rApplyPolicies\x12\x1b\n\x06policy\x18\x01 \x03(\x0b\x32\x0b.PolicyType\"?\n\tFlashHash\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0e\n\x06length\x18\x02 \x01(\r\x12\x11\n\tchallenge\x18\x03 \x01(\x0c\":\n\nFlashWrite\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\r\n\x05\x65rase\x18\x03 \x01(\x08\"!\n\x11\x46lashHashResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"5\n\x12\x44\x65\x62ugLinkFlashDump\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0e\n\x06length\x18\x02 \x01(\r\"*\n\x1a\x44\x65\x62ugLinkFlashDumpResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x0b\n\tSoftReset\"\x0f\n\rFirmwareErase\"7\n\x0e\x46irmwareUpload\x12\x14\n\x0cpayload_hash\x18\x01 \x02(\x0c\x12\x0f\n\x07payload\x18\x02 \x02(\x0c\"2\n\x11\x44\x65\x62ugLinkDecision\x12\x0e\n\x06yes_no\x18\x01 \x02(\x08\x12\r\n\x05input\x18\x02 \x01(\t\"\x13\n\x11\x44\x65\x62ugLinkGetState\"\x99\x03\n\x0e\x44\x65\x62ugLinkState\x12\x0e\n\x06layout\x18\x01 \x01(\x0c\x12\x0b\n\x03pin\x18\x02 \x01(\t\x12\x0e\n\x06matrix\x18\x03 \x01(\t\x12\x10\n\x08mnemonic\x18\x04 \x01(\t\x12\x19\n\x04node\x18\x05 \x01(\x0b\x32\x0b.HDNodeType\x12\x1d\n\x15passphrase_protection\x18\x06 \x01(\x08\x12\x12\n\nreset_word\x18\x07 \x01(\t\x12\x15\n\rreset_entropy\x18\x08 \x01(\x0c\x12\x1a\n\x12recovery_fake_word\x18\t \x01(\t\x12\x19\n\x11recovery_word_pos\x18\n \x01(\r\x12\x17\n\x0frecovery_cipher\x18\x0b \x01(\t\x12$\n\x1crecovery_auto_completed_word\x18\x0c \x01(\t\x12\x15\n\rfirmware_hash\x18\r \x01(\x0c\x12\x14\n\x0cstorage_hash\x18\x0e \x01(\x0c\x12\x13\n\x0b\x64ice_digest\x18\x0f \x01(\x0c\x12\x15\n\rconfirm_title\x18\x10 \x01(\t\x12\x14\n\x0c\x63onfirm_body\x18\x11 \x01(\t\"\x0f\n\rDebugLinkStop\";\n\x0c\x44\x65\x62ugLinkLog\x12\r\n\x05level\x18\x01 \x01(\r\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12\x0c\n\x04text\x18\x03 \x01(\t\"\x15\n\x13\x44\x65\x62ugLinkFillConfig\" \n\x0e\x43hangeWipeCode\x12\x0e\n\x06remove\x18\x01 \x01(\x08\"\x1f\n\x1d\x43learsignAttestorGetPublicKey\"0\n\x1a\x43learsignAttestorPublicKey\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\"(\n\x15\x43learsignAttestorSign\x12\x0f\n\x07payload\x18\x01 \x01(\x0c\"C\n\x1a\x43learsignAttestorSignature\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\x12\n\npublic_key\x18\x02 \x01(\x0c*\xc3\x46\n\x0bMessageType\x12 \n\x16MessageType_Initialize\x10\x00\x1a\x04\x90\xb5\x18\x01\x12\x1a\n\x10MessageType_Ping\x10\x01\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Success\x10\x02\x1a\x04\x98\xb5\x18\x01\x12\x1d\n\x13MessageType_Failure\x10\x03\x1a\x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_ChangePin\x10\x04\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_WipeDevice\x10\x05\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_FirmwareErase\x10\x06\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_FirmwareUpload\x10\x07\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_GetEntropy\x10\t\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Entropy\x10\n\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_GetPublicKey\x10\x0b\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_PublicKey\x10\x0c\x1a\x04\x98\xb5\x18\x01\x12 \n\x16MessageType_LoadDevice\x10\r\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_ResetDevice\x10\x0e\x1a\x04\x90\xb5\x18\x01\x12\x1c\n\x12MessageType_SignTx\x10\x0f\x1a\x04\x90\xb5\x18\x01\x12\x1e\n\x14MessageType_Features\x10\x11\x1a\x04\x98\xb5\x18\x01\x12&\n\x1cMessageType_PinMatrixRequest\x10\x12\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_PinMatrixAck\x10\x13\x1a\x04\x90\xb5\x18\x01\x12\x1c\n\x12MessageType_Cancel\x10\x14\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_TxRequest\x10\x15\x1a\x04\x98\xb5\x18\x01\x12\x1b\n\x11MessageType_TxAck\x10\x16\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_CipherKeyValue\x10\x17\x1a\x04\x90\xb5\x18\x01\x12\"\n\x18MessageType_ClearSession\x10\x18\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ApplySettings\x10\x19\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ButtonRequest\x10\x1a\x1a\x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_ButtonAck\x10\x1b\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_GetAddress\x10\x1d\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Address\x10\x1e\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EntropyRequest\x10#\x1a\x04\x98\xb5\x18\x01\x12 \n\x16MessageType_EntropyAck\x10$\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_SignMessage\x10&\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_VerifyMessage\x10\'\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_MessageSignature\x10(\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1dMessageType_PassphraseRequest\x10)\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_PassphraseAck\x10*\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_RecoveryDevice\x10-\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_WordRequest\x10.\x1a\x04\x98\xb5\x18\x01\x12\x1d\n\x13MessageType_WordAck\x10/\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_CipheredKeyValue\x10\x30\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EncryptMessage\x10\x31\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_EncryptedMessage\x10\x32\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_DecryptMessage\x10\x33\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_DecryptedMessage\x10\x34\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_SignIdentity\x10\x35\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_SignedIdentity\x10\x36\x1a\x04\x98\xb5\x18\x01\x12!\n\x17MessageType_GetFeatures\x10\x37\x1a\x04\x90\xb5\x18\x01\x12(\n\x1eMessageType_EthereumGetAddress\x10\x38\x1a\x04\x90\xb5\x18\x01\x12%\n\x1bMessageType_EthereumAddress\x10\x39\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EthereumSignTx\x10:\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1dMessageType_EthereumTxRequest\x10;\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_EthereumTxAck\x10<\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_CharacterRequest\x10P\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_CharacterAck\x10Q\x1a\x04\x90\xb5\x18\x01\x12\x1e\n\x14MessageType_RawTxAck\x10R\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ApplyPolicies\x10S\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_FlashHash\x10T\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_FlashWrite\x10U\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1dMessageType_FlashHashResponse\x10V\x1a\x04\x98\xb5\x18\x01\x12(\n\x1eMessageType_DebugLinkFlashDump\x10W\x1a\x04\xa0\xb5\x18\x01\x12\x30\n&MessageType_DebugLinkFlashDumpResponse\x10X\x1a\x04\xa8\xb5\x18\x01\x12\x1f\n\x15MessageType_SoftReset\x10Y\x1a\x04\xa0\xb5\x18\x01\x12\'\n\x1dMessageType_DebugLinkDecision\x10\x64\x1a\x04\xa0\xb5\x18\x01\x12\'\n\x1dMessageType_DebugLinkGetState\x10\x65\x1a\x04\xa0\xb5\x18\x01\x12$\n\x1aMessageType_DebugLinkState\x10\x66\x1a\x04\xa8\xb5\x18\x01\x12#\n\x19MessageType_DebugLinkStop\x10g\x1a\x04\xa0\xb5\x18\x01\x12\"\n\x18MessageType_DebugLinkLog\x10h\x1a\x04\xa8\xb5\x18\x01\x12)\n\x1fMessageType_DebugLinkFillConfig\x10i\x1a\x04\xa8\xb5\x18\x01\x12\"\n\x18MessageType_GetCoinTable\x10j\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_CoinTable\x10k\x1a\x04\x98\xb5\x18\x01\x12)\n\x1fMessageType_EthereumSignMessage\x10l\x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumVerifyMessage\x10m\x1a\x04\x90\xb5\x18\x01\x12.\n$MessageType_EthereumMessageSignature\x10n\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_ChangeWipeCode\x10o\x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumSignTypedHash\x10p\x1a\x04\x90\xb5\x18\x01\x12\x30\n&MessageType_EthereumTypedDataSignature\x10q\x1a\x04\x98\xb5\x18\x01\x12,\n\"MessageType_Ethereum712TypesValues\x10r\x1a\x04\x90\xb5\x18\x01\x12(\n\x1eMessageType_EthereumTxMetadata\x10s\x1a\x04\x90\xb5\x18\x01\x12)\n\x1fMessageType_EthereumMetadataAck\x10t\x1a\x04\x98\xb5\x18\x01\x12)\n\x1fMessageType_LoadClearsignSigner\x10u\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_EthereumSignTypedData\x10\xa8\r\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_EthereumTypedDataStructRequest\x10\xa9\r\x1a\x04\x98\xb5\x18\x01\x12\x31\n&MessageType_EthereumTypedDataStructAck\x10\xaa\r\x1a\x04\x90\xb5\x18\x01\x12\x34\n)MessageType_EthereumTypedDataValueRequest\x10\xab\r\x1a\x04\x98\xb5\x18\x01\x12\x30\n%MessageType_EthereumTypedDataValueAck\x10\xac\r\x1a\x04\x90\xb5\x18\x01\x12\x32\n\'MessageType_EthereumClearSignDefinition\x10\xad\r\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_EthereumClearSignDefinitionAck\x10\xae\r\x1a\x04\x98\xb5\x18\x01\x12\x39\n.MessageType_EthereumClearSignDefinitionRequest\x10\xaf\r\x1a\x04\x98\xb5\x18\x01\x12\x37\n,MessageType_EthereumClearSignDefinitionChunk\x10\xb0\r\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_GetBip85Mnemonic\x10x\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_Bip85Mnemonic\x10y\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_RippleGetAddress\x10\x90\x03\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_RippleAddress\x10\x91\x03\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_RippleSignTx\x10\x92\x03\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_RippleSignedTx\x10\x93\x03\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ThorchainGetAddress\x10\xf4\x03\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_ThorchainAddress\x10\xf5\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ThorchainSignTx\x10\xf6\x03\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ThorchainMsgRequest\x10\xf7\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ThorchainMsgAck\x10\xf8\x03\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_ThorchainSignedTx\x10\xf9\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_EosGetPublicKey\x10\xd8\x04\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_EosPublicKey\x10\xd9\x04\x1a\x04\x98\xb5\x18\x01\x12 \n\x15MessageType_EosSignTx\x10\xda\x04\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_EosTxActionRequest\x10\xdb\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_EosTxActionAck\x10\xdc\x04\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_EosSignedTx\x10\xdd\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_NanoGetAddress\x10\xbc\x05\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_NanoAddress\x10\xbd\x05\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_NanoSignTx\x10\xbe\x05\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_NanoSignedTx\x10\xbf\x05\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_SolanaGetAddress\x10\xee\x05\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_SolanaAddress\x10\xef\x05\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_SolanaSignTx\x10\xf0\x05\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_SolanaSignedTx\x10\xf1\x05\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_SolanaSignMessage\x10\xf2\x05\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_SolanaMessageSignature\x10\xf3\x05\x1a\x04\x98\xb5\x18\x01\x12\x30\n%MessageType_SolanaSignOffchainMessage\x10\xf4\x05\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_SolanaOffchainMessageSignature\x10\xf5\x05\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_BinanceGetAddress\x10\xa0\x06\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_BinanceAddress\x10\xa1\x06\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_BinanceGetPublicKey\x10\xa2\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinancePublicKey\x10\xa3\x06\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_BinanceSignTx\x10\xa4\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinanceTxRequest\x10\xa5\x06\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_BinanceTransferMsg\x10\xa6\x06\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_BinanceOrderMsg\x10\xa7\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinanceCancelMsg\x10\xa8\x06\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_BinanceSignedTx\x10\xa9\x06\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosGetAddress\x10\x84\x07\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_CosmosAddress\x10\x85\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_CosmosSignTx\x10\x86\x07\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosMsgRequest\x10\x87\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_CosmosMsgAck\x10\x88\x07\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_CosmosSignedTx\x10\x89\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_CosmosMsgDelegate\x10\x8a\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CosmosMsgUndelegate\x10\x8b\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CosmosMsgRedelegate\x10\x8c\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosMsgRewards\x10\x8d\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_CosmosMsgIBCTransfer\x10\x8e\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_TendermintGetAddress\x10\xe8\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TendermintAddress\x10\xe9\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_TendermintSignTx\x10\xea\x07\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_TendermintMsgRequest\x10\xeb\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_TendermintMsgAck\x10\xec\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TendermintMsgSend\x10\xed\x07\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_TendermintSignedTx\x10\xee\x07\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_TendermintMsgDelegate\x10\xef\x07\x1a\x04\x98\xb5\x18\x01\x12.\n#MessageType_TendermintMsgUndelegate\x10\xf0\x07\x1a\x04\x98\xb5\x18\x01\x12.\n#MessageType_TendermintMsgRedelegate\x10\xf1\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_TendermintMsgRewards\x10\xf2\x07\x1a\x04\x98\xb5\x18\x01\x12/\n$MessageType_TendermintMsgIBCTransfer\x10\xf3\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisGetAddress\x10\xcc\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisAddress\x10\xcd\x08\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_OsmosisSignTx\x10\xce\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgRequest\x10\xcf\x08\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_OsmosisMsgAck\x10\xd0\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisMsgSend\x10\xd1\x08\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_OsmosisMsgDelegate\x10\xd2\x08\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_OsmosisMsgUndelegate\x10\xd3\x08\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_OsmosisMsgRedelegate\x10\xd4\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgRewards\x10\xd5\x08\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_OsmosisMsgLPAdd\x10\xd6\x08\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_OsmosisMsgLPRemove\x10\xd7\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgLPStake\x10\xd8\x08\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_OsmosisMsgLPUnstake\x10\xd9\x08\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_OsmosisMsgIBCTransfer\x10\xda\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisMsgSwap\x10\xdb\x08\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_OsmosisSignedTx\x10\xdc\x08\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_MayachainGetAddress\x10\xb0\t\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_MayachainAddress\x10\xb1\t\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_MayachainSignTx\x10\xb2\t\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_MayachainMsgRequest\x10\xb3\t\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_MayachainMsgAck\x10\xb4\t\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_MayachainSignedTx\x10\xb5\t\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_ZcashSignPCZT\x10\x94\n\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_ZcashPCZTAction\x10\x95\n\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_ZcashPCZTActionAck\x10\x96\n\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ZcashSignedPCZT\x10\x97\n\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_ZcashGetOrchardFVK\x10\x98\n\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_ZcashOrchardFVK\x10\x99\n\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_ZcashTransparentInput\x10\x9a\n\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_ZcashTransparentSigned\x10\x9b\n\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_ZcashDisplayAddress\x10\x9c\n\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_ZcashAddress\x10\x9d\n\x1a\x04\x98\xb5\x18\x01\x12-\n\"MessageType_ZcashTransparentOutput\x10\x9e\n\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ZcashTransparentAck\x10\x9f\n\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_TronGetAddress\x10\xf8\n\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TronAddress\x10\xf9\n\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_TronSignTx\x10\xfa\n\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_TronSignedTx\x10\xfb\n\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_TronSignMessage\x10\xfc\n\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_TronMessageSignature\x10\xfd\n\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_TronVerifyMessage\x10\xfe\n\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TronSignTypedHash\x10\xff\n\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_TronTypedDataSignature\x10\x80\x0b\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_TonGetAddress\x10\xdc\x0b\x1a\x04\x90\xb5\x18\x01\x12!\n\x16MessageType_TonAddress\x10\xdd\x0b\x1a\x04\x98\xb5\x18\x01\x12 \n\x15MessageType_TonSignTx\x10\xde\x0b\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TonSignedTx\x10\xdf\x0b\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_TonSignMessage\x10\xe0\x0b\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_TonMessageSignature\x10\xe1\x0b\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_HiveGetPublicKey\x10\xc0\x0c\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_HivePublicKey\x10\xc1\x0c\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_HiveSignTx\x10\xc2\x0c\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_HiveSignedTx\x10\xc3\x0c\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_HiveGetPublicKeys\x10\xc4\x0c\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_HivePublicKeys\x10\xc5\x0c\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_HiveSignAccountCreate\x10\xc6\x0c\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_HiveSignedAccountCreate\x10\xc7\x0c\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_HiveSignAccountUpdate\x10\xc8\x0c\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_HiveSignedAccountUpdate\x10\xc9\x0c\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_NearGetAddress\x10\xca\x0c\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_NearAddress\x10\xcb\x0c\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_NearSignTx\x10\xcc\x0c\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_NearSignedTx\x10\xcd\x0c\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_HiveSignMessage\x10\xce\x0c\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_HiveSignedMessage\x10\xcf\x0c\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_HiveSignOperations\x10\xd0\x0c\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_HiveSignedOperations\x10\xd1\x0c\x1a\x04\x98\xb5\x18\x01\x12\x34\n)MessageType_ClearsignAttestorGetPublicKey\x10\xa4\r\x1a\x04\x90\xb5\x18\x01\x12\x31\n&MessageType_ClearsignAttestorPublicKey\x10\xa5\r\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_ClearsignAttestorSign\x10\xa6\r\x1a\x04\x90\xb5\x18\x01\x12\x31\n&MessageType_ClearsignAttestorSignature\x10\xa7\r\x1a\x04\x98\xb5\x18\x01\x42,\n\x1a\x63om.keepkey.deviceprotocolB\x0eKeepKeyMessage') + serialized_pb=_b('\n\x0emessages.proto\x1a\x0btypes.proto\"\x0c\n\nInitialize\"\r\n\x0bGetFeatures\"\x93\r\n\x08\x46\x65\x61tures\x12\x0e\n\x06vendor\x18\x01 \x01(\t\x12\x15\n\rmajor_version\x18\x02 \x01(\r\x12\x15\n\rminor_version\x18\x03 \x01(\r\x12\x15\n\rpatch_version\x18\x04 \x01(\r\x12\x17\n\x0f\x62ootloader_mode\x18\x05 \x01(\x08\x12\x11\n\tdevice_id\x18\x06 \x01(\t\x12\x16\n\x0epin_protection\x18\x07 \x01(\x08\x12\x1d\n\x15passphrase_protection\x18\x08 \x01(\x08\x12\x10\n\x08language\x18\t \x01(\t\x12\r\n\x05label\x18\n \x01(\t\x12\x18\n\x05\x63oins\x18\x0b \x03(\x0b\x32\t.CoinType\x12\x13\n\x0binitialized\x18\x0c \x01(\x08\x12\x10\n\x08revision\x18\r \x01(\x0c\x12\x17\n\x0f\x62ootloader_hash\x18\x0e \x01(\x0c\x12\x10\n\x08imported\x18\x0f \x01(\x08\x12\x12\n\npin_cached\x18\x10 \x01(\x08\x12\x19\n\x11passphrase_cached\x18\x11 \x01(\x08\x12\x1d\n\x08policies\x18\x12 \x03(\x0b\x32\x0b.PolicyType\x12\r\n\x05model\x18\x15 \x01(\t\x12\x18\n\x10\x66irmware_variant\x18\x16 \x01(\t\x12\x15\n\rfirmware_hash\x18\x17 \x01(\x0c\x12\x11\n\tno_backup\x18\x18 \x01(\x08\x12\x1c\n\x14wipe_code_protection\x18\x19 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x1a \x01(\r\x12\x18\n\x10supports_taproot\x18\x1b \x01(\x08\x12\x1b\n\x13supports_dice_modes\x18\x1c \x01(\x08\x12\'\n\x1fsupports_solana_lut_attestation\x18\x1d \x01(\x08\x12.\n\x0c\x63\x61pabilities\x18\x1e \x03(\x0e\x32\x14.Features.CapabilityB\x02\x10\x01\"\xd6\x07\n\nCapability\x12\x1a\n\x16\x43\x41PABILITY_UNSPECIFIED\x10\x00\x12#\n\x1f\x43\x41PABILITY_ENTROPY_AUDIT_BUDGET\x10\x01\x12-\n)CAPABILITY_ERC20_UNLIMITED_APPROVE_REVIEW\x10\x02\x12,\n(CAPABILITY_ERC20_UNLIMITED_PERMIT_REVIEW\x10\x03\x12%\n!CAPABILITY_ERC7730_RUNTIME_REVIEW\x10\x04\x12#\n\x1f\x43\x41PABILITY_EVM_CERTIFIED_INTENT\x10\x05\x12$\n CAPABILITY_EVM_MAX_AMOUNT_REVIEW\x10\x06\x12\x1e\n\x1a\x43\x41PABILITY_EVM_TX_METADATA\x10\x07\x12\'\n#CAPABILITY_EVM_UNKNOWN_TOKEN_REVIEW\x10\x08\x12\"\n\x1e\x43\x41PABILITY_HIVE_RELEASE_REVIEW\x10\t\x12(\n$CAPABILITY_LEGACY_EVM_ROUTER_SIGNING\x10\n\x12\"\n\x1e\x43\x41PABILITY_MAYA_SINGLE_MESSAGE\x10\x0b\x12\"\n\x1e\x43\x41PABILITY_OSMOSIS_WIRE_GUARDS\x10\x0c\x12\x1d\n\x19\x43\x41PABILITY_PERMIT2_REVIEW\x10\r\x12%\n!CAPABILITY_PROMPT_WORKFLOW_UNWIND\x10\x0e\x12&\n\"CAPABILITY_PROTECTED_PING_PRESENCE\x10\x0f\x12!\n\x1d\x43\x41PABILITY_RIPPLE_MEMO_POLICY\x10\x10\x12%\n!CAPABILITY_SESSION_TRUST_LIFETIME\x10\x11\x12&\n\"CAPABILITY_SOLANA_CERTIFIED_REVIEW\x10\x12\x12%\n!CAPABILITY_SOLANA_LUT_ATTESTATION\x10\x13\x12$\n CAPABILITY_SOLANA_RUNTIME_REVIEW\x10\x14\x12\x1e\n\x1a\x43\x41PABILITY_STORAGE_V19_KDF\x10\x15\x12\"\n\x1e\x43\x41PABILITY_TENDERMINT_PROGRESS\x10\x16\x12\"\n\x1e\x43\x41PABILITY_SAFE_RESET_CEREMONY\x10\x17\x12\"\n\x1e\x43\x41PABILITY_THOR_DEPOSIT_REVIEW\x10\x18\x12 \n\x1c\x43\x41PABILITY_TRON_TRC20_REVIEW\x10\x19\"*\n\x0cGetCoinTable\x12\r\n\x05start\x18\x01 \x01(\r\x12\x0b\n\x03\x65nd\x18\x02 \x01(\r\"L\n\tCoinTable\x12\x18\n\x05table\x18\x01 \x03(\x0b\x32\t.CoinType\x12\x11\n\tnum_coins\x18\x02 \x01(\r\x12\x12\n\nchunk_size\x18\x03 \x01(\r\"\x0e\n\x0c\x43learSession\"y\n\rApplySettings\x12\x10\n\x08language\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x16\n\x0euse_passphrase\x18\x03 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x04 \x01(\r\x12\x13\n\x0bu2f_counter\x18\x05 \x01(\r\"\x1b\n\tChangePin\x12\x0e\n\x06remove\x18\x01 \x01(\x08\"\x87\x01\n\x04Ping\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x19\n\x11\x62utton_protection\x18\x02 \x01(\x08\x12\x16\n\x0epin_protection\x18\x03 \x01(\x08\x12\x1d\n\x15passphrase_protection\x18\x04 \x01(\x08\x12\x1c\n\x14wipe_code_protection\x18\x05 \x01(\x08\"\x1a\n\x07Success\x12\x0f\n\x07message\x18\x01 \x01(\t\"6\n\x07\x46\x61ilure\x12\x1a\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x0c.FailureType\x12\x0f\n\x07message\x18\x02 \x01(\t\"?\n\rButtonRequest\x12 \n\x04\x63ode\x18\x01 \x01(\x0e\x32\x12.ButtonRequestType\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x0b\n\tButtonAck\"7\n\x10PinMatrixRequest\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.PinMatrixRequestType\"\x1b\n\x0cPinMatrixAck\x12\x0b\n\x03pin\x18\x01 \x02(\t\"\x08\n\x06\x43\x61ncel\"\x13\n\x11PassphraseRequest\"#\n\rPassphraseAck\x12\x12\n\npassphrase\x18\x01 \x02(\t\"\x1a\n\nGetEntropy\x12\x0c\n\x04size\x18\x01 \x02(\r\"\x1a\n\x07\x45ntropy\x12\x0f\n\x07\x65ntropy\x18\x01 \x02(\x0c\"\xa2\x01\n\x0cGetPublicKey\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x18\n\x10\x65\x63\x64sa_curve_name\x18\x02 \x01(\t\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\x12\x1a\n\tcoin_name\x18\x04 \x01(\t:\x07\x42itcoin\x12\x33\n\x0bscript_type\x18\x05 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"4\n\tPublicKey\x12\x19\n\x04node\x18\x01 \x02(\x0b\x32\x0b.HDNodeType\x12\x0c\n\x04xpub\x18\x02 \x01(\t\"\xb3\x01\n\nGetAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x1a\n\tcoin_name\x18\x02 \x01(\t:\x07\x42itcoin\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\x12+\n\x08multisig\x18\x04 \x01(\x0b\x32\x19.MultisigRedeemScriptType\x12\x33\n\x0bscript_type\x18\x05 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"\x1a\n\x07\x41\x64\x64ress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x02(\t\"\x0c\n\nWipeDevice\"\xbb\x01\n\nLoadDevice\x12\x10\n\x08mnemonic\x18\x01 \x01(\t\x12\x19\n\x04node\x18\x02 \x01(\x0b\x32\x0b.HDNodeType\x12\x0b\n\x03pin\x18\x03 \x01(\t\x12\x1d\n\x15passphrase_protection\x18\x04 \x01(\x08\x12\x19\n\x08language\x18\x05 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x06 \x01(\t\x12\x15\n\rskip_checksum\x18\x07 \x01(\x08\x12\x13\n\x0bu2f_counter\x18\x08 \x01(\r\"\x8a\x02\n\x0bResetDevice\x12\x16\n\x0e\x64isplay_random\x18\x01 \x01(\x08\x12\x15\n\x08strength\x18\x02 \x01(\r:\x03\x32\x35\x36\x12\x1d\n\x15passphrase_protection\x18\x03 \x01(\x08\x12\x16\n\x0epin_protection\x18\x04 \x01(\x08\x12\x19\n\x08language\x18\x05 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x06 \x01(\t\x12\x11\n\tno_backup\x18\x07 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x08 \x01(\r\x12\x13\n\x0bu2f_counter\x18\t \x01(\r\x12\x14\n\x0c\x64ice_entropy\x18\n \x01(\x08\x12\x11\n\tdice_only\x18\x0b \x01(\x08\"\x10\n\x0e\x45ntropyRequest\"\x1d\n\nEntropyAck\x12\x0f\n\x07\x65ntropy\x18\x01 \x01(\x0c\"\xff\x01\n\x0eRecoveryDevice\x12\x12\n\nword_count\x18\x01 \x01(\r\x12\x1d\n\x15passphrase_protection\x18\x02 \x01(\x08\x12\x16\n\x0epin_protection\x18\x03 \x01(\x08\x12\x19\n\x08language\x18\x04 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x05 \x01(\t\x12\x18\n\x10\x65nforce_wordlist\x18\x06 \x01(\x08\x12\x1c\n\x14use_character_cipher\x18\x07 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x08 \x01(\r\x12\x13\n\x0bu2f_counter\x18\t \x01(\r\x12\x0f\n\x07\x64ry_run\x18\n \x01(\x08\"\r\n\x0bWordRequest\"\x17\n\x07WordAck\x12\x0c\n\x04word\x18\x01 \x02(\t\";\n\x10\x43haracterRequest\x12\x10\n\x08word_pos\x18\x01 \x02(\r\x12\x15\n\rcharacter_pos\x18\x02 \x02(\r\"?\n\x0c\x43haracterAck\x12\x11\n\tcharacter\x18\x01 \x01(\t\x12\x0e\n\x06\x64\x65lete\x18\x02 \x01(\x08\x12\x0c\n\x04\x64one\x18\x03 \x01(\x08\"\x82\x01\n\x0bSignMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07message\x18\x02 \x02(\x0c\x12\x1a\n\tcoin_name\x18\x03 \x01(\t:\x07\x42itcoin\x12\x33\n\x0bscript_type\x18\x04 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"`\n\rVerifyMessage\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\x12\x1a\n\tcoin_name\x18\x04 \x01(\t:\x07\x42itcoin\"6\n\x10MessageSignature\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"v\n\x0e\x45ncryptMessage\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\x12\x14\n\x0c\x64isplay_only\x18\x03 \x01(\x08\x12\x11\n\taddress_n\x18\x04 \x03(\r\x12\x1a\n\tcoin_name\x18\x05 \x01(\t:\x07\x42itcoin\"@\n\x10\x45ncryptedMessage\x12\r\n\x05nonce\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\x12\x0c\n\x04hmac\x18\x03 \x01(\x0c\"Q\n\x0e\x44\x65\x63ryptMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\r\n\x05nonce\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\x12\x0c\n\x04hmac\x18\x04 \x01(\x0c\"4\n\x10\x44\x65\x63ryptedMessage\x12\x0f\n\x07message\x18\x01 \x01(\x0c\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"\x8c\x01\n\x0e\x43ipherKeyValue\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\x0c\x12\x0f\n\x07\x65ncrypt\x18\x04 \x01(\x08\x12\x16\n\x0e\x61sk_on_encrypt\x18\x05 \x01(\x08\x12\x16\n\x0e\x61sk_on_decrypt\x18\x06 \x01(\x08\x12\n\n\x02iv\x18\x07 \x01(\x0c\"!\n\x10\x43ipheredKeyValue\x12\r\n\x05value\x18\x01 \x01(\x0c\"5\n\x10GetBip85Mnemonic\x12\x12\n\nword_count\x18\x01 \x02(\r\x12\r\n\x05index\x18\x02 \x02(\r\"!\n\rBip85Mnemonic\x12\x10\n\x08mnemonic\x18\x01 \x02(\t\"\xce\x01\n\x06SignTx\x12\x15\n\routputs_count\x18\x01 \x02(\r\x12\x14\n\x0cinputs_count\x18\x02 \x02(\r\x12\x1a\n\tcoin_name\x18\x03 \x01(\t:\x07\x42itcoin\x12\x12\n\x07version\x18\x04 \x01(\r:\x01\x31\x12\x14\n\tlock_time\x18\x05 \x01(\r:\x01\x30\x12\x0e\n\x06\x65xpiry\x18\x06 \x01(\r\x12\x14\n\x0coverwintered\x18\x07 \x01(\x08\x12\x18\n\x10version_group_id\x18\x08 \x01(\r\x12\x11\n\tbranch_id\x18\n \x01(\r\"\x85\x01\n\tTxRequest\x12\"\n\x0crequest_type\x18\x01 \x01(\x0e\x32\x0c.RequestType\x12&\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32\x15.TxRequestDetailsType\x12,\n\nserialized\x18\x03 \x01(\x0b\x32\x18.TxRequestSerializedType\"%\n\x05TxAck\x12\x1c\n\x02tx\x18\x01 \x01(\x0b\x32\x10.TransactionType\"+\n\x08RawTxAck\x12\x1f\n\x02tx\x18\x01 \x01(\x0b\x32\x13.RawTransactionType\"}\n\x0cSignIdentity\x12\x1f\n\x08identity\x18\x01 \x01(\x0b\x32\r.IdentityType\x12\x18\n\x10\x63hallenge_hidden\x18\x02 \x01(\x0c\x12\x18\n\x10\x63hallenge_visual\x18\x03 \x01(\t\x12\x18\n\x10\x65\x63\x64sa_curve_name\x18\x04 \x01(\t\"H\n\x0eSignedIdentity\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x12\n\npublic_key\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\",\n\rApplyPolicies\x12\x1b\n\x06policy\x18\x01 \x03(\x0b\x32\x0b.PolicyType\"?\n\tFlashHash\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0e\n\x06length\x18\x02 \x01(\r\x12\x11\n\tchallenge\x18\x03 \x01(\x0c\":\n\nFlashWrite\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\r\n\x05\x65rase\x18\x03 \x01(\x08\"!\n\x11\x46lashHashResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"5\n\x12\x44\x65\x62ugLinkFlashDump\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0e\n\x06length\x18\x02 \x01(\r\"*\n\x1a\x44\x65\x62ugLinkFlashDumpResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x0b\n\tSoftReset\"\x0f\n\rFirmwareErase\"7\n\x0e\x46irmwareUpload\x12\x14\n\x0cpayload_hash\x18\x01 \x02(\x0c\x12\x0f\n\x07payload\x18\x02 \x02(\x0c\"2\n\x11\x44\x65\x62ugLinkDecision\x12\x0e\n\x06yes_no\x18\x01 \x02(\x08\x12\r\n\x05input\x18\x02 \x01(\t\"\x13\n\x11\x44\x65\x62ugLinkGetState\"\x99\x03\n\x0e\x44\x65\x62ugLinkState\x12\x0e\n\x06layout\x18\x01 \x01(\x0c\x12\x0b\n\x03pin\x18\x02 \x01(\t\x12\x0e\n\x06matrix\x18\x03 \x01(\t\x12\x10\n\x08mnemonic\x18\x04 \x01(\t\x12\x19\n\x04node\x18\x05 \x01(\x0b\x32\x0b.HDNodeType\x12\x1d\n\x15passphrase_protection\x18\x06 \x01(\x08\x12\x12\n\nreset_word\x18\x07 \x01(\t\x12\x15\n\rreset_entropy\x18\x08 \x01(\x0c\x12\x1a\n\x12recovery_fake_word\x18\t \x01(\t\x12\x19\n\x11recovery_word_pos\x18\n \x01(\r\x12\x17\n\x0frecovery_cipher\x18\x0b \x01(\t\x12$\n\x1crecovery_auto_completed_word\x18\x0c \x01(\t\x12\x15\n\rfirmware_hash\x18\r \x01(\x0c\x12\x14\n\x0cstorage_hash\x18\x0e \x01(\x0c\x12\x13\n\x0b\x64ice_digest\x18\x0f \x01(\x0c\x12\x15\n\rconfirm_title\x18\x10 \x01(\t\x12\x14\n\x0c\x63onfirm_body\x18\x11 \x01(\t\"\x0f\n\rDebugLinkStop\";\n\x0c\x44\x65\x62ugLinkLog\x12\r\n\x05level\x18\x01 \x01(\r\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12\x0c\n\x04text\x18\x03 \x01(\t\"\x15\n\x13\x44\x65\x62ugLinkFillConfig\" \n\x0e\x43hangeWipeCode\x12\x0e\n\x06remove\x18\x01 \x01(\x08\"\x1f\n\x1d\x43learsignAttestorGetPublicKey\"0\n\x1a\x43learsignAttestorPublicKey\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\"(\n\x15\x43learsignAttestorSign\x12\x0f\n\x07payload\x18\x01 \x01(\x0c\"C\n\x1a\x43learsignAttestorSignature\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\x12\n\npublic_key\x18\x02 \x01(\x0c*\xc3\x46\n\x0bMessageType\x12 \n\x16MessageType_Initialize\x10\x00\x1a\x04\x90\xb5\x18\x01\x12\x1a\n\x10MessageType_Ping\x10\x01\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Success\x10\x02\x1a\x04\x98\xb5\x18\x01\x12\x1d\n\x13MessageType_Failure\x10\x03\x1a\x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_ChangePin\x10\x04\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_WipeDevice\x10\x05\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_FirmwareErase\x10\x06\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_FirmwareUpload\x10\x07\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_GetEntropy\x10\t\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Entropy\x10\n\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_GetPublicKey\x10\x0b\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_PublicKey\x10\x0c\x1a\x04\x98\xb5\x18\x01\x12 \n\x16MessageType_LoadDevice\x10\r\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_ResetDevice\x10\x0e\x1a\x04\x90\xb5\x18\x01\x12\x1c\n\x12MessageType_SignTx\x10\x0f\x1a\x04\x90\xb5\x18\x01\x12\x1e\n\x14MessageType_Features\x10\x11\x1a\x04\x98\xb5\x18\x01\x12&\n\x1cMessageType_PinMatrixRequest\x10\x12\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_PinMatrixAck\x10\x13\x1a\x04\x90\xb5\x18\x01\x12\x1c\n\x12MessageType_Cancel\x10\x14\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_TxRequest\x10\x15\x1a\x04\x98\xb5\x18\x01\x12\x1b\n\x11MessageType_TxAck\x10\x16\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_CipherKeyValue\x10\x17\x1a\x04\x90\xb5\x18\x01\x12\"\n\x18MessageType_ClearSession\x10\x18\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ApplySettings\x10\x19\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ButtonRequest\x10\x1a\x1a\x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_ButtonAck\x10\x1b\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_GetAddress\x10\x1d\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Address\x10\x1e\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EntropyRequest\x10#\x1a\x04\x98\xb5\x18\x01\x12 \n\x16MessageType_EntropyAck\x10$\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_SignMessage\x10&\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_VerifyMessage\x10\'\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_MessageSignature\x10(\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1dMessageType_PassphraseRequest\x10)\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_PassphraseAck\x10*\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_RecoveryDevice\x10-\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_WordRequest\x10.\x1a\x04\x98\xb5\x18\x01\x12\x1d\n\x13MessageType_WordAck\x10/\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_CipheredKeyValue\x10\x30\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EncryptMessage\x10\x31\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_EncryptedMessage\x10\x32\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_DecryptMessage\x10\x33\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_DecryptedMessage\x10\x34\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_SignIdentity\x10\x35\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_SignedIdentity\x10\x36\x1a\x04\x98\xb5\x18\x01\x12!\n\x17MessageType_GetFeatures\x10\x37\x1a\x04\x90\xb5\x18\x01\x12(\n\x1eMessageType_EthereumGetAddress\x10\x38\x1a\x04\x90\xb5\x18\x01\x12%\n\x1bMessageType_EthereumAddress\x10\x39\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EthereumSignTx\x10:\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1dMessageType_EthereumTxRequest\x10;\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_EthereumTxAck\x10<\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_CharacterRequest\x10P\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_CharacterAck\x10Q\x1a\x04\x90\xb5\x18\x01\x12\x1e\n\x14MessageType_RawTxAck\x10R\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ApplyPolicies\x10S\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_FlashHash\x10T\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_FlashWrite\x10U\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1dMessageType_FlashHashResponse\x10V\x1a\x04\x98\xb5\x18\x01\x12(\n\x1eMessageType_DebugLinkFlashDump\x10W\x1a\x04\xa0\xb5\x18\x01\x12\x30\n&MessageType_DebugLinkFlashDumpResponse\x10X\x1a\x04\xa8\xb5\x18\x01\x12\x1f\n\x15MessageType_SoftReset\x10Y\x1a\x04\xa0\xb5\x18\x01\x12\'\n\x1dMessageType_DebugLinkDecision\x10\x64\x1a\x04\xa0\xb5\x18\x01\x12\'\n\x1dMessageType_DebugLinkGetState\x10\x65\x1a\x04\xa0\xb5\x18\x01\x12$\n\x1aMessageType_DebugLinkState\x10\x66\x1a\x04\xa8\xb5\x18\x01\x12#\n\x19MessageType_DebugLinkStop\x10g\x1a\x04\xa0\xb5\x18\x01\x12\"\n\x18MessageType_DebugLinkLog\x10h\x1a\x04\xa8\xb5\x18\x01\x12)\n\x1fMessageType_DebugLinkFillConfig\x10i\x1a\x04\xa8\xb5\x18\x01\x12\"\n\x18MessageType_GetCoinTable\x10j\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_CoinTable\x10k\x1a\x04\x98\xb5\x18\x01\x12)\n\x1fMessageType_EthereumSignMessage\x10l\x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumVerifyMessage\x10m\x1a\x04\x90\xb5\x18\x01\x12.\n$MessageType_EthereumMessageSignature\x10n\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_ChangeWipeCode\x10o\x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumSignTypedHash\x10p\x1a\x04\x90\xb5\x18\x01\x12\x30\n&MessageType_EthereumTypedDataSignature\x10q\x1a\x04\x98\xb5\x18\x01\x12,\n\"MessageType_Ethereum712TypesValues\x10r\x1a\x04\x90\xb5\x18\x01\x12(\n\x1eMessageType_EthereumTxMetadata\x10s\x1a\x04\x90\xb5\x18\x01\x12)\n\x1fMessageType_EthereumMetadataAck\x10t\x1a\x04\x98\xb5\x18\x01\x12)\n\x1fMessageType_LoadClearsignSigner\x10u\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_EthereumSignTypedData\x10\xa8\r\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_EthereumTypedDataStructRequest\x10\xa9\r\x1a\x04\x98\xb5\x18\x01\x12\x31\n&MessageType_EthereumTypedDataStructAck\x10\xaa\r\x1a\x04\x90\xb5\x18\x01\x12\x34\n)MessageType_EthereumTypedDataValueRequest\x10\xab\r\x1a\x04\x98\xb5\x18\x01\x12\x30\n%MessageType_EthereumTypedDataValueAck\x10\xac\r\x1a\x04\x90\xb5\x18\x01\x12\x32\n\'MessageType_EthereumClearSignDefinition\x10\xad\r\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_EthereumClearSignDefinitionAck\x10\xae\r\x1a\x04\x98\xb5\x18\x01\x12\x39\n.MessageType_EthereumClearSignDefinitionRequest\x10\xaf\r\x1a\x04\x98\xb5\x18\x01\x12\x37\n,MessageType_EthereumClearSignDefinitionChunk\x10\xb0\r\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_GetBip85Mnemonic\x10x\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_Bip85Mnemonic\x10y\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_RippleGetAddress\x10\x90\x03\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_RippleAddress\x10\x91\x03\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_RippleSignTx\x10\x92\x03\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_RippleSignedTx\x10\x93\x03\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ThorchainGetAddress\x10\xf4\x03\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_ThorchainAddress\x10\xf5\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ThorchainSignTx\x10\xf6\x03\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ThorchainMsgRequest\x10\xf7\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ThorchainMsgAck\x10\xf8\x03\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_ThorchainSignedTx\x10\xf9\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_EosGetPublicKey\x10\xd8\x04\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_EosPublicKey\x10\xd9\x04\x1a\x04\x98\xb5\x18\x01\x12 \n\x15MessageType_EosSignTx\x10\xda\x04\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_EosTxActionRequest\x10\xdb\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_EosTxActionAck\x10\xdc\x04\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_EosSignedTx\x10\xdd\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_NanoGetAddress\x10\xbc\x05\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_NanoAddress\x10\xbd\x05\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_NanoSignTx\x10\xbe\x05\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_NanoSignedTx\x10\xbf\x05\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_SolanaGetAddress\x10\xee\x05\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_SolanaAddress\x10\xef\x05\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_SolanaSignTx\x10\xf0\x05\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_SolanaSignedTx\x10\xf1\x05\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_SolanaSignMessage\x10\xf2\x05\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_SolanaMessageSignature\x10\xf3\x05\x1a\x04\x98\xb5\x18\x01\x12\x30\n%MessageType_SolanaSignOffchainMessage\x10\xf4\x05\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_SolanaOffchainMessageSignature\x10\xf5\x05\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_BinanceGetAddress\x10\xa0\x06\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_BinanceAddress\x10\xa1\x06\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_BinanceGetPublicKey\x10\xa2\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinancePublicKey\x10\xa3\x06\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_BinanceSignTx\x10\xa4\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinanceTxRequest\x10\xa5\x06\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_BinanceTransferMsg\x10\xa6\x06\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_BinanceOrderMsg\x10\xa7\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinanceCancelMsg\x10\xa8\x06\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_BinanceSignedTx\x10\xa9\x06\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosGetAddress\x10\x84\x07\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_CosmosAddress\x10\x85\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_CosmosSignTx\x10\x86\x07\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosMsgRequest\x10\x87\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_CosmosMsgAck\x10\x88\x07\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_CosmosSignedTx\x10\x89\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_CosmosMsgDelegate\x10\x8a\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CosmosMsgUndelegate\x10\x8b\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CosmosMsgRedelegate\x10\x8c\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosMsgRewards\x10\x8d\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_CosmosMsgIBCTransfer\x10\x8e\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_TendermintGetAddress\x10\xe8\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TendermintAddress\x10\xe9\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_TendermintSignTx\x10\xea\x07\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_TendermintMsgRequest\x10\xeb\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_TendermintMsgAck\x10\xec\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TendermintMsgSend\x10\xed\x07\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_TendermintSignedTx\x10\xee\x07\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_TendermintMsgDelegate\x10\xef\x07\x1a\x04\x98\xb5\x18\x01\x12.\n#MessageType_TendermintMsgUndelegate\x10\xf0\x07\x1a\x04\x98\xb5\x18\x01\x12.\n#MessageType_TendermintMsgRedelegate\x10\xf1\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_TendermintMsgRewards\x10\xf2\x07\x1a\x04\x98\xb5\x18\x01\x12/\n$MessageType_TendermintMsgIBCTransfer\x10\xf3\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisGetAddress\x10\xcc\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisAddress\x10\xcd\x08\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_OsmosisSignTx\x10\xce\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgRequest\x10\xcf\x08\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_OsmosisMsgAck\x10\xd0\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisMsgSend\x10\xd1\x08\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_OsmosisMsgDelegate\x10\xd2\x08\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_OsmosisMsgUndelegate\x10\xd3\x08\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_OsmosisMsgRedelegate\x10\xd4\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgRewards\x10\xd5\x08\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_OsmosisMsgLPAdd\x10\xd6\x08\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_OsmosisMsgLPRemove\x10\xd7\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgLPStake\x10\xd8\x08\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_OsmosisMsgLPUnstake\x10\xd9\x08\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_OsmosisMsgIBCTransfer\x10\xda\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisMsgSwap\x10\xdb\x08\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_OsmosisSignedTx\x10\xdc\x08\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_MayachainGetAddress\x10\xb0\t\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_MayachainAddress\x10\xb1\t\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_MayachainSignTx\x10\xb2\t\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_MayachainMsgRequest\x10\xb3\t\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_MayachainMsgAck\x10\xb4\t\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_MayachainSignedTx\x10\xb5\t\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_ZcashSignPCZT\x10\x94\n\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_ZcashPCZTAction\x10\x95\n\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_ZcashPCZTActionAck\x10\x96\n\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ZcashSignedPCZT\x10\x97\n\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_ZcashGetOrchardFVK\x10\x98\n\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_ZcashOrchardFVK\x10\x99\n\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_ZcashTransparentInput\x10\x9a\n\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_ZcashTransparentSigned\x10\x9b\n\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_ZcashDisplayAddress\x10\x9c\n\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_ZcashAddress\x10\x9d\n\x1a\x04\x98\xb5\x18\x01\x12-\n\"MessageType_ZcashTransparentOutput\x10\x9e\n\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ZcashTransparentAck\x10\x9f\n\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_TronGetAddress\x10\xf8\n\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TronAddress\x10\xf9\n\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_TronSignTx\x10\xfa\n\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_TronSignedTx\x10\xfb\n\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_TronSignMessage\x10\xfc\n\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_TronMessageSignature\x10\xfd\n\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_TronVerifyMessage\x10\xfe\n\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TronSignTypedHash\x10\xff\n\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_TronTypedDataSignature\x10\x80\x0b\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_TonGetAddress\x10\xdc\x0b\x1a\x04\x90\xb5\x18\x01\x12!\n\x16MessageType_TonAddress\x10\xdd\x0b\x1a\x04\x98\xb5\x18\x01\x12 \n\x15MessageType_TonSignTx\x10\xde\x0b\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TonSignedTx\x10\xdf\x0b\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_TonSignMessage\x10\xe0\x0b\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_TonMessageSignature\x10\xe1\x0b\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_HiveGetPublicKey\x10\xc0\x0c\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_HivePublicKey\x10\xc1\x0c\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_HiveSignTx\x10\xc2\x0c\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_HiveSignedTx\x10\xc3\x0c\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_HiveGetPublicKeys\x10\xc4\x0c\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_HivePublicKeys\x10\xc5\x0c\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_HiveSignAccountCreate\x10\xc6\x0c\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_HiveSignedAccountCreate\x10\xc7\x0c\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_HiveSignAccountUpdate\x10\xc8\x0c\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_HiveSignedAccountUpdate\x10\xc9\x0c\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_NearGetAddress\x10\xca\x0c\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_NearAddress\x10\xcb\x0c\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_NearSignTx\x10\xcc\x0c\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_NearSignedTx\x10\xcd\x0c\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_HiveSignMessage\x10\xce\x0c\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_HiveSignedMessage\x10\xcf\x0c\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_HiveSignOperations\x10\xd0\x0c\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_HiveSignedOperations\x10\xd1\x0c\x1a\x04\x98\xb5\x18\x01\x12\x34\n)MessageType_ClearsignAttestorGetPublicKey\x10\xa4\r\x1a\x04\x90\xb5\x18\x01\x12\x31\n&MessageType_ClearsignAttestorPublicKey\x10\xa5\r\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_ClearsignAttestorSign\x10\xa6\r\x1a\x04\x90\xb5\x18\x01\x12\x31\n&MessageType_ClearsignAttestorSignature\x10\xa7\r\x1a\x04\x98\xb5\x18\x01\x42,\n\x1a\x63om.keepkey.deviceprotocolB\x0eKeepKeyMessage') , dependencies=[types__pb2.DESCRIPTOR,]) @@ -926,8 +926,8 @@ ], containing_type=None, options=None, - serialized_start=6602, - serialized_end=15629, + serialized_start=6636, + serialized_end=15663, ) _sym_db.RegisterEnumDescriptor(_MESSAGETYPE) @@ -1263,11 +1263,15 @@ name='CAPABILITY_THOR_DEPOSIT_REVIEW', index=24, number=24, options=None, type=None), + _descriptor.EnumValueDescriptor( + name='CAPABILITY_TRON_TRC20_REVIEW', index=25, number=25, + options=None, + type=None), ], containing_type=None, options=None, serialized_start=762, - serialized_end=1710, + serialized_end=1744, ) _sym_db.RegisterEnumDescriptor(_FEATURES_CAPABILITY) @@ -1537,7 +1541,7 @@ oneofs=[ ], serialized_start=61, - serialized_end=1710, + serialized_end=1744, ) @@ -1574,8 +1578,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1712, - serialized_end=1754, + serialized_start=1746, + serialized_end=1788, ) @@ -1619,8 +1623,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1756, - serialized_end=1832, + serialized_start=1790, + serialized_end=1866, ) @@ -1643,8 +1647,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1834, - serialized_end=1848, + serialized_start=1868, + serialized_end=1882, ) @@ -1702,8 +1706,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1850, - serialized_end=1971, + serialized_start=1884, + serialized_end=2005, ) @@ -1733,8 +1737,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1973, - serialized_end=2000, + serialized_start=2007, + serialized_end=2034, ) @@ -1792,8 +1796,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2003, - serialized_end=2138, + serialized_start=2037, + serialized_end=2172, ) @@ -1823,8 +1827,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2140, - serialized_end=2166, + serialized_start=2174, + serialized_end=2200, ) @@ -1861,8 +1865,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2168, - serialized_end=2222, + serialized_start=2202, + serialized_end=2256, ) @@ -1899,8 +1903,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2224, - serialized_end=2287, + serialized_start=2258, + serialized_end=2321, ) @@ -1923,8 +1927,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2289, - serialized_end=2300, + serialized_start=2323, + serialized_end=2334, ) @@ -1954,8 +1958,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2302, - serialized_end=2357, + serialized_start=2336, + serialized_end=2391, ) @@ -1985,8 +1989,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2359, - serialized_end=2386, + serialized_start=2393, + serialized_end=2420, ) @@ -2009,8 +2013,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2388, - serialized_end=2396, + serialized_start=2422, + serialized_end=2430, ) @@ -2033,8 +2037,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2398, - serialized_end=2417, + serialized_start=2432, + serialized_end=2451, ) @@ -2064,8 +2068,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2419, - serialized_end=2454, + serialized_start=2453, + serialized_end=2488, ) @@ -2095,8 +2099,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2456, - serialized_end=2482, + serialized_start=2490, + serialized_end=2516, ) @@ -2126,8 +2130,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2484, - serialized_end=2510, + serialized_start=2518, + serialized_end=2544, ) @@ -2185,8 +2189,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2513, - serialized_end=2675, + serialized_start=2547, + serialized_end=2709, ) @@ -2223,8 +2227,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2677, - serialized_end=2729, + serialized_start=2711, + serialized_end=2763, ) @@ -2282,8 +2286,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2732, - serialized_end=2911, + serialized_start=2766, + serialized_end=2945, ) @@ -2313,8 +2317,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2913, - serialized_end=2939, + serialized_start=2947, + serialized_end=2973, ) @@ -2337,8 +2341,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2941, - serialized_end=2953, + serialized_start=2975, + serialized_end=2987, ) @@ -2417,8 +2421,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2956, - serialized_end=3143, + serialized_start=2990, + serialized_end=3177, ) @@ -2518,8 +2522,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3146, - serialized_end=3412, + serialized_start=3180, + serialized_end=3446, ) @@ -2542,8 +2546,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3414, - serialized_end=3430, + serialized_start=3448, + serialized_end=3464, ) @@ -2573,8 +2577,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3432, - serialized_end=3461, + serialized_start=3466, + serialized_end=3495, ) @@ -2667,8 +2671,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3464, - serialized_end=3719, + serialized_start=3498, + serialized_end=3753, ) @@ -2691,8 +2695,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3721, - serialized_end=3734, + serialized_start=3755, + serialized_end=3768, ) @@ -2722,8 +2726,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3736, - serialized_end=3759, + serialized_start=3770, + serialized_end=3793, ) @@ -2760,8 +2764,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3761, - serialized_end=3820, + serialized_start=3795, + serialized_end=3854, ) @@ -2805,8 +2809,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3822, - serialized_end=3885, + serialized_start=3856, + serialized_end=3919, ) @@ -2857,8 +2861,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3888, - serialized_end=4018, + serialized_start=3922, + serialized_end=4052, ) @@ -2909,8 +2913,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4020, - serialized_end=4116, + serialized_start=4054, + serialized_end=4150, ) @@ -2947,8 +2951,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4118, - serialized_end=4172, + serialized_start=4152, + serialized_end=4206, ) @@ -3006,8 +3010,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4174, - serialized_end=4292, + serialized_start=4208, + serialized_end=4326, ) @@ -3051,8 +3055,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4294, - serialized_end=4358, + serialized_start=4328, + serialized_end=4392, ) @@ -3103,8 +3107,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4360, - serialized_end=4441, + serialized_start=4394, + serialized_end=4475, ) @@ -3141,8 +3145,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4443, - serialized_end=4495, + serialized_start=4477, + serialized_end=4529, ) @@ -3214,8 +3218,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4498, - serialized_end=4638, + serialized_start=4532, + serialized_end=4672, ) @@ -3245,8 +3249,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4640, - serialized_end=4673, + serialized_start=4674, + serialized_end=4707, ) @@ -3283,8 +3287,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4675, - serialized_end=4728, + serialized_start=4709, + serialized_end=4762, ) @@ -3314,8 +3318,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4730, - serialized_end=4763, + serialized_start=4764, + serialized_end=4797, ) @@ -3401,8 +3405,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4766, - serialized_end=4972, + serialized_start=4800, + serialized_end=5006, ) @@ -3446,8 +3450,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4975, - serialized_end=5108, + serialized_start=5009, + serialized_end=5142, ) @@ -3477,8 +3481,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5110, - serialized_end=5147, + serialized_start=5144, + serialized_end=5181, ) @@ -3508,8 +3512,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5149, - serialized_end=5192, + serialized_start=5183, + serialized_end=5226, ) @@ -3560,8 +3564,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5194, - serialized_end=5319, + serialized_start=5228, + serialized_end=5353, ) @@ -3605,8 +3609,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5321, - serialized_end=5393, + serialized_start=5355, + serialized_end=5427, ) @@ -3636,8 +3640,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5395, - serialized_end=5439, + serialized_start=5429, + serialized_end=5473, ) @@ -3681,8 +3685,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5441, - serialized_end=5504, + serialized_start=5475, + serialized_end=5538, ) @@ -3726,8 +3730,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5506, - serialized_end=5564, + serialized_start=5540, + serialized_end=5598, ) @@ -3757,8 +3761,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5566, - serialized_end=5599, + serialized_start=5600, + serialized_end=5633, ) @@ -3795,8 +3799,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5601, - serialized_end=5654, + serialized_start=5635, + serialized_end=5688, ) @@ -3826,8 +3830,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5656, - serialized_end=5698, + serialized_start=5690, + serialized_end=5732, ) @@ -3850,8 +3854,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5700, - serialized_end=5711, + serialized_start=5734, + serialized_end=5745, ) @@ -3874,8 +3878,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5713, - serialized_end=5728, + serialized_start=5747, + serialized_end=5762, ) @@ -3912,8 +3916,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5730, - serialized_end=5785, + serialized_start=5764, + serialized_end=5819, ) @@ -3950,8 +3954,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5787, - serialized_end=5837, + serialized_start=5821, + serialized_end=5871, ) @@ -3974,8 +3978,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5839, - serialized_end=5858, + serialized_start=5873, + serialized_end=5892, ) @@ -4117,8 +4121,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5861, - serialized_end=6270, + serialized_start=5895, + serialized_end=6304, ) @@ -4141,8 +4145,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6272, - serialized_end=6287, + serialized_start=6306, + serialized_end=6321, ) @@ -4186,8 +4190,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6289, - serialized_end=6348, + serialized_start=6323, + serialized_end=6382, ) @@ -4210,8 +4214,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6350, - serialized_end=6371, + serialized_start=6384, + serialized_end=6405, ) @@ -4241,8 +4245,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6373, - serialized_end=6405, + serialized_start=6407, + serialized_end=6439, ) @@ -4265,8 +4269,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6407, - serialized_end=6438, + serialized_start=6441, + serialized_end=6472, ) @@ -4296,8 +4300,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6440, - serialized_end=6488, + serialized_start=6474, + serialized_end=6522, ) @@ -4327,8 +4331,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6490, - serialized_end=6530, + serialized_start=6524, + serialized_end=6564, ) @@ -4365,8 +4369,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6532, - serialized_end=6599, + serialized_start=6566, + serialized_end=6633, ) _FEATURES.fields_by_name['coins'].message_type = types__pb2._COINTYPE From 5dee326902a387a9a6702e6a1f212aa7aa632d8e Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 21:17:29 -0500 Subject: [PATCH 03/37] test(tron): USDT clear-signs; unknown TRC-20 contracts need AdvancedMode Gated on 7.15.0 and the tron-trc20-review capability, so 7.15 builds without the trusted token table skip. Checks the screen text and verifies the signature over sha256(raw_data) against the account's address. --- tests/test_msg_tron_signtx.py | 120 +++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/tests/test_msg_tron_signtx.py b/tests/test_msg_tron_signtx.py index 6d9d8659..315e02ba 100644 --- a/tests/test_msg_tron_signtx.py +++ b/tests/test_msg_tron_signtx.py @@ -16,13 +16,66 @@ _has_tron = False import common import binascii +import hashlib import struct +from ecdsa import SECP256k1, VerifyingKey +from ecdsa.util import sigdecode_string + from keepkeylib import messages_pb2 as messages from keepkeylib import messages_tron_pb2 as tron_messages from keepkeylib import types_pb2 as types from keepkeylib.client import CallException -from keepkeylib.tools import parse_path +from keepkeylib.signed_metadata import keccak256 +from keepkeylib.tools import b58decode, b58encode, parse_path + + +USDT = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" # tether.to supported-protocols +TRIGGER_URL = b"type.googleapis.com/protocol.TriggerSmartContract" + + +def _varint(value): + out = bytearray() + while value >= 0x80: + out.append((value & 0x7F) | 0x80) + value >>= 7 + out.append(value) + return bytes(out) + + +def _field(number, payload): + """Length-delimited protobuf field.""" + return _varint(number << 3 | 2) + _varint(len(payload)) + payload + + +def _uint(number, value): + return _varint(number << 3) + _varint(value) + + +def _tron_raw(address): + """21-byte TRON address from Base58Check.""" + raw = b58decode(address, None) + payload, checksum = raw[:-4], raw[-4:] + assert hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4] == checksum + assert len(payload) == 21 and payload[0] == 0x41 + return payload + + +def _tron_address(raw21): + checksum = hashlib.sha256(hashlib.sha256(raw21).digest()).digest()[:4] + return b58encode(raw21 + checksum) + + +def _trc20_transfer_raw_data(owner, contract, to, amount, fee_limit): + """protocol.Transaction.raw for one TriggerSmartContract transfer().""" + calldata = (binascii.unhexlify("a9059cbb") + b"\0" * 12 + to[1:] + + amount.to_bytes(32, "big")) + trigger = _field(1, owner) + _field(2, contract) + _field(4, calldata) + contract_msg = _uint(1, 31) + _field(2, _field(1, TRIGGER_URL) + + _field(2, trigger)) + return (_field(1, b"\xab\xcd") + _field(4, b"\x42" * 8) + + _uint(8, 1700000000000) + _field(11, contract_msg) + + _uint(14, 1699999990000) + _uint(18, fee_limit)) @unittest.skipUnless(_has_tron, "TRON protobuf messages not available in this build") @@ -164,6 +217,71 @@ def test_tron_sign_trc20_transfer(self): self.assertGreater(len(resp.serialized_tx), 0) + def _walk_sign(self, msg): + """Approve every screen, recording (title, body) as the device drew it.""" + screens = [] + response = self.client.call_raw(msg) + while isinstance(response, messages.ButtonRequest): + screens.append(self.client.debug.read_confirm_text()) + self.client.debug.press_yes() + response = self.client.call_raw(messages.ButtonAck()) + return response, screens + + def _trc20_request(self, contract): + path = parse_path("m/44'/195'/0'/0/0") + owner = self.client.call(tron_messages.TronGetAddress( + address_n=path, show_display=False)).address + to = b"\x41" + b"\x22" * 20 + raw_data = _trc20_transfer_raw_data(_tron_raw(owner), contract, to, + 1500000, 30000000) + return owner, to, raw_data, tron_messages.TronSignTx( + address_n=path, raw_data=raw_data) + + def test_tron_sign_trc20_usdt_clear_signs(self): + """USDT is in the firmware's trusted TRC-20 table: the transfer + clear-signs without AdvancedMode, in token units, and the signature + covers sha256(raw_data) under the account's own key.""" + self.requires_fullFeature() + self.requires_firmware("7.15.0") + self.requires_release_capability("tron-trc20-review") + self.setup_mnemonic_allallall() + self.client.apply_policy("AdvancedMode", 0) + + owner, to, raw_data, msg = self._trc20_request(_tron_raw(USDT)) + response, screens = self._walk_sign(msg) + + self.assertIsInstance(response, tron_messages.TronSignedTx) + self.assertEqual(screens[0], ("TRC-20 Transfer", + "Send 1.5 USDT to %s?" % _tron_address(to))) + self.assertEqual(screens[1][1], + "Energy fee limit 30 TRX\nBandwidth fees are extra") + self.assertEqual(len(screens), 2) + + signature = response.signature + self.assertEqual(len(signature), 65) + digest = hashlib.sha256(raw_data).digest() + candidates = VerifyingKey.from_public_key_recovery_with_digest( + signature[:64], digest, SECP256k1, sigdecode=sigdecode_string) + signers = set() + for key in candidates: + point = key.to_string() # 64-byte uncompressed X || Y + signers.add(_tron_address(b"\x41" + keccak256(point)[-20:])) + self.assertIn(owner, signers) + + def test_tron_sign_trc20_unknown_contract_needs_advanced_mode(self): + """A transfer() call to a contract outside the trusted table is not + clear-signed: without AdvancedMode it is refused before any screen.""" + self.requires_fullFeature() + self.requires_firmware("7.15.0") + self.requires_release_capability("tron-trc20-review") + self.setup_mnemonic_allallall() + self.client.apply_policy("AdvancedMode", 0) + + _, _, _, msg = self._trc20_request(b"\x41" + b"\x33" * 20) + response = self.client.call_raw(msg) + self.assertIsInstance(response, messages.Failure) + self.assertEqual(response.message, "Enable AdvancedMode to blind-sign") + def test_tron_sign_empty_raw_data(self): """Signing with empty raw_data should be rejected by firmware.""" self.requires_fullFeature() From 4db0affae048124ec27115a556a8a944730ce061 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 21:17:30 -0500 Subject: [PATCH 04/37] oled_text: find an address as the QR address screens draw it Address screens wrap at their own widths and pitches beside a QR code, so shows() takes a pitch, ignores the blank top row the QR border may cover, and stops the edge check at the wrap limit. shows_address() tries each layout in app_layout.c; assert_shows_address() runs a show_display call and requires one screen to show the whole value. --- tests/common.py | 27 ++++++++++++++++++++++++ tests/oled_text.py | 51 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/tests/common.py b/tests/common.py index 37b1d36a..39502c35 100644 --- a/tests/common.py +++ b/tests/common.py @@ -27,6 +27,8 @@ import os import semver +import oled_text + from google.protobuf.message import EncodeError from keepkeylib.client import KeepKeyClient, KeepKeyDebuglinkClient, KeepKeyDebuglinkClientVerbose from keepkeylib.transport_udp import EmulatorNotResponding @@ -182,6 +184,31 @@ def setup_mnemonic_pin_passphrase(self): def tearDown(self): self.client.close() + def assert_shows_address(self, call, expected): + """Run `call`, a show_display request, and assert one of the screens + it confirms shows ALL of `expected` -- a known vector or the + show_display=False response, never the value under test. Returns + what `call` returns. Only where neither exists may `expected` be a + function of that result.""" + screens = [] + press = self.client.callback_ButtonRequest + + def capture(msg): + screens.append(self.client._read_oled_after_settle()) + return press(msg) + + self.client.callback_ButtonRequest = capture + try: + result = call() + finally: + del self.client.callback_ButtonRequest + if callable(expected): + expected = expected(result) + self.assertTrue( + any(oled_text.shows_address(s, expected) for s in screens), + "none of %d screens shows all of %r" % (len(screens), expected)) + return result + def assertEqual(self, lhs, rhs): if type(lhs) == type(b'') and type(rhs) == type(''): super(KeepKeyTest, self).assertEqual(lhs, rhs.encode('utf-8')) diff --git a/tests/oled_text.py b/tests/oled_text.py index b67d0b7f..a2247aa0 100644 --- a/tests/oled_text.py +++ b/tests/oled_text.py @@ -15,7 +15,8 @@ draw_string_walk() in lib/board/draw.c breaks them (a 44-character Solana address always wraps), each line whole -- nothing drawn just before or after it -- and the lines left-aligned on consecutive rows of ONE screen. A body -that pages is not found; assert each page's part instead. +that pages is not found; assert each page's part instead. shows_address() +finds an address or key the way the QR-code address screens draw it. """ from __future__ import print_function @@ -145,16 +146,23 @@ def _render(line, font): return x, rows -def _at(rows, want, width, x, y, whole): +def _at(rows, want, width, x, y, whole, end=256): """`want` drawn at (x, y); if `whole`, with nothing lit within a glyph's - width on either side of it (a longer line is not this line).""" + width on either side of it (a longer line is not this line) -- short of + `end`, the wrap limit, past which the line cannot continue and an icon + may sit. A row with no ink in `want` is not compared: layout_address() + draws the QR code after the text, and its border may cover the line's + blank top row.""" mask = (1 << width) - 1 - if any((rows[y + r] >> x) & mask != want[r] for r in range(GLYPH_HEIGHT)): + if any(want[r] and (rows[y + r] >> x) & mask != want[r] + for r in range(GLYPH_HEIGHT)): return False if not whole: return True before = min(x, 9) - edges = ((1 << before) - 1) << (x - before) | 0x1ff << (x + width) + after = max(0, min(end - x - width, 9)) + edges = ((1 << before) - 1) << (x - before) | \ + ((1 << after) - 1) << (x + width) return not any(rows[y + r] & edges for r in range(GLYPH_HEIGHT)) @@ -170,19 +178,44 @@ def find_line(layout, line, font=BODY_FONT): return None -def shows(layout, text, font=BODY_FONT, width=BODY_WIDTH): - """Whether this screen shows `text` as confirm() draws a body.""" +def shows(layout, text, font=BODY_FONT, width=BODY_WIDTH, + pitch=BODY_LINE_PITCH): + """Whether this screen shows `text` as confirm() draws a body, or as + draw_string() draws it at another wrap width and line pitch.""" lines = [_render(line, font) for line in wrap(text, font, width)] rows = _rows(layout) - height = (len(lines) - 1) * BODY_LINE_PITCH + GLYPH_HEIGHT + height = (len(lines) - 1) * pitch + GLYPH_HEIGHT for y in range(64 - height + 1): for x in range(256 - lines[0][0] + 1): - if all(_at(rows, want, w, x, y + i * BODY_LINE_PITCH, True) + if all(_at(rows, want, w, x, y + i * pitch, True, x + width) for i, (w, want) in enumerate(lines)): return True return False +# How lib/firmware/app_layout.c draws an address or key beside its QR code, +# as (font, wrap width, line pitch): +ADDRESS_LAYOUTS = ( + # layout_address_notification(): bold while it fits TRANSACTION_WIDTH... + (TITLE_FONT, 250, BODY_LINE_PITCH), + # ...else the body font, its lines closed up to the font height. + (BODY_FONT, 250, GLYPH_HEIGHT), + # layout_{ethereum,cosmos,nano}_address_notification(); two lines fit. + (BODY_FONT, 140, BODY_LINE_PITCH), + # layout_osmosis_address_notification() + (BODY_FONT, 160, BODY_LINE_PITCH), + # confirm() bodies and layout_xpub_notification() (TRANSACTION_WIDTH - 25) + (BODY_FONT, BODY_WIDTH, BODY_LINE_PITCH), +) + + +def shows_address(layout, text): + """Whether this screen shows ALL of `text` in one of ADDRESS_LAYOUTS. A + layout that drops the lines past the bottom of the screen does not.""" + return any(shows(layout, text, font, width, pitch) + for font, width, pitch in ADDRESS_LAYOUTS) + + def find_text(screens, text, font=BODY_FONT, width=BODY_WIDTH, start=0): """Index of the first screen from `start` that shows `text`, or None.""" for i in range(start, len(screens)): From af7689f7afd57fef173d2f2978bf00703983deb0 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 21:17:30 -0500 Subject: [PATCH 05/37] Assert every shown address and key is on screen in full A Hive STM key lost its tail on the device and no test noticed. Each show_display test now checks the screen against a known vector or the show_display=False response. Adds Hive, THORChain, Maya, Osmosis and EOS show tests, and stops the TON and Tron show tests swallowing errors. --- tests/test_msg_cosmos_getaddress.py | 18 +++++++++++------ tests/test_msg_eos_getpublickey.py | 11 +++++++++++ tests/test_msg_ethereum_getaddress.py | 8 +++++--- tests/test_msg_getaddress_segwit.py | 15 +++++++++++--- tests/test_msg_getaddress_segwit_native.py | 5 ++++- tests/test_msg_getaddress_show.py | 19 +++++++++++++----- tests/test_msg_getaddress_taproot.py | 13 ++++++------ tests/test_msg_getpublickey.py | 5 ++++- tests/test_msg_hive.py | 17 ++++++++++++++++ tests/test_msg_mayachain_getaddress.py | 11 +++++++++++ tests/test_msg_nano_getaddress.py | 3 ++- tests/test_msg_osmosis_getaddress.py | 23 ++++++++++++++++++++++ tests/test_msg_ripple_get_address.py | 6 ++++-- tests/test_msg_solana_getaddress.py | 16 +++++++++------ tests/test_msg_thorchain_getaddress.py | 11 +++++++++++ tests/test_msg_ton_getaddress.py | 23 +++++++++++----------- tests/test_msg_tron_getaddress.py | 21 ++++++++++---------- tests/test_msg_zcash_display_address.py | 16 +++++++++------ 18 files changed, 180 insertions(+), 61 deletions(-) create mode 100644 tests/test_msg_osmosis_getaddress.py diff --git a/tests/test_msg_cosmos_getaddress.py b/tests/test_msg_cosmos_getaddress.py index 86d6691e..ca619356 100644 --- a/tests/test_msg_cosmos_getaddress.py +++ b/tests/test_msg_cosmos_getaddress.py @@ -28,7 +28,9 @@ def test_standard(self): cosmos_proto.CosmosAddress(address=expected) ]) - self.assertEqual(expected, self.client.cosmos_get_address(path, show_display=True)) + self.assertEqual(expected, self.assert_shows_address( + lambda: self.client.cosmos_get_address(path, show_display=True), + expected)) with self.client: self.client.set_expected_responses([ @@ -61,7 +63,9 @@ def test_nonstandard(self): cosmos_proto.CosmosAddress(address=expected) ]) - self.assertEqual(expected, self.client.cosmos_get_address(path, show_display=True)) + self.assertEqual(expected, self.assert_shows_address( + lambda: self.client.cosmos_get_address(path, show_display=True), + expected)) with self.client: self.client.set_expected_responses([ @@ -85,10 +89,12 @@ def test_cosmos_get_address_sep(self): address = self.client.cosmos_get_address(parse_path(DEFAULT_BIP32_PATH)) assert address == "cosmos1jcwdsdelc4cwvall0twl974sfkpqmzrgkszu9l" - address = self.client.cosmos_get_address( - parse_path("m/44h/118h/1h/0/0"), show_display=True - ) - assert address == "cosmos1280uphuty5rxr2m05t6xujvylkkftlrvdnw0pp" + expected = "cosmos1280uphuty5rxr2m05t6xujvylkkftlrvdnw0pp" + address = self.assert_shows_address( + lambda: self.client.cosmos_get_address( + parse_path("m/44h/118h/1h/0/0"), show_display=True), + expected) + assert address == expected def test_onchain(self): self.requires_fullFeature() diff --git a/tests/test_msg_eos_getpublickey.py b/tests/test_msg_eos_getpublickey.py index d1121ef3..75d82d58 100644 --- a/tests/test_msg_eos_getpublickey.py +++ b/tests/test_msg_eos_getpublickey.py @@ -51,6 +51,17 @@ def test(self): res = self.client.eos_get_public_key(path, show, legacy) self.assertEqual(res.wif_public_key, wif) + def test_show(self): + self.requires_fullFeature() + self.requires_message("EosGetPublicKey") + self.setup_mnemonic_nopin_nopassphrase() + for legacy, prefix in ((True, 'EOS'), (False, 'EOS_K1_')): + wif = prefix + EOS_ACCOUNT_0_PUBKEY + res = self.assert_shows_address( + lambda: self.client.eos_get_public_key( + EOS_ACCOUNT_0_PATH, True, legacy), wif) + self.assertEqual(res.wif_public_key, wif) + def test_trezor(self): self.requires_fullFeature() self.setup_mnemonic_abandon() diff --git a/tests/test_msg_ethereum_getaddress.py b/tests/test_msg_ethereum_getaddress.py index 501ad8d6..39f1f605 100644 --- a/tests/test_msg_ethereum_getaddress.py +++ b/tests/test_msg_ethereum_getaddress.py @@ -38,9 +38,11 @@ def test_ethereum_show_address(self): self.requires_fullFeature() self.setup_mnemonic_nopin_nopassphrase() # The legacy display response is empty after the ButtonAck; address - # correctness is covered above. This case exists to retain the actual - # operation-specific OLED sequence for visual review. - self.client.ethereum_get_address([], show_display=True) + # correctness is covered above. The screen must show the whole + # EIP-55 form of the first vector above. + self.assert_shows_address( + lambda: self.client.ethereum_get_address([], show_display=True), + '0x1D1C328764A41BdA0492b66bAA30C4A339FF85EF') if __name__ == '__main__': unittest.main() diff --git a/tests/test_msg_getaddress_segwit.py b/tests/test_msg_getaddress_segwit.py index 8a9202a2..40353a59 100644 --- a/tests/test_msg_getaddress_segwit.py +++ b/tests/test_msg_getaddress_segwit.py @@ -28,7 +28,10 @@ class TestMsgGetaddressSegwit(common.KeepKeyTest): def test_show_segwit(self): self.setup_mnemonic_allallall() self.client.clear_session() - self.assertEqual(self.client.get_address("Testnet", parse_path("49'/1'/0'/1/0"), True, None, script_type=proto.SPENDP2SHWITNESS), '2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX') + address = '2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX' + self.assertEqual(self.assert_shows_address( + lambda: self.client.get_address("Testnet", parse_path("49'/1'/0'/1/0"), True, None, script_type=proto.SPENDP2SHWITNESS), + address), address) self.assertEqual(self.client.get_address("Testnet", parse_path("49'/1'/0'/0/0"), False, None, script_type=proto.SPENDP2SHWITNESS), '2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp') self.assertEqual(self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.SPENDP2SHWITNESS), '2N6UeBoqYEEnybg4cReFYDammpsyDw8R2Mc') self.assertEqual(self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.SPENDADDRESS), 'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q') @@ -37,7 +40,10 @@ def test_grs(self): self.requires_fullFeature() self.setup_mnemonic_allallall() self.client.clear_session() - self.assertEqual(self.client.get_address('Groestlcoin', parse_path("49'/17'/0'/0/0"), True, None, script_type=proto.SPENDP2SHWITNESS), '31inaRqambLsd9D7Ke4USZmGEVd3PHkh7P') + address = '31inaRqambLsd9D7Ke4USZmGEVd3PHkh7P' + self.assertEqual(self.assert_shows_address( + lambda: self.client.get_address('Groestlcoin', parse_path("49'/17'/0'/0/0"), True, None, script_type=proto.SPENDP2SHWITNESS), + address), address) self.assertEqual(self.client.get_address('Groestlcoin', parse_path("49'/17'/0'/1/0"), False, None, script_type=proto.SPENDP2SHWITNESS), '3NH9SuUAjw1ZocQdTDMuqm3My3Mcg3ovEV') self.assertEqual(self.client.get_address('Groestlcoin', parse_path("49'/17'/0'/1/1"), False, None, script_type=proto.SPENDP2SHWITNESS), '3D65LEJYJ2Yda6UJr8tYBWspP5MZSeR5wz') @@ -45,7 +51,10 @@ def test_tgrs(self): self.requires_fullFeature() self.setup_mnemonic_allallall() self.client.clear_session() - self.assertEqual(self.client.get_address('GRS Testnet', parse_path("49'/1'/0'/0/0"), True, None, script_type=proto.SPENDP2SHWITNESS), '2N4Q5FhU2497BryFfUgbqkAJE87aKDv3V3e') + address = '2N4Q5FhU2497BryFfUgbqkAJE87aKDv3V3e' + self.assertEqual(self.assert_shows_address( + lambda: self.client.get_address('GRS Testnet', parse_path("49'/1'/0'/0/0"), True, None, script_type=proto.SPENDP2SHWITNESS), + address), address) self.assertEqual(self.client.get_address('GRS Testnet', parse_path("49'/1'/0'/1/0"), False, None, script_type=proto.SPENDP2SHWITNESS), '2N1LGaGg836mqSQqiuUBLfcyGBhyZYBtBZ7') self.assertEqual(self.client.get_address('GRS Testnet', parse_path("49'/1'/0'/1/1"), False, None, script_type=proto.SPENDP2SHWITNESS), '2NFWLCJQBSpz1oUJwwLpX8ECifFWGxQyzGu') diff --git a/tests/test_msg_getaddress_segwit_native.py b/tests/test_msg_getaddress_segwit_native.py index 2cdd0a04..2f167cae 100644 --- a/tests/test_msg_getaddress_segwit_native.py +++ b/tests/test_msg_getaddress_segwit_native.py @@ -29,7 +29,10 @@ def test_show_segwit(self): self.requires_fullFeature() self.setup_mnemonic_allallall() self.client.clear_session() - self.assertEqual(self.client.get_address("Testnet", parse_path("49'/1'/0'/0/0"), True, None, script_type=proto.SPENDWITNESS), 'tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s') + address = 'tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s' + self.assertEqual(self.assert_shows_address( + lambda: self.client.get_address("Testnet", parse_path("49'/1'/0'/0/0"), True, None, script_type=proto.SPENDWITNESS), + address), address) self.assertEqual(self.client.get_address("Testnet", parse_path("49'/1'/0'/1/0"), False, None, script_type=proto.SPENDWITNESS), 'tb1q694ccp5qcc0udmfwgp692u2s2hjpq5h407urtu') self.assertEqual(self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.SPENDWITNESS), 'tb1q54un3q39sf7e7tlfq99d6ezys7qgc62a6rxllc') self.assertEqual(self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.SPENDADDRESS), 'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q') diff --git a/tests/test_msg_getaddress_show.py b/tests/test_msg_getaddress_show.py index 196aeea7..a6aa923a 100644 --- a/tests/test_msg_getaddress_show.py +++ b/tests/test_msg_getaddress_show.py @@ -28,9 +28,12 @@ class TestMsgGetaddress(common.KeepKeyTest): def test_show(self): self.setup_mnemonic_nopin_nopassphrase() - self.assertEqual(self.client.get_address('Bitcoin', [1], show_display=True), '1CK7SJdcb8z9HuvVft3D91HLpLC6KSsGb') - self.assertEqual(self.client.get_address('Bitcoin', [2], show_display=True), '15AeAhtNJNKyowK8qPHwgpXkhsokzLtUpG') - self.assertEqual(self.client.get_address('Bitcoin', [3], show_display=True), '1CmzyJp9w3NafXMSEFH4SLYUPAVCSUrrJ5') + for n, address in ((1, '1CK7SJdcb8z9HuvVft3D91HLpLC6KSsGb'), + (2, '15AeAhtNJNKyowK8qPHwgpXkhsokzLtUpG'), + (3, '1CmzyJp9w3NafXMSEFH4SLYUPAVCSUrrJ5')): + self.assertEqual(self.assert_shows_address( + lambda: self.client.get_address('Bitcoin', [n], show_display=True), + address), address) def test_show_multisig_3(self): self.setup_mnemonic_nopin_nopassphrase() @@ -45,7 +48,10 @@ def test_show_multisig_3(self): ) for i in [1, 2, 3]: - self.assertEqual(self.client.get_address('Bitcoin', [i], show_display=True, multisig=multisig), '3E7GDtuHqnqPmDgwH59pVC7AvySiSkbibz') + address = '3E7GDtuHqnqPmDgwH59pVC7AvySiSkbibz' + self.assertEqual(self.assert_shows_address( + lambda: self.client.get_address('Bitcoin', [i], show_display=True, multisig=multisig), + address), address) def test_show_multisig_15(self): self.setup_mnemonic_nopin_nopassphrase() @@ -63,7 +69,10 @@ def test_show_multisig_15(self): ) for i in range(15): - self.assertEqual(self.client.get_address('Bitcoin', [i], show_display=True, multisig=multisig), '3QaKF8zobqcqY8aS6nxCD5ZYdiRfL3RCmU') + address = '3QaKF8zobqcqY8aS6nxCD5ZYdiRfL3RCmU' + self.assertEqual(self.assert_shows_address( + lambda: self.client.get_address('Bitcoin', [i], show_display=True, multisig=multisig), + address), address) if __name__ == '__main__': unittest.main() diff --git a/tests/test_msg_getaddress_taproot.py b/tests/test_msg_getaddress_taproot.py index 650b8de6..aaec24a8 100644 --- a/tests/test_msg_getaddress_taproot.py +++ b/tests/test_msg_getaddress_taproot.py @@ -64,12 +64,13 @@ def test_show_taproot_address(self): self.requires_taproot() self.setup_mnemonic_abandon() self.client.clear_session() - address = self.client.get_address( - "Bitcoin", parse_path("86'/0'/0'/0/0"), True, None, - script_type=proto.SPENDTAPROOT) - self.assertEqual( - address, - 'bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr') + expected = 'bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr' + address = self.assert_shows_address( + lambda: self.client.get_address( + "Bitcoin", parse_path("86'/0'/0'/0/0"), True, None, + script_type=proto.SPENDTAPROOT), + expected) + self.assertEqual(address, expected) if __name__ == '__main__': diff --git a/tests/test_msg_getpublickey.py b/tests/test_msg_getpublickey.py index 4d43d5c6..596b0da8 100644 --- a/tests/test_msg_getpublickey.py +++ b/tests/test_msg_getpublickey.py @@ -51,7 +51,10 @@ def test_script_type(self): self.assertEqual(self.client.get_public_node([], coin_name="Bitcoin", script_type=proto_types.SPENDADDRESS).xpub, "xpub661MyMwAqRbcF1zGijBb2K6x9YiJPh58xpcCeLvTxMX6spkY3PcpJ4ABcCyWfskq5DDxM3e6Ez5ePCqG5bnPUXR4wL8TZWyoDaUdiWW7bKy") self.assertEqual(self.client.get_public_node([], coin_name="Bitcoin", script_type=proto_types.SPENDP2SHWITNESS).xpub, "ypub6QqdH2c5z7966KBPZ5yDEQCTKWrkLK4dsw8RRjpMLMtyvvZmJ3nNv7pKdQw6fnQkUrLm6XEeheSCGVSpoJCQGm6fofpt9RoHVJYH72ecmVm") self.assertEqual(self.client.get_public_node([], coin_name="Bitcoin", script_type=proto_types.SPENDWITNESS).xpub, "zpub6jftahH18ngZwcNWPSkqSVHxVV1CGw48o3eeD8iEiNGrz2NzYhwwYBUTectgfh4ftVTZqzqDAJnk9n4PWzcR4znGg1XJjLcmm2bvVc3Honv") - self.assertEqual(self.client.get_public_node([], coin_name="Bitcoin", script_type=proto_types.SPENDWITNESS, show_display=True).xpub, "zpub6jftahH18ngZwcNWPSkqSVHxVV1CGw48o3eeD8iEiNGrz2NzYhwwYBUTectgfh4ftVTZqzqDAJnk9n4PWzcR4znGg1XJjLcmm2bvVc3Honv") + zpub = "zpub6jftahH18ngZwcNWPSkqSVHxVV1CGw48o3eeD8iEiNGrz2NzYhwwYBUTectgfh4ftVTZqzqDAJnk9n4PWzcR4znGg1XJjLcmm2bvVc3Honv" + self.assertEqual(self.assert_shows_address( + lambda: self.client.get_public_node([], coin_name="Bitcoin", script_type=proto_types.SPENDWITNESS, show_display=True), + zpub).xpub, zpub) if __name__ == '__main__': unittest.main() diff --git a/tests/test_msg_hive.py b/tests/test_msg_hive.py index 688881c5..52cca18c 100644 --- a/tests/test_msg_hive.py +++ b/tests/test_msg_hive.py @@ -308,6 +308,23 @@ def test_hive_get_public_key_active(self): self.assertEqual(len(resp.raw_public_key), 33) self.assertIn(resp.raw_public_key[0], (2, 3), "compressed pubkey prefix") + def test_hive_show_public_key(self): + """The whole STM key is on the screen: confirm_ethereum_address() had + room for two 140 px rows and silently dropped the tail of the key.""" + self.requires_firmware("7.15.0") + self.requires_message("HiveGetPublicKey") + self.requires_release_capability("hive-release-review") + self.setup_mnemonic_nopin_nopassphrase() + + for role in (ROLE_OWNER, ROLE_ACTIVE, ROLE_MEMO, ROLE_POSTING): + expected = hive.get_public_key( + self.client, hive_path(role), show_display=False).public_key + resp = self.assert_shows_address( + lambda: hive.get_public_key( + self.client, hive_path(role), show_display=True), + expected) + self.assertEqual(resp.public_key, expected) + def test_hive_get_public_keys_all_roles(self): """All four role keys derive, are distinct, and STM-formatted.""" self.requires_firmware("7.15.0") diff --git a/tests/test_msg_mayachain_getaddress.py b/tests/test_msg_mayachain_getaddress.py index 372092e8..68439ec2 100644 --- a/tests/test_msg_mayachain_getaddress.py +++ b/tests/test_msg_mayachain_getaddress.py @@ -17,5 +17,16 @@ def test_mayachain_get_address(self): address = self.client.mayachain_get_address(parse_path(DEFAULT_BIP32_PATH), testnet=True) self.assertEqual(address, "smaya1ls33ayg26kmltw7jjy55p32ghjna09zp2mf0av") + def test_mayachain_show_address(self): + self.requires_firmware("7.9.1") + self.requires_fullFeature() + self.setup_mnemonic_nopin_nopassphrase() + expected = "smaya1ls33ayg26kmltw7jjy55p32ghjna09zp2mf0av" + address = self.assert_shows_address( + lambda: self.client.mayachain_get_address( + parse_path(DEFAULT_BIP32_PATH), show_display=True, testnet=True), + expected) + self.assertEqual(address, expected) + if __name__ == '__main__': unittest.main() diff --git a/tests/test_msg_nano_getaddress.py b/tests/test_msg_nano_getaddress.py index 2ae17ac0..a3def5eb 100644 --- a/tests/test_msg_nano_getaddress.py +++ b/tests/test_msg_nano_getaddress.py @@ -47,7 +47,8 @@ def test(self): ] for path, show, address in vec: - res = self.client.nano_get_address('Nano', path, show) + call = lambda: self.client.nano_get_address('Nano', path, show) + res = self.assert_shows_address(call, address) if show else call() self.assertEqual(res.address, address) if __name__ == '__main__': diff --git a/tests/test_msg_osmosis_getaddress.py b/tests/test_msg_osmosis_getaddress.py new file mode 100644 index 00000000..98b5445d --- /dev/null +++ b/tests/test_msg_osmosis_getaddress.py @@ -0,0 +1,23 @@ +import unittest +import common + +from keepkeylib.tools import parse_path + + +class TestMsgOsmosisGetAddress(common.KeepKeyTest): + + def test_osmosis_show_address(self): + self.requires_fullFeature() + self.requires_firmware("7.7.0") + self.requires_message("OsmosisGetAddress") + self.setup_mnemonic_nopin_nopassphrase() + path = parse_path("m/44h/118h/0h/0/0") + expected = self.client.osmosis_get_address(path) + address = self.assert_shows_address( + lambda: self.client.osmosis_get_address(path, show_display=True), + expected) + self.assertEqual(address, expected) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_msg_ripple_get_address.py b/tests/test_msg_ripple_get_address.py index 8ac0dcd0..05b82577 100644 --- a/tests/test_msg_ripple_get_address.py +++ b/tests/test_msg_ripple_get_address.py @@ -39,8 +39,10 @@ def test_ripple_show_address(self): self.requires_fullFeature() self.requires_firmware("6.4.0") self.setup_mnemonic_allallall() - address = self.client.ripple_get_address( - parse_path("m/44'/144'/0'/0/0"), show_display=True) + address = self.assert_shows_address( + lambda: self.client.ripple_get_address( + parse_path("m/44'/144'/0'/0/0"), show_display=True), + "rNaqKtKrMSwpwZSzRckPf7S96DkimjkF4H") # 7.14.2 and later preserve the response across DebugLinkGetState # requests made during screenshot capture; the same fix is on all three # products, so the floor is the release the fix first shipped in. diff --git a/tests/test_msg_solana_getaddress.py b/tests/test_msg_solana_getaddress.py index 8a5abfb7..db767fc9 100644 --- a/tests/test_msg_solana_getaddress.py +++ b/tests/test_msg_solana_getaddress.py @@ -72,13 +72,17 @@ def test_solana_show_address(self): self.requires_message("SolanaGetAddress") self.setup_mnemonic_allallall() - resp = self.client.call( - solana_proto.SolanaGetAddress( - address_n=[H + 44, H + 501, H + 0, H + 0], - show_display=True, - ) - ) + expected = self.client.solana_get_address( + [H + 44, H + 501, H + 0, H + 0]).address + resp = self.assert_shows_address( + lambda: self.client.call( + solana_proto.SolanaGetAddress( + address_n=[H + 44, H + 501, H + 0, H + 0], + show_display=True, + ) + ), expected) self.assertIsInstance(resp, solana_proto.SolanaAddress) + self.assertEqual(resp.address, expected) def test_solana_different_accounts(self): """Different account indices must produce different addresses.""" diff --git a/tests/test_msg_thorchain_getaddress.py b/tests/test_msg_thorchain_getaddress.py index 621926a9..8ceca23c 100644 --- a/tests/test_msg_thorchain_getaddress.py +++ b/tests/test_msg_thorchain_getaddress.py @@ -18,5 +18,16 @@ def test_thorchain_get_address(self): address = self.client.thorchain_get_address(parse_path(DEFAULT_BIP32_PATH), testnet=True) self.assertEqual(address, "tthor1ls33ayg26kmltw7jjy55p32ghjna09zp6z69y8") + def test_thorchain_show_address(self): + self.requires_fullFeature() + self.requires_firmware("7.0.2") + self.setup_mnemonic_nopin_nopassphrase() + expected = "tthor1ls33ayg26kmltw7jjy55p32ghjna09zp6z69y8" + address = self.assert_shows_address( + lambda: self.client.thorchain_get_address( + parse_path(DEFAULT_BIP32_PATH), show_display=True, testnet=True), + expected) + self.assertEqual(address, expected) + if __name__ == '__main__': unittest.main() diff --git a/tests/test_msg_ton_getaddress.py b/tests/test_msg_ton_getaddress.py index c74cc62d..6163ff01 100644 --- a/tests/test_msg_ton_getaddress.py +++ b/tests/test_msg_ton_getaddress.py @@ -45,24 +45,25 @@ def test_ton_get_address(self): self.assertTrue(len(address) > 0, "TON address must be non-empty") def test_ton_show_address(self): - """Display TON address on OLED (triggers ButtonRequest for screenshot). + """The whole TON address is on the screen, and in the response. - In screenshot mode, DebugLink read_layout() can race with the - show_display response. Known issue: raw_address field causes - UnicodeDecodeError. Address correctness verified by test_ton_get_address. + The handler builds its response before the confirm, and before 7.14.2 + a DebugLink read during the confirm could overwrite it (this test used + to swallow a raw_address UnicodeDecodeError), so it runs from 7.14.2. """ - self.requires_firmware("7.14.0") + self.requires_firmware("7.14.2") self.requires_message("TonGetAddress") self.setup_mnemonic_allallall() - try: - resp = self.client.ton_get_address( + expected = self.client.ton_get_address( + parse_path(TON_DEFAULT_PATH), show_display=False) + resp = self.assert_shows_address( + lambda: self.client.ton_get_address( parse_path(TON_DEFAULT_PATH), show_display=True - ) - self.assertIsNotNone(resp) - except (UnicodeDecodeError, Exception): - pass # raw_address proto bug or screenshot race + ), expected.address) + self.assertEqual(resp.address, expected.address) + self.assertEqual(resp.raw_address, expected.raw_address) def test_ton_different_accounts(self): """Different derivation paths must produce different addresses.""" diff --git a/tests/test_msg_tron_getaddress.py b/tests/test_msg_tron_getaddress.py index 21bdabb4..ed62e83f 100644 --- a/tests/test_msg_tron_getaddress.py +++ b/tests/test_msg_tron_getaddress.py @@ -45,23 +45,24 @@ def test_tron_get_address(self): self.assertTrue(address.startswith('T'), "Tron address must start with 'T', got '%s'" % address) def test_tron_show_address(self): - """Display TRON address on OLED (triggers ButtonRequest for screenshot). + """The whole TRON address is on the screen, and in the response. - In screenshot mode, DebugLink read_layout() can race with the - show_display response. Address correctness verified by test_tron_get_address. + The handler builds its response before the confirm, and before 7.14.2 + a DebugLink read during the confirm could overwrite it, so it runs + from 7.14.2. """ - self.requires_firmware("7.14.0") + self.requires_firmware("7.14.2") self.requires_message("TronGetAddress") self.setup_mnemonic_allallall() - try: - resp = self.client.tron_get_address( + expected = self.client.tron_get_address( + parse_path(TRON_DEFAULT_PATH)).address + resp = self.assert_shows_address( + lambda: self.client.tron_get_address( parse_path(TRON_DEFAULT_PATH), show_display=True - ) - self.assertIsNotNone(resp) - except Exception: - pass # Screenshot race -- OLED display still worked + ), expected) + self.assertEqual(resp.address, expected) def test_tron_different_accounts(self): """Different derivation paths must produce different addresses.""" diff --git a/tests/test_msg_zcash_display_address.py b/tests/test_msg_zcash_display_address.py index ebcd24a1..3de0e109 100644 --- a/tests/test_msg_zcash_display_address.py +++ b/tests/test_msg_zcash_display_address.py @@ -40,12 +40,16 @@ def test_zcash_display_address_basic(self): # The device derives its OWN unified address from address_n/account # (the host does not supply address/FVK — those fields are reserved). - resp = self.client.call( - zcash_proto.ZcashDisplayAddress( - address_n=[H + 32, H + 133, H + 0], - account=0, - ) - ) + # No message returns the UA without displaying it and there is no + # independent vector, so this proves only that the screen shows all of + # the address the device returns. + resp = self.assert_shows_address( + lambda: self.client.call( + zcash_proto.ZcashDisplayAddress( + address_n=[H + 32, H + 133, H + 0], + account=0, + ) + ), lambda resp: resp.address) # Device returns the confirmed UA bound to its seed. self.assertIsInstance(resp, zcash_proto.ZcashAddress) From 44ab6c6f72b4290fb686105a9e949c876f114b41 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 21:38:41 -0500 Subject: [PATCH 06/37] Assert Cosmos staking screens show the whole validator address 7.14 and the 7.15 blocks before the fix drop the last characters of a 52-character cosmosvaloper1 address. Covers delegate, undelegate, redelegate (both validators) and rewards; nested display checks now restore the enclosing capture. --- tests/common.py | 6 +++- tests/test_msg_cosmos_signtx.py | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/tests/common.py b/tests/common.py index 39502c35..ba84ba9a 100644 --- a/tests/common.py +++ b/tests/common.py @@ -192,6 +192,7 @@ def assert_shows_address(self, call, expected): function of that result.""" screens = [] press = self.client.callback_ButtonRequest + nested = 'callback_ButtonRequest' in vars(self.client) def capture(msg): screens.append(self.client._read_oled_after_settle()) @@ -201,7 +202,10 @@ def capture(msg): try: result = call() finally: - del self.client.callback_ButtonRequest + if nested: # an enclosing check is capturing too + self.client.callback_ButtonRequest = press + else: + del self.client.callback_ButtonRequest if callable(expected): expected = expected(result) self.assertTrue( diff --git a/tests/test_msg_cosmos_signtx.py b/tests/test_msg_cosmos_signtx.py index 703ed36f..b04d5b35 100644 --- a/tests/test_msg_cosmos_signtx.py +++ b/tests/test_msg_cosmos_signtx.py @@ -5,11 +5,16 @@ from binascii import hexlify, unhexlify import keepkeylib.messages_pb2 as proto +import keepkeylib.messages_cosmos_pb2 as cosmos_proto import keepkeylib.types_pb2 as proto_types from keepkeylib.tools import parse_path DEFAULT_BIP32_PATH = "m/44h/118h/0h/0/0" +# Real 52-character operator addresses (valid bech32): three 140 px rows. +VALIDATOR = "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0" +VALIDATOR_2 = "cosmosvaloper1c4k24jzduc365kywrsvf5ujz4ya6mwympnc4en" + def make_send(from_address, to_address, amount): return { 'type': 'cosmos-sdk/MsgSend', @@ -127,5 +132,57 @@ def test_onchain2(self): self.assertEqual(hexlify(signature.signature), "71295606d64f1fa987fea1af2292d0b735a5c2d5104b7cc3f818a7208ea9b1a504a386c40011242c115f77268c67af841d29137af5d608d21361ebc7e0513a11") + def _sign_staking(self, **msg): + path = parse_path(DEFAULT_BIP32_PATH) + self.client.call(cosmos_proto.CosmosSignTx( + address_n=path, account_number=19637, chain_id="cosmoshub-4", + fee_amount=5000, gas=200000, memo="", sequence=3, msg_count=1)) + resp = self.client.call(cosmos_proto.CosmosMsgAck(**msg)) + self.assertIsInstance(resp, cosmos_proto.CosmosSignedTx) + return resp + + def _delegator(self): + self.setup_mnemonic_nopin_nopassphrase() + return self.client.cosmos_get_address(parse_path(DEFAULT_BIP32_PATH)) + + # 7.14.x drops the third row of a validator address: these skip there. + def test_delegate_shows_whole_validator(self): + self.requires_fullFeature() + self.requires_firmware("7.15.0") + me = self._delegator() + self.assert_shows_address(lambda: self._sign_staking( + delegate=cosmos_proto.CosmosMsgDelegate( + delegator_address=me, validator_address=VALIDATOR, + amount=1000000)), VALIDATOR) + + def test_undelegate_shows_whole_validator(self): + self.requires_fullFeature() + self.requires_firmware("7.15.0") + me = self._delegator() + self.assert_shows_address(lambda: self._sign_staking( + undelegate=cosmos_proto.CosmosMsgUndelegate( + delegator_address=me, validator_address=VALIDATOR, + amount=1000000)), VALIDATOR) + + def test_redelegate_shows_both_whole_validators(self): + self.requires_fullFeature() + self.requires_firmware("7.15.0") + me = self._delegator() + # Nested: the inner check sees the same screens as the outer one. + self.assert_shows_address(lambda: self.assert_shows_address( + lambda: self._sign_staking( + redelegate=cosmos_proto.CosmosMsgRedelegate( + delegator_address=me, validator_src_address=VALIDATOR, + validator_dst_address=VALIDATOR_2, amount=1000000)), + VALIDATOR), VALIDATOR_2) + + def test_rewards_shows_whole_validator(self): + self.requires_fullFeature() + self.requires_firmware("7.15.0") + me = self._delegator() + self.assert_shows_address(lambda: self._sign_staking( + rewards=cosmos_proto.CosmosMsgRewards( + delegator_address=me, validator_address=VALIDATOR)), VALIDATOR) + if __name__ == '__main__': unittest.main() From 3e62deb1fb836e63137caff88742ea2bf35dbd26 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 21:47:49 -0500 Subject: [PATCH 07/37] Read address screens only on 7.15.0 and later Before firmware 88ae1c3be DebugLinkState was built in the shared response arena, so reading the screen during a confirm wiped a response the handler had already built; on a 7.14.3 emulator the cosmos, thor, maya, osmosis, solana, ripple, eos, tron and ton responses came back empty and an xpub confirm wedged. Older firmware now runs the call unwatched, and the TON and Tron show tests gate on 7.15.0, not 7.14.2. --- tests/common.py | 12 +++++++++++- tests/test_msg_ton_getaddress.py | 9 +++++---- tests/test_msg_tron_getaddress.py | 8 ++++---- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/tests/common.py b/tests/common.py index ba84ba9a..568302ae 100644 --- a/tests/common.py +++ b/tests/common.py @@ -189,7 +189,17 @@ def assert_shows_address(self, call, expected): it confirms shows ALL of `expected` -- a known vector or the show_display=False response, never the value under test. Returns what `call` returns. Only where neither exists may `expected` be a - function of that result.""" + function of that result. + + Before 7.15.0 DebugLinkState was built in the shared response arena + (fixed by firmware 88ae1c3be), so reading the screen during the + confirm wiped a response already built -- and wedged an xpub confirm + on 7.14.3. There `call` runs unwatched and nothing is asserted.""" + # Cached Features, not firmware_at_least(): its Initialize would break + # a caller's set_expected_responses(). + features = self.client.features + if (features.major_version, features.minor_version) < (7, 15): + return call() screens = [] press = self.client.callback_ButtonRequest nested = 'callback_ButtonRequest' in vars(self.client) diff --git a/tests/test_msg_ton_getaddress.py b/tests/test_msg_ton_getaddress.py index 6163ff01..667cb232 100644 --- a/tests/test_msg_ton_getaddress.py +++ b/tests/test_msg_ton_getaddress.py @@ -47,11 +47,12 @@ def test_ton_get_address(self): def test_ton_show_address(self): """The whole TON address is on the screen, and in the response. - The handler builds its response before the confirm, and before 7.14.2 - a DebugLink read during the confirm could overwrite it (this test used - to swallow a raw_address UnicodeDecodeError), so it runs from 7.14.2. + The handler builds its response before the confirm. Before 7.15.0 a + DebugLink read during the confirm wiped it (firmware 88ae1c3be fixed + that; this test used to swallow the resulting raw_address + UnicodeDecodeError), so it runs from 7.15.0. """ - self.requires_firmware("7.14.2") + self.requires_firmware("7.15.0") self.requires_message("TonGetAddress") self.setup_mnemonic_allallall() diff --git a/tests/test_msg_tron_getaddress.py b/tests/test_msg_tron_getaddress.py index ed62e83f..da3dd089 100644 --- a/tests/test_msg_tron_getaddress.py +++ b/tests/test_msg_tron_getaddress.py @@ -47,11 +47,11 @@ def test_tron_get_address(self): def test_tron_show_address(self): """The whole TRON address is on the screen, and in the response. - The handler builds its response before the confirm, and before 7.14.2 - a DebugLink read during the confirm could overwrite it, so it runs - from 7.14.2. + The handler builds its response before the confirm. Before 7.15.0 a + DebugLink read during the confirm wiped it (fixed by firmware + 88ae1c3be), so it runs from 7.15.0. """ - self.requires_firmware("7.14.2") + self.requires_firmware("7.15.0") self.requires_message("TronGetAddress") self.setup_mnemonic_allallall() From c04ad72c61e707ef0afe81dcc6691b254c3dc8cd Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 22:48:13 -0500 Subject: [PATCH 08/37] test(solana): attest exactly the accounts the lookup table loads The fixture attested two accounts for a message that loads one, which firmware now refuses to show. Add the partial/padded-list case. --- tests/test_msg_solana_lut_attestation.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/test_msg_solana_lut_attestation.py b/tests/test_msg_solana_lut_attestation.py index 265ec93c..8455de8a 100644 --- a/tests/test_msg_solana_lut_attestation.py +++ b/tests/test_msg_solana_lut_attestation.py @@ -146,7 +146,8 @@ def test_attested_accounts_are_shown_and_blind_sign_still_follows(self): self._load_signer() pk = self._raw_pubkey() raw = self._build_lut_tx(pk) - accounts = [b'\x51' * 32, b'\x52' * 32] + # Exactly the one account the message's lookup table loads. + accounts = [b'\x51' * 32] base_codes, _ = self._screens(raw_tx=raw) # Reload: identities are RAM-only and a completed signing tears the @@ -164,6 +165,23 @@ def test_attested_accounts_are_shown_and_blind_sign_still_follows(self): self.assertEqual(len(att_codes), len(base_codes) + 1 + len(accounts)) self.assertEqual(att_codes[-len(base_codes):], base_codes) + def test_partial_or_padded_list_degrades_to_todays_flow(self): + """A valid attestation is shown only when it lists every account the + message loads: one more or one fewer would make "describes N + account(s)" a misleading claim, so nothing is added.""" + self._load_signer() + pk = self._raw_pubkey() + raw = self._build_lut_tx(pk) # loads exactly one account + base_codes, _ = self._screens(raw_tx=raw) + for accounts in ([b'\x51' * 32, b'\x52' * 32], []): + self._load_signer() + codes, resp = self._screens( + raw_tx=raw, lut_account=accounts, + lut_signature=self._attest(raw, accounts), + lut_signer_key_id=SLOT) + self.assertEqual(len(resp.signature), 64) + self.assertEqual(codes, base_codes) + def test_bad_signature_degrades_to_todays_flow(self): """A signature that does not verify must change NOTHING.""" self._load_signer() From ccd5b69e8996f1e363a8b9794c62eb83cfaa903e Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 22:50:53 -0500 Subject: [PATCH 09/37] test: read address screens on 7.14.3 too 7.14.3 now builds DebugLinkState outside the response arena, so the bitcoin-only report checks every shown address as 7.15 does. --- tests/common.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/common.py b/tests/common.py index 568302ae..0ca314f0 100644 --- a/tests/common.py +++ b/tests/common.py @@ -191,14 +191,15 @@ def assert_shows_address(self, call, expected): what `call` returns. Only where neither exists may `expected` be a function of that result. - Before 7.15.0 DebugLinkState was built in the shared response arena - (fixed by firmware 88ae1c3be), so reading the screen during the - confirm wiped a response already built -- and wedged an xpub confirm - on 7.14.3. There `call` runs unwatched and nothing is asserted.""" + Before 7.14.3 DebugLinkState was built in the shared response arena + (fixed in 7.14.3 and 7.15), so reading the screen during the confirm + wiped a response already built. There `call` runs unwatched and + nothing is asserted.""" # Cached Features, not firmware_at_least(): its Initialize would break # a caller's set_expected_responses(). features = self.client.features - if (features.major_version, features.minor_version) < (7, 15): + if (features.major_version, features.minor_version, + features.patch_version) < (7, 14, 3): return call() screens = [] press = self.client.callback_ButtonRequest From ff86b16c65acc8688f6435de77e257df98112ff2 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Tue, 6 Oct 2026 23:49:27 -0500 Subject: [PATCH 10/37] test: show a native SegWit address on bitcoin-only firmware too The Groestlcoin checks made the whole test full-firmware only, so the bitcoin-only report never displayed a bech32 address. --- tests/test_msg_getaddress_segwit_native.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_msg_getaddress_segwit_native.py b/tests/test_msg_getaddress_segwit_native.py index 2f167cae..44bf9138 100644 --- a/tests/test_msg_getaddress_segwit_native.py +++ b/tests/test_msg_getaddress_segwit_native.py @@ -26,7 +26,6 @@ class TestMsgGetaddressSegwitNative(common.KeepKeyTest): def test_show_segwit(self): - self.requires_fullFeature() self.setup_mnemonic_allallall() self.client.clear_session() address = 'tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s' @@ -36,6 +35,13 @@ def test_show_segwit(self): self.assertEqual(self.client.get_address("Testnet", parse_path("49'/1'/0'/1/0"), False, None, script_type=proto.SPENDWITNESS), 'tb1q694ccp5qcc0udmfwgp692u2s2hjpq5h407urtu') self.assertEqual(self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.SPENDWITNESS), 'tb1q54un3q39sf7e7tlfq99d6ezys7qgc62a6rxllc') self.assertEqual(self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.SPENDADDRESS), 'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q') + + def test_groestlcoin_segwit(self): + # Groestlcoin is not in bitcoin-only firmware; Bitcoin bech32 display + # above runs on every build. + self.requires_fullFeature() + self.setup_mnemonic_allallall() + self.client.clear_session() self.assertEqual(self.client.get_address("Groestlcoin", parse_path("84'/17'/0'/0/0"), False, None, script_type=proto.SPENDWITNESS), 'grs1qw4teyraux2s77nhjdwh9ar8rl9dt7zww8r6lne') self.assertEqual(self.client.get_address("GRS Testnet", parse_path("84'/1'/0'/0/0"), False, None, script_type=proto.SPENDWITNESS), 'tgrs1qkvwu9g3k2pdxewfqr7syz89r3gj557l3ued7ja') From d33e56098faeb23acb47171838d681ed3700f036 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 02:40:34 -0500 Subject: [PATCH 11/37] test(zcash): check and show the user's address on a shielded output A real multi-receiver Unified Address holding the output's Orchard receiver is shown verbatim, in both pools. A valid address with another Orchard receiver is refused before any output screen. Without one the device shows the Orchard-only address as "Orchard address". Repins device-protocol for ZcashPCZTAction.user_address. --- device-protocol | 2 +- keepkeylib/messages_zcash_pb2.py | 55 ++++++++------- tests/test_msg_zcash_sign_pczt_device.py | 87 ++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 25 deletions(-) diff --git a/device-protocol b/device-protocol index 13ce81a4..71a2a239 160000 --- a/device-protocol +++ b/device-protocol @@ -1 +1 @@ -Subproject commit 13ce81a415e9d0023f4a7e6a4e4101eb9d1dd55e +Subproject commit 71a2a239940656e420850e59d62c96b4e1fc89e5 diff --git a/keepkeylib/messages_zcash_pb2.py b/keepkeylib/messages_zcash_pb2.py index 953b2849..ae1c7cb5 100644 --- a/keepkeylib/messages_zcash_pb2.py +++ b/keepkeylib/messages_zcash_pb2.py @@ -20,7 +20,7 @@ name='messages-zcash.proto', package='', syntax='proto2', - serialized_pb=_b('\n\x14messages-zcash.proto\"\xd9\x04\n\rZcashSignPCZT\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x11\n\tpczt_data\x18\x03 \x01(\x0c\x12\x11\n\tn_actions\x18\x04 \x01(\r\x12\x14\n\x0ctotal_amount\x18\x05 \x01(\x04\x12\x0b\n\x03\x66\x65\x65\x18\x06 \x01(\x04\x12\x11\n\tbranch_id\x18\x07 \x01(\r\x12\x15\n\rheader_digest\x18\x08 \x01(\x0c\x12\x1a\n\x12transparent_digest\x18\t \x01(\x0c\x12\x16\n\x0esapling_digest\x18\n \x01(\x0c\x12\x16\n\x0eorchard_digest\x18\x0b \x01(\x0c\x12\x15\n\rorchard_flags\x18\x0c \x01(\r\x12\x1d\n\x15orchard_value_balance\x18\r \x01(\x03\x12\x16\n\x0eorchard_anchor\x18\x0e \x01(\x0c\x12\x12\n\ntx_version\x18\x0f \x01(\r\x12\x18\n\x10version_group_id\x18\x10 \x01(\r\x12\x11\n\tlock_time\x18\x11 \x01(\r\x12\x15\n\rexpiry_height\x18\x12 \x01(\r\x12\x46\n\rshielded_pool\x18\x13 \x01(\x0e\x32\x12.ZcashShieldedPool:\x1bZCASH_SHIELDED_POOL_ORCHARD\x12\x17\n\x0fironwood_digest\x18\x14 \x01(\x0c\x12\x1d\n\x15n_transparent_outputs\x18\x1d \x01(\r\x12\x1c\n\x14n_transparent_inputs\x18\x1e \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x1f \x01(\x0c\"\xa3\x02\n\x0fZcashPCZTAction\x12\r\n\x05index\x18\x01 \x01(\r\x12\r\n\x05\x61lpha\x18\x02 \x01(\x0c\x12\x0f\n\x07sighash\x18\x03 \x01(\x0c\x12\x0e\n\x06\x63v_net\x18\x04 \x01(\x0c\x12\r\n\x05value\x18\x05 \x01(\x04\x12\x10\n\x08is_spend\x18\x06 \x01(\x08\x12\x11\n\tnullifier\x18\x07 \x01(\x0c\x12\x0b\n\x03\x63mx\x18\x08 \x01(\x0c\x12\x0b\n\x03\x65pk\x18\t \x01(\x0c\x12\x13\n\x0b\x65nc_compact\x18\n \x01(\x0c\x12\x10\n\x08\x65nc_memo\x18\x0b \x01(\x0c\x12\x16\n\x0e\x65nc_noncompact\x18\x0c \x01(\x0c\x12\n\n\x02rk\x18\r \x01(\x0c\x12\x16\n\x0eout_ciphertext\x18\x0e \x01(\x0c\x12\x11\n\trecipient\x18\x0f \x01(\x0c\x12\r\n\x05rseed\x18\x10 \x01(\x0c\"(\n\x12ZcashPCZTActionAck\x12\x12\n\nnext_index\x18\x01 \x01(\r\"3\n\x0fZcashSignedPCZT\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"N\n\x12ZcashGetOrchardFVK\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\"Q\n\x0fZcashOrchardFVK\x12\n\n\x02\x61k\x18\x01 \x01(\x0c\x12\n\n\x02nk\x18\x02 \x01(\x0c\x12\x0c\n\x04rivk\x18\x03 \x01(\x0c\x12\x18\n\x10seed_fingerprint\x18\x04 \x01(\x0c\"N\n\x16ZcashTransparentOutput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x15\n\rscript_pubkey\x18\x03 \x01(\x0c\"\xb0\x01\n\x15ZcashTransparentInput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0f\n\x07sighash\x18\x02 \x01(\x0c\x12\x11\n\taddress_n\x18\x03 \x03(\r\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x04\x12\x14\n\x0cprevout_txid\x18\x05 \x01(\x0c\x12\x15\n\rprevout_index\x18\x06 \x01(\r\x12\x10\n\x08sequence\x18\x07 \x01(\r\x12\x15\n\rscript_pubkey\x18\x08 \x01(\x0c\"J\n\x13ZcashTransparentAck\x12\x19\n\x11next_output_index\x18\x01 \x01(\r\x12\x18\n\x10next_input_index\x18\x02 \x01(\r\",\n\x16ZcashTransparentSigned\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\"\x8b\x01\n\x13ZcashDisplayAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x07 \x01(\x0cJ\x04\x08\x03\x10\x04J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07R\x07\x61\x64\x64ressR\x02\x61kR\x02nkR\x04rivk\"9\n\x0cZcashAddress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x18\n\x10seed_fingerprint\x18\x02 \x01(\x0c*V\n\x11ZcashShieldedPool\x12\x1f\n\x1bZCASH_SHIELDED_POOL_ORCHARD\x10\x00\x12 \n\x1cZCASH_SHIELDED_POOL_IRONWOOD\x10\x01\x42\x31\n\x1a\x63om.keepkey.deviceprotocolB\x13KeepKeyMessageZcash') + serialized_pb=_b('\n\x14messages-zcash.proto\"\xd9\x04\n\rZcashSignPCZT\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x11\n\tpczt_data\x18\x03 \x01(\x0c\x12\x11\n\tn_actions\x18\x04 \x01(\r\x12\x14\n\x0ctotal_amount\x18\x05 \x01(\x04\x12\x0b\n\x03\x66\x65\x65\x18\x06 \x01(\x04\x12\x11\n\tbranch_id\x18\x07 \x01(\r\x12\x15\n\rheader_digest\x18\x08 \x01(\x0c\x12\x1a\n\x12transparent_digest\x18\t \x01(\x0c\x12\x16\n\x0esapling_digest\x18\n \x01(\x0c\x12\x16\n\x0eorchard_digest\x18\x0b \x01(\x0c\x12\x15\n\rorchard_flags\x18\x0c \x01(\r\x12\x1d\n\x15orchard_value_balance\x18\r \x01(\x03\x12\x16\n\x0eorchard_anchor\x18\x0e \x01(\x0c\x12\x12\n\ntx_version\x18\x0f \x01(\r\x12\x18\n\x10version_group_id\x18\x10 \x01(\r\x12\x11\n\tlock_time\x18\x11 \x01(\r\x12\x15\n\rexpiry_height\x18\x12 \x01(\r\x12\x46\n\rshielded_pool\x18\x13 \x01(\x0e\x32\x12.ZcashShieldedPool:\x1bZCASH_SHIELDED_POOL_ORCHARD\x12\x17\n\x0fironwood_digest\x18\x14 \x01(\x0c\x12\x1d\n\x15n_transparent_outputs\x18\x1d \x01(\r\x12\x1c\n\x14n_transparent_inputs\x18\x1e \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x1f \x01(\x0c\"\xb9\x02\n\x0fZcashPCZTAction\x12\r\n\x05index\x18\x01 \x01(\r\x12\r\n\x05\x61lpha\x18\x02 \x01(\x0c\x12\x0f\n\x07sighash\x18\x03 \x01(\x0c\x12\x0e\n\x06\x63v_net\x18\x04 \x01(\x0c\x12\r\n\x05value\x18\x05 \x01(\x04\x12\x10\n\x08is_spend\x18\x06 \x01(\x08\x12\x11\n\tnullifier\x18\x07 \x01(\x0c\x12\x0b\n\x03\x63mx\x18\x08 \x01(\x0c\x12\x0b\n\x03\x65pk\x18\t \x01(\x0c\x12\x13\n\x0b\x65nc_compact\x18\n \x01(\x0c\x12\x10\n\x08\x65nc_memo\x18\x0b \x01(\x0c\x12\x16\n\x0e\x65nc_noncompact\x18\x0c \x01(\x0c\x12\n\n\x02rk\x18\r \x01(\x0c\x12\x16\n\x0eout_ciphertext\x18\x0e \x01(\x0c\x12\x11\n\trecipient\x18\x0f \x01(\x0c\x12\r\n\x05rseed\x18\x10 \x01(\x0c\x12\x14\n\x0cuser_address\x18\x11 \x01(\t\"(\n\x12ZcashPCZTActionAck\x12\x12\n\nnext_index\x18\x01 \x01(\r\"3\n\x0fZcashSignedPCZT\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"N\n\x12ZcashGetOrchardFVK\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\"Q\n\x0fZcashOrchardFVK\x12\n\n\x02\x61k\x18\x01 \x01(\x0c\x12\n\n\x02nk\x18\x02 \x01(\x0c\x12\x0c\n\x04rivk\x18\x03 \x01(\x0c\x12\x18\n\x10seed_fingerprint\x18\x04 \x01(\x0c\"N\n\x16ZcashTransparentOutput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x15\n\rscript_pubkey\x18\x03 \x01(\x0c\"\xb0\x01\n\x15ZcashTransparentInput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0f\n\x07sighash\x18\x02 \x01(\x0c\x12\x11\n\taddress_n\x18\x03 \x03(\r\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x04\x12\x14\n\x0cprevout_txid\x18\x05 \x01(\x0c\x12\x15\n\rprevout_index\x18\x06 \x01(\r\x12\x10\n\x08sequence\x18\x07 \x01(\r\x12\x15\n\rscript_pubkey\x18\x08 \x01(\x0c\"J\n\x13ZcashTransparentAck\x12\x19\n\x11next_output_index\x18\x01 \x01(\r\x12\x18\n\x10next_input_index\x18\x02 \x01(\r\",\n\x16ZcashTransparentSigned\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\"\x8b\x01\n\x13ZcashDisplayAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x07 \x01(\x0cJ\x04\x08\x03\x10\x04J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07R\x07\x61\x64\x64ressR\x02\x61kR\x02nkR\x04rivk\"9\n\x0cZcashAddress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x18\n\x10seed_fingerprint\x18\x02 \x01(\x0c*V\n\x11ZcashShieldedPool\x12\x1f\n\x1bZCASH_SHIELDED_POOL_ORCHARD\x10\x00\x12 \n\x1cZCASH_SHIELDED_POOL_IRONWOOD\x10\x01\x42\x31\n\x1a\x63om.keepkey.deviceprotocolB\x13KeepKeyMessageZcash') ) _ZCASHSHIELDEDPOOL = _descriptor.EnumDescriptor( @@ -40,8 +40,8 @@ ], containing_type=None, options=None, - serialized_start=1762, - serialized_end=1848, + serialized_start=1784, + serialized_end=1870, ) _sym_db.RegisterEnumDescriptor(_ZCASHSHIELDEDPOOL) @@ -355,6 +355,13 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='user_address', full_name='ZcashPCZTAction.user_address', index=16, + number=17, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), ], extensions=[ ], @@ -368,7 +375,7 @@ oneofs=[ ], serialized_start=629, - serialized_end=920, + serialized_end=942, ) @@ -398,8 +405,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=922, - serialized_end=962, + serialized_start=944, + serialized_end=984, ) @@ -436,8 +443,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=964, - serialized_end=1015, + serialized_start=986, + serialized_end=1037, ) @@ -481,8 +488,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1017, - serialized_end=1095, + serialized_start=1039, + serialized_end=1117, ) @@ -533,8 +540,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1097, - serialized_end=1178, + serialized_start=1119, + serialized_end=1200, ) @@ -578,8 +585,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1180, - serialized_end=1258, + serialized_start=1202, + serialized_end=1280, ) @@ -658,8 +665,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1261, - serialized_end=1437, + serialized_start=1283, + serialized_end=1459, ) @@ -696,8 +703,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1439, - serialized_end=1513, + serialized_start=1461, + serialized_end=1535, ) @@ -727,8 +734,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1515, - serialized_end=1559, + serialized_start=1537, + serialized_end=1581, ) @@ -772,8 +779,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1562, - serialized_end=1701, + serialized_start=1584, + serialized_end=1723, ) @@ -810,8 +817,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1703, - serialized_end=1760, + serialized_start=1725, + serialized_end=1782, ) _ZCASHSIGNPCZT.fields_by_name['shielded_pool'].enum_type = _ZCASHSHIELDEDPOOL diff --git a/tests/test_msg_zcash_sign_pczt_device.py b/tests/test_msg_zcash_sign_pczt_device.py index 888cd253..3e2daf61 100644 --- a/tests/test_msg_zcash_sign_pczt_device.py +++ b/tests/test_msg_zcash_sign_pczt_device.py @@ -415,6 +415,93 @@ def test_ironwood_note_is_accepted(self): self.assertTrue(len(outputs) == 2, "expected 2 ConfirmOutput screens, got %d" % len(outputs)) + # ZIP 374 user_address. Both addresses are real librustzcash output: + # MULTI_RECEIVER_UA is RECIPIENT plus a Sapling and a P2PKH receiver + # (zcash_address 0.13.0); OTHER_ORCHARD_UA is the "all" seed's account-0 + # address at diversifier index 1 (zcash_keys 0.16.1), a valid mainnet UA + # whose Orchard receiver is not RECIPIENT. + USER_ADDRESS_FIRMWARE = "7.15.0" + MULTI_RECEIVER_UA = ( + 'u16065qzvddm89jcmzufxjs5pe6dr006tezvd7pap2nc58cctca8tt373s2he7xx76cn' + 'lyfatutph9kfl5g35cnuw6szxlf0qhpqajh0xrujjny6rxh6wej6mx6x5zuz4auaffd5' + 'hd56t8kwxnnquasruhg8qv3344cn6dauw00waq8ak2lmlyn8r84jumahr2nrd246gdxw' + '932t8uvgs') + OTHER_ORCHARD_UA = ( + 'u1elnjt36zcqfelwj62v8lujthlqefztqcy02jfm2p5vs9phrzr8fj68j3mpzmvlktay' + 'k9fdz4zd4k3x6f7z3n62dw09w8sr9a8a0ka5m6xktd8hl6x5ekd0qky8h8t0an6p8eqk' + '3ggwnl30dkv7txlw5r2qef330j94r0lftqktn0ev70kc78h8ev43ja5x7de27rvvhf0h' + '4ku663uw6') + + def _capture_confirm_text(self): + """(code, title, body) of each confirmation, as the firmware formatted it.""" + screens = [] + original = self.client.callback_ButtonRequest + + def capture(msg): + title, body = self.client.debug.read_confirm_text() + screens.append((msg.code, title, body)) + return original(msg) + + self.client.callback_ButtonRequest = capture + return screens + + def test_user_address_is_checked_then_shown(self): + """The address the user pasted is shown once it holds the recipient. + + The device decodes the Unified Address, finds its single Orchard + receiver equal to the output's recipient, and shows the user's own + string -- not the Orchard-only address it would otherwise rebuild. + Ironwood outputs use the same Orchard receiver type. + """ + self.requires_firmware(self.USER_ADDRESS_FIRMWARE) + for ironwood in (False, True): + action = (note_action(CMX_IRONWOOD, c_enc=C_ENC_IRONWOOD) + if ironwood else note_action(CMX_ORCHARD)) + action['user_address'] = self.MULTI_RECEIVER_UA + screens = self._capture_confirm_text() + + result = self.client.zcash_sign_pczt( + **sign_kwargs([action], ironwood=ironwood)) + + self.assertIsInstance(result, zcash_proto.ZcashSignedPCZT) + shown = [(title, body) for code, title, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput + and title == 'Shielded recipient'] + self.assertEqual(shown, + [('Shielded recipient', self.MULTI_RECEIVER_UA)]) + + def test_mismatched_user_address_is_refused(self): + """A valid address that does not hold the recipient refuses signing. + + Nothing about the output is shown: the refusal comes before the output + screens, so the user is never asked to approve the wrong address. + """ + self.requires_firmware(self.USER_ADDRESS_FIRMWARE) + action = note_action(CMX_ORCHARD) + action['user_address'] = self.OTHER_ORCHARD_UA + screens = self._capture_confirm_text() + + with self.assertRaises(Exception) as caught: + self.client.zcash_sign_pczt(**sign_kwargs([action])) + self.assertIn('does not match output', str(caught.exception)) + self.assertEqual([c for c, _, _ in screens + if c == proto_types.ButtonRequest_ConfirmOutput], []) + + def test_absent_user_address_shows_the_orchard_address(self): + """Without user_address the device rebuilds an Orchard-only address. + + It is labelled as exactly that, since it is not what the user pasted. + """ + self.requires_firmware(self.USER_ADDRESS_FIRMWARE) + screens = self._capture_confirm_text() + + self.client.zcash_sign_pczt(**sign_kwargs([note_action(CMX_ORCHARD)])) + + shown = [(title, body) for code, title, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput + and title == 'Orchard address'] + self.assertEqual(shown, [('Orchard address', EXPECTED_UA)]) + if __name__ == '__main__': unittest.main() From d0c6e17f882fd9f090873e9b8c6ace41e754f504 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 02:51:54 -0500 Subject: [PATCH 12/37] test(eip712): real-world documents must sign on the device eip712_corpus.json is the firmware repository's corpus (42 documents, one per protocol and shape, written from each protocol's source). Each must sign on the device with the expected domain separator and message hash, recover to the device's address, and end on a screen that names the whole primary type. Gated on 7.15.0 and structured EIP-712. The host encoder now accepts struct type names with ':' up to 47 characters, as the firmware does, so Hyperliquid's HyperliquidTransaction:* actions can be sent. --- keepkeylib/eip712_stream.py | 9 +- tests/eip712_corpus.json | 3756 +++++++++++++++++++++++++++++++ tests/test_msg_eip712_corpus.py | 92 + 3 files changed, 3855 insertions(+), 2 deletions(-) create mode 100644 tests/eip712_corpus.json create mode 100644 tests/test_msg_eip712_corpus.py diff --git a/keepkeylib/eip712_stream.py b/keepkeylib/eip712_stream.py index 9b813267..ab98b462 100644 --- a/keepkeylib/eip712_stream.py +++ b/keepkeylib/eip712_stream.py @@ -31,10 +31,15 @@ # EIP712_MAX_LEAF on the device. MAX_LEAF_BYTES = 1024 MAX_IDENTIFIER_BYTES = 31 +# Struct type names (EIP712_MAX_STRUCT_NAME - 1 on the device) may also use +# ':' after the first character, as Hyperliquid's +# "HyperliquidTransaction:ApproveBuilderFee" does. +MAX_TYPE_NAME_BYTES = 47 _ARRAY_GROUP = re.compile(r'\[([0-9]*)\]') _CANONICAL_DIGITS = re.compile(r'^[1-9][0-9]*$') _IDENTIFIER = re.compile(r'^[A-Za-z_$][A-Za-z0-9_$]*$') +_TYPE_NAME = re.compile(r'^[A-Za-z_$][A-Za-z0-9_$:]*$') class Eip712Error(Exception): @@ -116,7 +121,7 @@ def parse_solidity_type(type_str): 'array_levels': levels, } - if not _IDENTIFIER.match(base) or len(base) > MAX_IDENTIFIER_BYTES: + if not _TYPE_NAME.match(base) or len(base) > MAX_TYPE_NAME_BYTES: raise Eip712Error('Unparseable EIP-712 type: %s' % type_str) return {'data_type': STRUCT, 'struct_name': base, 'array_levels': levels} @@ -227,7 +232,7 @@ def struct_members(typed_data, name): Order is part of the signature: it sets both encodeType and the order encodeData concatenates members. """ - if not isinstance(name, str) or not _IDENTIFIER.match(name) or len(name) > MAX_IDENTIFIER_BYTES: + if not isinstance(name, str) or not _TYPE_NAME.match(name) or len(name) > MAX_TYPE_NAME_BYTES: raise Eip712Error('Struct name is not a canonical EIP-712 identifier') members = typed_data['types'].get(name) if members is None: diff --git a/tests/eip712_corpus.json b/tests/eip712_corpus.json new file mode 100644 index 00000000..2dbd9213 --- /dev/null +++ b/tests/eip712_corpus.json @@ -0,0 +1,3756 @@ +[ + { + "id": "permit2-PermitSingle", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitSingle": [ + { + "name": "details", + "type": "PermitDetails" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "sigDeadline", + "type": "uint256" + } + ], + "PermitDetails": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint160" + }, + { + "name": "expiration", + "type": "uint48" + }, + { + "name": "nonce", + "type": "uint48" + } + ] + }, + "primaryType": "PermitSingle", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "details": { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "1000000", + "expiration": "1893456000", + "nonce": "0" + }, + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "sigDeadline": "1775803746" + } + }, + "expected": { + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "434404194ecf97fbcbf7e55f4bdd2ff1f78f0689ea793bda4b85c7e73060a815", + "digest": "8b4985b6263546864eb8921379708c29bf922304edf4fc0abe5e077f853a60b2" + } + }, + { + "id": "permit2-PermitBatch", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitBatch": [ + { + "name": "details", + "type": "PermitDetails[]" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "sigDeadline", + "type": "uint256" + } + ], + "PermitDetails": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint160" + }, + { + "name": "expiration", + "type": "uint48" + }, + { + "name": "nonce", + "type": "uint48" + } + ] + }, + "primaryType": "PermitBatch", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "details": [ + { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "1000000", + "expiration": "1893456000", + "nonce": "0" + }, + { + "token": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "amount": "2000000", + "expiration": "1893456000", + "nonce": "1" + }, + { + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "amount": "3000000", + "expiration": "1893456000", + "nonce": "2" + }, + { + "token": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "amount": "4000000", + "expiration": "1893456000", + "nonce": "3" + }, + { + "token": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "amount": "5000000", + "expiration": "1893456000", + "nonce": "4" + } + ], + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "sigDeadline": "1775803746" + } + }, + "expected": { + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "988853f08bc1888f09fc0fac97c129a5f79f8700da45e7135511753138073bb2", + "digest": "21b393815af1f64e893e0681d486fd4baf58532c1b079aa5d52a3d212fba7d31" + } + }, + { + "id": "permit2-PermitTransferFrom", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ] + }, + "primaryType": "PermitTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "250000000" + }, + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "nonce": "1997", + "deadline": "1775803746" + } + }, + "expected": { + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "a72b11929a9c4bff37f00eb63b3615519e00a8aa297140855c44cfff4da27146", + "digest": "ece4ce2c0d6c3f27171599e51e0b42287645bb3029f07ef9671ddca458007d5e" + } + }, + { + "id": "permit2-PermitBatchTransferFrom", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitBatchTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions[]" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ] + }, + "primaryType": "PermitBatchTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": [ + { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "1000000" + }, + { + "token": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "amount": "10000000" + }, + { + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "amount": "100000000" + }, + { + "token": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "amount": "1000000000" + }, + { + "token": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "amount": "10000000000" + } + ], + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "nonce": "1998", + "deadline": "1775803746" + } + }, + "expected": { + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "b0347a078a3a77dee64d7c255ba56e39221a3d6c5b2375d2e89d09b28533b5ab", + "digest": "31227578aa0209aad26446c56711f4f4ad009a6befab3034ce466ad45e295090" + } + }, + { + "id": "uniswapx-ExclusiveDutchOrder", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/ExclusiveDutchOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/DutchOrderLib.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitWitnessTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "ExclusiveDutchOrder" + } + ], + "DutchOutput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + }, + { + "name": "recipient", + "type": "address" + } + ], + "ExclusiveDutchOrder": [ + { + "name": "info", + "type": "OrderInfo" + }, + { + "name": "decayStartTime", + "type": "uint256" + }, + { + "name": "decayEndTime", + "type": "uint256" + }, + { + "name": "exclusiveFiller", + "type": "address" + }, + { + "name": "exclusivityOverrideBps", + "type": "uint256" + }, + { + "name": "inputToken", + "type": "address" + }, + { + "name": "inputStartAmount", + "type": "uint256" + }, + { + "name": "inputEndAmount", + "type": "uint256" + }, + { + "name": "outputs", + "type": "DutchOutput[]" + } + ], + "OrderInfo": [ + { + "name": "reactor", + "type": "address" + }, + { + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "additionalValidationContract", + "type": "address" + }, + { + "name": "additionalValidationData", + "type": "bytes" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "3500000000" + }, + "spender": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", + "deadline": "1775803746", + "witness": { + "info": { + "reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4", + "swapper": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", + "deadline": "1775803746", + "additionalValidationContract": "0x0000000000000000000000000000000000000000", + "additionalValidationData": "0x" + }, + "decayStartTime": "1775803680", + "decayEndTime": "1775803740", + "exclusiveFiller": "0xff8ba4d1fc3762f6154cc942ccf30049a2a0cec6", + "exclusivityOverrideBps": "100", + "inputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "inputStartAmount": "3500000000", + "inputEndAmount": "3500000000", + "outputs": [ + { + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "startAmount": "1000000000000000000", + "endAmount": "990000000000000000", + "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" + }, + { + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "startAmount": "1500000000000000", + "endAmount": "1485000000000000", + "recipient": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47" + } + ] + } + } + }, + "expected": { + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "9d5278e60026d517ef5795ddfa37fdbb3c45bb9aa1428e1100420191c4d582bf", + "digest": "5def1fbddaf0631deb17c013e1778be8a134da1e4f4ce1ed070479f12e4cdc67" + } + }, + { + "id": "uniswapx-V2DutchOrder", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/V2DutchOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitWitnessTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "V2DutchOrder" + } + ], + "DutchOutput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + }, + { + "name": "recipient", + "type": "address" + } + ], + "OrderInfo": [ + { + "name": "reactor", + "type": "address" + }, + { + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "additionalValidationContract", + "type": "address" + }, + { + "name": "additionalValidationData", + "type": "bytes" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "V2DutchOrder": [ + { + "name": "info", + "type": "OrderInfo" + }, + { + "name": "cosigner", + "type": "address" + }, + { + "name": "baseInputToken", + "type": "address" + }, + { + "name": "baseInputStartAmount", + "type": "uint256" + }, + { + "name": "baseInputEndAmount", + "type": "uint256" + }, + { + "name": "baseOutputs", + "type": "DutchOutput[]" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "3500000000" + }, + "spender": "0x00000011F84B9aa48e5f8aA8B9897600006289Be", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484289", + "deadline": "1775803746", + "witness": { + "info": { + "reactor": "0x00000011F84B9aa48e5f8aA8B9897600006289Be", + "swapper": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", + "deadline": "1775803746", + "additionalValidationContract": "0x0000000000000000000000000000000000000000", + "additionalValidationData": "0x" + }, + "cosigner": "0x4449cd34d1eb1fedcf02a1be3834ffde8e6a6180", + "baseInputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "baseInputStartAmount": "3500000000", + "baseInputEndAmount": "3500000000", + "baseOutputs": [ + { + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "startAmount": "1000000000000000000", + "endAmount": "990000000000000000", + "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" + }, + { + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "startAmount": "1500000000000000", + "endAmount": "1485000000000000", + "recipient": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47" + } + ] + } + } + }, + "expected": { + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "1d895c005457e7cdee8450d706c53a359a4176c12dd4d48f9e38339160809b83", + "digest": "7cc7576f193542ecfbed9254ff7785c3aa296d2382d317858aa13d87dd24d652" + } + }, + { + "id": "uniswapx-PriorityOrder", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/PriorityOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitWitnessTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "PriorityOrder" + } + ], + "OrderInfo": [ + { + "name": "reactor", + "type": "address" + }, + { + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "additionalValidationContract", + "type": "address" + }, + { + "name": "additionalValidationData", + "type": "bytes" + } + ], + "PriorityInput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "mpsPerPriorityFeeWei", + "type": "uint256" + } + ], + "PriorityOrder": [ + { + "name": "info", + "type": "OrderInfo" + }, + { + "name": "cosigner", + "type": "address" + }, + { + "name": "auctionStartBlock", + "type": "uint256" + }, + { + "name": "baselinePriorityFeeWei", + "type": "uint256" + }, + { + "name": "input", + "type": "PriorityInput" + }, + { + "name": "outputs", + "type": "PriorityOutput[]" + } + ], + "PriorityOutput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "mpsPerPriorityFeeWei", + "type": "uint256" + }, + { + "name": "recipient", + "type": "address" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 8453, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "amount": "100000000" + }, + "spender": "0x0000000000fbd5e2a41b6d0f3b0fe9ed9b2d0cc9", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484290", + "deadline": "1775803746", + "witness": { + "info": { + "reactor": "0x0000000000fbd5e2a41b6d0f3b0fe9ed9b2d0cc9", + "swapper": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", + "deadline": "1775803746", + "additionalValidationContract": "0x0000000000000000000000000000000000000000", + "additionalValidationData": "0x" + }, + "cosigner": "0x0000000000000000000000000000000000000000", + "auctionStartBlock": "24000000", + "baselinePriorityFeeWei": "0", + "input": { + "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "amount": "100000000", + "mpsPerPriorityFeeWei": "0" + }, + "outputs": [ + { + "token": "0x4200000000000000000000000000000000000006", + "amount": "28000000000000000", + "mpsPerPriorityFeeWei": "1", + "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" + }, + { + "token": "0x4200000000000000000000000000000000000006", + "amount": "42000000000000", + "mpsPerPriorityFeeWei": "0", + "recipient": "0x7FFC3DBF3B2b50Ff3A1D5523bc24Bb5043837B14" + } + ] + } + } + }, + "expected": { + "domain_separator": "3b6f35e4fce979ef8eac3bcdc8c3fc38fe7911bb0c69c8fe72bf1fd1a17e6f07", + "message_hash": "a641126490de075257de4fc0b0ade25f8fa4bfc12858d49bb388cdcf34fd0c78", + "digest": "c7b4e513709348a52a6b13dea66c18975713be646ec021d9d62fb632874c7eb5" + } + }, + { + "id": "seaport-OrderComponents", + "source": [ + "https://github.com/ProjectOpenSea/seaport-core/blob/523097f9cee66c15d308c900c50f336b291cda08/src/lib/ConsiderationBase.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "OrderComponents": [ + { + "name": "offerer", + "type": "address" + }, + { + "name": "zone", + "type": "address" + }, + { + "name": "offer", + "type": "OfferItem[]" + }, + { + "name": "consideration", + "type": "ConsiderationItem[]" + }, + { + "name": "orderType", + "type": "uint8" + }, + { + "name": "startTime", + "type": "uint256" + }, + { + "name": "endTime", + "type": "uint256" + }, + { + "name": "zoneHash", + "type": "bytes32" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "conduitKey", + "type": "bytes32" + }, + { + "name": "counter", + "type": "uint256" + } + ], + "ConsiderationItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + }, + { + "name": "recipient", + "type": "address" + } + ], + "OfferItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + } + ] + }, + "primaryType": "OrderComponents", + "domain": { + "name": "Seaport", + "version": "1.6", + "chainId": 1, + "verifyingContract": "0x0000000000000068F116a894984e2DB1123eB395" + }, + "message": { + "offerer": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "zone": "0x0000000000000000000000000000000000000000", + "offer": [ + { + "itemType": 2, + "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "identifierOrCriteria": "8520", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "identifierOrCriteria": "8521", + "startAmount": "1", + "endAmount": "1" + } + ], + "consideration": [ + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "19500000000000000000", + "endAmount": "19500000000000000000", + "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "500000000000000000", + "endAmount": "500000000000000000", + "recipient": "0x0000a26b00c1F0DF003000390027140000fAa719" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "1000000000000000000", + "endAmount": "1000000000000000000", + "recipient": "0xA858DDc0445d8131daC4d1DE01f834ffcbA52Ef1" + } + ], + "orderType": 0, + "startTime": "1775700000", + "endTime": "1778300000", + "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "salt": "24446860302761739304752683030156737591518664810215442929818227897836383814680", + "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", + "counter": "0" + } + }, + "expected": { + "domain_separator": "fce34bc6e1752c1117e5063116d25cad2fa2bdcf15ff2d2e275eece7dc31ba64", + "message_hash": "5bdedde642e259a1841ef18afaf47285be8da69b311260a27436eba16517c016", + "digest": "b6f6cd11f7cd0e8f8ed8964727b57e23d38680ad2d818d3a87b551e4762a1f69" + } + }, + { + "id": "looksrare-Maker", + "source": [ + "https://github.com/LooksRare/contracts-exchange-v2/blob/7fca5650da653a2a0680e18e60164e639c18f830/contracts/libraries/OrderStructs.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Maker": [ + { + "name": "quoteType", + "type": "uint8" + }, + { + "name": "globalNonce", + "type": "uint256" + }, + { + "name": "subsetNonce", + "type": "uint256" + }, + { + "name": "orderNonce", + "type": "uint256" + }, + { + "name": "strategyId", + "type": "uint256" + }, + { + "name": "collectionType", + "type": "uint8" + }, + { + "name": "collection", + "type": "address" + }, + { + "name": "currency", + "type": "address" + }, + { + "name": "signer", + "type": "address" + }, + { + "name": "startTime", + "type": "uint256" + }, + { + "name": "endTime", + "type": "uint256" + }, + { + "name": "price", + "type": "uint256" + }, + { + "name": "itemIds", + "type": "uint256[]" + }, + { + "name": "amounts", + "type": "uint256[]" + }, + { + "name": "additionalParameters", + "type": "bytes" + } + ] + }, + "primaryType": "Maker", + "domain": { + "name": "LooksRareProtocol", + "version": "2", + "chainId": 1, + "verifyingContract": "0x0000000000E655fAe4d56241588680F86E3b2377" + }, + "message": { + "quoteType": 1, + "globalNonce": "0", + "subsetNonce": "0", + "orderNonce": "7", + "strategyId": "0", + "collectionType": 0, + "collection": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "currency": "0x0000000000000000000000000000000000000000", + "signer": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "startTime": "1775700000", + "endTime": "1778300000", + "price": "21000000000000000000", + "itemIds": [ + "8520", + "8521" + ], + "amounts": [ + "1", + "1" + ], + "additionalParameters": "0x" + } + }, + "expected": { + "domain_separator": "13be80cb8c4239588caab638256c555b825f3fbead883414b042fd375781040c", + "message_hash": "f66521e3a0d0416b42e738575d0e63db76241755e1d097629aa2e5f17927513c", + "digest": "c63794a61c6b972160381131e7d63b7d3a695f512248b9e061327c85d10e9004" + } + }, + { + "id": "blur-Order", + "source": [ + "https://github.com/code-423n4/2022-10-blur/blob/main/contracts/lib/EIP712.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Order": [ + { + "name": "trader", + "type": "address" + }, + { + "name": "side", + "type": "uint8" + }, + { + "name": "matchingPolicy", + "type": "address" + }, + { + "name": "collection", + "type": "address" + }, + { + "name": "tokenId", + "type": "uint256" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "paymentToken", + "type": "address" + }, + { + "name": "price", + "type": "uint256" + }, + { + "name": "listingTime", + "type": "uint256" + }, + { + "name": "expirationTime", + "type": "uint256" + }, + { + "name": "fees", + "type": "Fee[]" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "extraParams", + "type": "bytes" + }, + { + "name": "nonce", + "type": "uint256" + } + ], + "Fee": [ + { + "name": "rate", + "type": "uint16" + }, + { + "name": "recipient", + "type": "address" + } + ] + }, + "primaryType": "Order", + "domain": { + "name": "Blur Exchange", + "version": "1.0", + "chainId": 1, + "verifyingContract": "0x000000000000Ad05Ccc4F10045630fb830B95127" + }, + "message": { + "trader": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "side": 1, + "matchingPolicy": "0x00000000006411739DA1c40B106F8511de5D1FAC", + "collection": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "tokenId": "8520", + "amount": "1", + "paymentToken": "0x0000000000000000000000000000000000000000", + "price": "20000000000000000000", + "listingTime": "1775700000", + "expirationTime": "1778300000", + "fees": [ + { + "rate": 50, + "recipient": "0xaab1d2c1fc1c5b4e8a1de8b3fe2f5fc6e2fb86d3" + }, + { + "rate": 250, + "recipient": "0xA858DDc0445d8131daC4d1DE01f834ffcbA52Ef1" + } + ], + "salt": "265875887785256855606558013756560384533", + "extraParams": "0x01", + "nonce": "0" + } + }, + "expected": { + "domain_separator": "68e25ba9bdf16f19b7227e3ee7fff0b93558367587fc73fd50c5d978a0cd227f", + "message_hash": "c8858619b0e96f15b33b91c260c45570945ec84d5310016888785cec1252ec40", + "digest": "89424a5eaf944e4bda74c080e649e8f6bd3443a6e6b86a48e4100fc43c01a380" + } + }, + { + "id": "blur-Root", + "source": [ + "https://github.com/code-423n4/2022-10-blur/blob/main/contracts/lib/EIP712.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Root": [ + { + "name": "root", + "type": "bytes32" + } + ] + }, + "primaryType": "Root", + "domain": { + "name": "Blur Exchange", + "version": "1.0", + "chainId": 1, + "verifyingContract": "0x000000000000Ad05Ccc4F10045630fb830B95127" + }, + "message": { + "root": "0x6f1c86e1ea4b8b1d9f7f0c5c2e1d6a3b4c5d6e7f8091a2b3c4d5e6f708192a3b" + } + }, + "expected": { + "domain_separator": "68e25ba9bdf16f19b7227e3ee7fff0b93558367587fc73fd50c5d978a0cd227f", + "message_hash": "aeadf10ef9755aa81ad8a3219731271fe417be9c783dc542a42d5edfb3b77317", + "digest": "6d3bc4aec615720499edd705ee6673b5f1b40a1a3afa1a8d85ac273ebc427253" + } + }, + { + "id": "1inch-Order", + "source": [ + "https://github.com/1inch/limit-order-protocol/blob/50bd1300315606a3d32e9021898374c9d0dc2335/contracts/OrderLib.sol", + "https://sourcify.dev/#/lookup/0x111111125421cA6dc452d289314280a0f8842A65" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Order": [ + { + "name": "salt", + "type": "uint256" + }, + { + "name": "maker", + "type": "address" + }, + { + "name": "receiver", + "type": "address" + }, + { + "name": "makerAsset", + "type": "address" + }, + { + "name": "takerAsset", + "type": "address" + }, + { + "name": "makingAmount", + "type": "uint256" + }, + { + "name": "takingAmount", + "type": "uint256" + }, + { + "name": "makerTraits", + "type": "uint256" + } + ] + }, + "primaryType": "Order", + "domain": { + "name": "1inch Aggregation Router", + "version": "6", + "chainId": 1, + "verifyingContract": "0x111111125421cA6dc452d289314280a0f8842A65" + }, + "message": { + "salt": "102412815611287462193218283713587453612380483585130722463424346577", + "maker": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "receiver": "0x0000000000000000000000000000000000000000", + "makerAsset": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "takerAsset": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "makingAmount": "1000000000000000000", + "takingAmount": "3500000000", + "makerTraits": "62419173104490761595518734106557662061518414611782227068396304425790442831872" + } + }, + "expected": { + "domain_separator": "d999e213f11c7bfa3e796c3409e316f25e02aa3e25e5c207a92e381c7d22b6de", + "message_hash": "0dfdbba222c62bc366f22ca6339e12520ce10684376c80d61176781ac00c3f4b", + "digest": "cddd12ad70f117d173e3e40ae060696c549fdc0ec98bc913083f20f6ba3163d9" + } + }, + { + "id": "cow-Order", + "source": [ + "https://github.com/cowprotocol/contracts/blob/c07a93e3596194c5e3cf331c755a3f9f0e4a17d8/src/contracts/libraries/GPv2Order.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Order": [ + { + "name": "sellToken", + "type": "address" + }, + { + "name": "buyToken", + "type": "address" + }, + { + "name": "receiver", + "type": "address" + }, + { + "name": "sellAmount", + "type": "uint256" + }, + { + "name": "buyAmount", + "type": "uint256" + }, + { + "name": "validTo", + "type": "uint32" + }, + { + "name": "appData", + "type": "bytes32" + }, + { + "name": "feeAmount", + "type": "uint256" + }, + { + "name": "kind", + "type": "string" + }, + { + "name": "partiallyFillable", + "type": "bool" + }, + { + "name": "sellTokenBalance", + "type": "string" + }, + { + "name": "buyTokenBalance", + "type": "string" + } + ] + }, + "primaryType": "Order", + "domain": { + "name": "Gnosis Protocol", + "version": "v2", + "chainId": 1, + "verifyingContract": "0x9008D19f58AAbD9eD0D60971565AA8510560ab41" + }, + "message": { + "sellToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "buyToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "receiver": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "sellAmount": "1000000000000000000", + "buyAmount": "3480000000", + "validTo": 1775803746, + "appData": "0xf785fae7a7c5abc49f3cd6a61f6df1ff26433392b066ee9ff2240ff1eb7ab6e4", + "feeAmount": "0", + "kind": "sell", + "partiallyFillable": false, + "sellTokenBalance": "erc20", + "buyTokenBalance": "erc20" + } + }, + "expected": { + "domain_separator": "c078f884a2676e1345748b1feace7b0abee5d00ecadb6e574dcdd109a63e8943", + "message_hash": "cdc3f27a64d63d4d714edd7ac17d8ba1891d43b9743d7776cd58ad5fc4cec3a7", + "digest": "576a9e87719762457271789254f3ab5a2fa6a6bc7d20972883ac69ca076ffad8" + } + }, + { + "id": "cow-OrderCancellations", + "source": [ + "https://github.com/cowprotocol/cow-sdk/blob/8ac240cbea721a546618d3667a24c8054664d934/packages/contracts-ts/src/order.ts" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "OrderCancellations": [ + { + "name": "orderUids", + "type": "bytes[]" + } + ] + }, + "primaryType": "OrderCancellations", + "domain": { + "name": "Gnosis Protocol", + "version": "v2", + "chainId": 1, + "verifyingContract": "0x9008D19f58AAbD9eD0D60971565AA8510560ab41" + }, + "message": { + "orderUids": [ + "0xabababababababababababababababababababababababababababababababab9858effd232b4033e47d90003d41ec34ecaeda9469d89d62", + "0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd9858effd232b4033e47d90003d41ec34ecaeda9469d89d63" + ] + } + }, + "expected": { + "domain_separator": "c078f884a2676e1345748b1feace7b0abee5d00ecadb6e574dcdd109a63e8943", + "message_hash": "c2db95270ee0d0e6ae186f42a5acbd1d0809010e2f83398fca5ad7fa7e7dd803", + "digest": "5898792dcf9f9d984e4d0ce6b0dc8a10ebd652e8722e944b38bad099f1a968a4" + } + }, + { + "id": "0x-LimitOrder", + "source": [ + "https://github.com/0xProject/protocol/blob/2c8e51e36ccbafa39f7c9003f4087710665acb02/contracts/zero-ex/contracts/src/features/libs/LibNativeOrder.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "LimitOrder": [ + { + "name": "makerToken", + "type": "address" + }, + { + "name": "takerToken", + "type": "address" + }, + { + "name": "makerAmount", + "type": "uint128" + }, + { + "name": "takerAmount", + "type": "uint128" + }, + { + "name": "takerTokenFeeAmount", + "type": "uint128" + }, + { + "name": "maker", + "type": "address" + }, + { + "name": "taker", + "type": "address" + }, + { + "name": "sender", + "type": "address" + }, + { + "name": "feeRecipient", + "type": "address" + }, + { + "name": "pool", + "type": "bytes32" + }, + { + "name": "expiry", + "type": "uint64" + }, + { + "name": "salt", + "type": "uint256" + } + ] + }, + "primaryType": "LimitOrder", + "domain": { + "name": "ZeroEx", + "version": "1.0.0", + "chainId": 1, + "verifyingContract": "0xDef1C0ded9bec7F1a1670819833240f027b25EfF" + }, + "message": { + "makerToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "takerToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "makerAmount": "1000000000000000000", + "takerAmount": "3500000000", + "takerTokenFeeAmount": "0", + "maker": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "taker": "0x0000000000000000000000000000000000000000", + "sender": "0x0000000000000000000000000000000000000000", + "feeRecipient": "0x0000000000000000000000000000000000000000", + "pool": "0x0000000000000000000000000000000000000000000000000000000000000000", + "expiry": "1775803746", + "salt": "54308148374513278436071736734563213846290466613155327734536137082462373451223" + } + }, + "expected": { + "domain_separator": "fe3a8808ff7909b8c36164e6e9a076597c21c3fc2ec6f2c8ac04529c41ce507e", + "message_hash": "52eeeafb7e1db5a5dd3c0604bac947a340ea48a297fb5b4eabe88509e4bd8688", + "digest": "474fd3e0afa906ca55fd290375e08ccde65bf824e8bf85fff0c656849e713e88" + } + }, + { + "id": "0x-RfqOrder", + "source": [ + "https://github.com/0xProject/protocol/blob/2c8e51e36ccbafa39f7c9003f4087710665acb02/contracts/zero-ex/contracts/src/features/libs/LibNativeOrder.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "RfqOrder": [ + { + "name": "makerToken", + "type": "address" + }, + { + "name": "takerToken", + "type": "address" + }, + { + "name": "makerAmount", + "type": "uint128" + }, + { + "name": "takerAmount", + "type": "uint128" + }, + { + "name": "maker", + "type": "address" + }, + { + "name": "taker", + "type": "address" + }, + { + "name": "txOrigin", + "type": "address" + }, + { + "name": "pool", + "type": "bytes32" + }, + { + "name": "expiry", + "type": "uint64" + }, + { + "name": "salt", + "type": "uint256" + } + ] + }, + "primaryType": "RfqOrder", + "domain": { + "name": "ZeroEx", + "version": "1.0.0", + "chainId": 1, + "verifyingContract": "0xDef1C0ded9bec7F1a1670819833240f027b25EfF" + }, + "message": { + "makerToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "takerToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "makerAmount": "3500000000", + "takerAmount": "1000000000000000000", + "maker": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "taker": "0x0000000000000000000000000000000000000000", + "txOrigin": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47", + "pool": "0x0000000000000000000000000000000000000000000000000000000000000000", + "expiry": "1775803746", + "salt": "1775803000" + } + }, + "expected": { + "domain_separator": "fe3a8808ff7909b8c36164e6e9a076597c21c3fc2ec6f2c8ac04529c41ce507e", + "message_hash": "4fe4aaaecb08c6b07ebd5c06ba57304d9e8bf650668e51afd87c81f243578043", + "digest": "24dd6dea5cd2de49c1701d67eaf8487efdc213828d2c00a97408379a0e1fcd98" + } + }, + { + "id": "0x-OtcOrder", + "source": [ + "https://github.com/0xProject/protocol/blob/2c8e51e36ccbafa39f7c9003f4087710665acb02/contracts/zero-ex/contracts/src/features/libs/LibNativeOrder.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "OtcOrder": [ + { + "name": "makerToken", + "type": "address" + }, + { + "name": "takerToken", + "type": "address" + }, + { + "name": "makerAmount", + "type": "uint128" + }, + { + "name": "takerAmount", + "type": "uint128" + }, + { + "name": "maker", + "type": "address" + }, + { + "name": "taker", + "type": "address" + }, + { + "name": "txOrigin", + "type": "address" + }, + { + "name": "expiryAndNonce", + "type": "uint256" + } + ] + }, + "primaryType": "OtcOrder", + "domain": { + "name": "ZeroEx", + "version": "1.0.0", + "chainId": 1, + "verifyingContract": "0xDef1C0ded9bec7F1a1670819833240f027b25EfF" + }, + "message": { + "makerToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "takerToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "makerAmount": "3500000000", + "takerAmount": "1000000000000000000", + "maker": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "taker": "0x0000000000000000000000000000000000000000", + "txOrigin": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47", + "expiryAndNonce": "2724834893716958296099287542290232898829043766902734489108736" + } + }, + "expected": { + "domain_separator": "fe3a8808ff7909b8c36164e6e9a076597c21c3fc2ec6f2c8ac04529c41ce507e", + "message_hash": "c3d531dfd43da6373e92accb783b6c56e7eaa7148a88b36a0bdd4b156c73133d", + "digest": "a575e5d5eab33f2c528564d24febb5c74ad8919c25581ac9c7012f338b0590d1" + } + }, + { + "id": "safe-SafeTx", + "source": [ + "https://github.com/safe-global/safe-smart-account/blob/v1.4.1/contracts/Safe.sol", + "https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/GnosisSafe.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "SafeTx": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "operation", + "type": "uint8" + }, + { + "name": "safeTxGas", + "type": "uint256" + }, + { + "name": "baseGas", + "type": "uint256" + }, + { + "name": "gasPrice", + "type": "uint256" + }, + { + "name": "gasToken", + "type": "address" + }, + { + "name": "refundReceiver", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + } + ] + }, + "primaryType": "SafeTx", + "domain": { + "chainId": 1, + "verifyingContract": "0x4F2083f5fBede34C2714aFfb3105539775f7FE64" + }, + "message": { + "to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "value": "0", + "data": "0xa9059cbb00000000000000000000000027213e28d7fda5c57fe9e5dd923818dbccf71c470000000000000000000000000000000000000000000000000000000059682f00", + "operation": 0, + "safeTxGas": "0", + "baseGas": "0", + "gasPrice": "0", + "gasToken": "0x0000000000000000000000000000000000000000", + "refundReceiver": "0x0000000000000000000000000000000000000000", + "nonce": "42" + } + }, + "expected": { + "domain_separator": "facf7a68664ed3dabebe8e263b4cf03d3e1ba8bbda764c9697c80c7e38b41c62", + "message_hash": "f1e5adf415ba2dc900ccdd63831d31c79f9a30e40a2d41a8a0683c8d8f535f1d", + "digest": "c6d5a71fd45369207a574d910998c708553d0a8960cbc8ea4515f04891f78878" + } + }, + { + "id": "safe-SafeMessage", + "source": [ + "https://github.com/safe-global/safe-smart-account/blob/v1.4.1/contracts/handler/CompatibilityFallbackHandler.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "SafeMessage": [ + { + "name": "message", + "type": "bytes" + } + ] + }, + "primaryType": "SafeMessage", + "domain": { + "chainId": 1, + "verifyingContract": "0x4F2083f5fBede34C2714aFfb3105539775f7FE64" + }, + "message": { + "message": "0x5369676e20696e20746f206170702e6578616d706c652e6f7267207769746820796f75722053616665" + } + }, + "expected": { + "domain_separator": "facf7a68664ed3dabebe8e263b4cf03d3e1ba8bbda764c9697c80c7e38b41c62", + "message_hash": "50c1323949a0f56f1d841a54dcf0b029edeeb52ad42daa386d59ce7d8f5d9e26", + "digest": "10d37e6ca3e5d88f02051f712267a3763f108f8cc1bf478290254f3f6770cc79" + } + }, + { + "id": "eip2612-Permit", + "source": [ + "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/c98b7188338e3c5fce903747894561af78863bbb/contracts/token/ERC20/extensions/ERC20Permit.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Permit": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + } + ] + }, + "primaryType": "Permit", + "domain": { + "name": "USD Coin", + "version": "2", + "chainId": 1, + "verifyingContract": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "message": { + "owner": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "value": "250000000", + "nonce": "0", + "deadline": "1775803746" + } + }, + "expected": { + "domain_separator": "06c37168a7db5138defc7866392bb87a741f9b3d104deb5094588ce041cae335", + "message_hash": "10de0bd7514d5a137be30ca8b8d727c944556d219ac5338577ea5b44bbecc21a", + "digest": "9dfbe3c2bc66846b4e4d05cf917567d5448bac92b4b575285df4b198f459ed94" + } + }, + { + "id": "dai-Permit", + "source": [ + "https://github.com/makerdao/dss/blob/fa4f6630afb0624d04a003e920b0d71a00331d98/src/dai.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Permit": [ + { + "name": "holder", + "type": "address" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "expiry", + "type": "uint256" + }, + { + "name": "allowed", + "type": "bool" + } + ] + }, + "primaryType": "Permit", + "domain": { + "name": "Dai Stablecoin", + "version": "1", + "chainId": 1, + "verifyingContract": "0x6B175474E89094C44Da98b954EedeAC495271d0F" + }, + "message": { + "holder": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "nonce": "3", + "expiry": "1775803746", + "allowed": false + } + }, + "expected": { + "domain_separator": "dbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7", + "message_hash": "ab11f761da4f18c351b557993fff3af1b5276e0ca318a34eb7ff374040587863", + "digest": "ea585737a895795b5b84df7ff9082e15098bda1e22cc312b27ce64c6b656bde6" + } + }, + { + "id": "hyperliquid-Agent", + "source": [ + "https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/2fdb18f9517675ea03695a0962bd19eece9c83f0/hyperliquid/utils/signing.py" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Agent": [ + { + "name": "source", + "type": "string" + }, + { + "name": "connectionId", + "type": "bytes32" + } + ] + }, + "primaryType": "Agent", + "domain": { + "name": "Exchange", + "version": "1", + "chainId": 1337, + "verifyingContract": "0x0000000000000000000000000000000000000000" + }, + "message": { + "source": "a", + "connectionId": "0x5a9c4e1b6f3d2a8c7e0b1d4f6a8c2e4b6d8f0a1c3e5b7d9f1a3c5e7b9d1f3a5c" + } + }, + "expected": { + "domain_separator": "d79297fcdf2ffcd4ae223d01edaa2ba214ff8f401d7c9300d995d17c82aa4040", + "message_hash": "f4ece6c5a5f1499c017ee17624f5a4ac0a476f42a270b0300b96840c4fb407ad", + "digest": "6bce8eb48553ebe6181a9cdba358196071b0ac1f204f124cc3c6d9fcfe0d87e0" + } + }, + { + "id": "hyperliquid-UsdSend", + "source": [ + "https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/2fdb18f9517675ea03695a0962bd19eece9c83f0/hyperliquid/utils/signing.py" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "HyperliquidTransaction:UsdSend": [ + { + "name": "hyperliquidChain", + "type": "string" + }, + { + "name": "destination", + "type": "string" + }, + { + "name": "amount", + "type": "string" + }, + { + "name": "time", + "type": "uint64" + } + ] + }, + "primaryType": "HyperliquidTransaction:UsdSend", + "domain": { + "name": "HyperliquidSignTransaction", + "version": "1", + "chainId": 421614, + "verifyingContract": "0x0000000000000000000000000000000000000000" + }, + "message": { + "hyperliquidChain": "Mainnet", + "destination": "0x27213e28d7fda5c57fe9e5dd923818dbccf71c47", + "amount": "125.5", + "time": 1775803746123 + } + }, + "expected": { + "domain_separator": "feb1393ca4412a4ca577bd51d04f0a77033514c602f4d0a11490fb95f7428df6", + "message_hash": "518cb9e1a640cb3f4f1284c76dfd63a9a71962c694c22dc3f6dff33ba6f6ab92", + "digest": "ccaa5b3b61614b569661dc3b3898d09db03661064d3300c6dfc50a48c5257621" + } + }, + { + "id": "hyperliquid-Withdraw", + "source": [ + "https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/2fdb18f9517675ea03695a0962bd19eece9c83f0/hyperliquid/utils/signing.py" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "HyperliquidTransaction:Withdraw": [ + { + "name": "hyperliquidChain", + "type": "string" + }, + { + "name": "destination", + "type": "string" + }, + { + "name": "amount", + "type": "string" + }, + { + "name": "time", + "type": "uint64" + } + ] + }, + "primaryType": "HyperliquidTransaction:Withdraw", + "domain": { + "name": "HyperliquidSignTransaction", + "version": "1", + "chainId": 421614, + "verifyingContract": "0x0000000000000000000000000000000000000000" + }, + "message": { + "hyperliquidChain": "Mainnet", + "destination": "0x9858effd232b4033e47d90003d41ec34ecaeda94", + "amount": "1000", + "time": 1775803746124 + } + }, + "expected": { + "domain_separator": "feb1393ca4412a4ca577bd51d04f0a77033514c602f4d0a11490fb95f7428df6", + "message_hash": "dd5d1b845532de3345180aaeaf23bbb2b7bbcd4ff128c3278dae59485cb62609", + "digest": "7da96947dfdab71f98adc8b1dd35f14ca9d89a3748a47948fcbce63459a00153" + } + }, + { + "id": "hyperliquid-SpotSend", + "source": [ + "https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/2fdb18f9517675ea03695a0962bd19eece9c83f0/hyperliquid/utils/signing.py" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "HyperliquidTransaction:SpotSend": [ + { + "name": "hyperliquidChain", + "type": "string" + }, + { + "name": "destination", + "type": "string" + }, + { + "name": "token", + "type": "string" + }, + { + "name": "amount", + "type": "string" + }, + { + "name": "time", + "type": "uint64" + } + ] + }, + "primaryType": "HyperliquidTransaction:SpotSend", + "domain": { + "name": "HyperliquidSignTransaction", + "version": "1", + "chainId": 421614, + "verifyingContract": "0x0000000000000000000000000000000000000000" + }, + "message": { + "hyperliquidChain": "Mainnet", + "destination": "0x27213e28d7fda5c57fe9e5dd923818dbccf71c47", + "token": "PURR:0xc4bf3f870c0e9465323c0b6ed28096c2", + "amount": "42", + "time": 1775803746125 + } + }, + "expected": { + "domain_separator": "feb1393ca4412a4ca577bd51d04f0a77033514c602f4d0a11490fb95f7428df6", + "message_hash": "9873b52950b87cda36625decbdeedcfbc0821cc1c49c413ec361f50286ed624a", + "digest": "024b66a25a2fd6473d749d58a5ab9811278d3ac66f0fa014d7a2c7ae3ef22086" + } + }, + { + "id": "hyperliquid-ApproveAgent", + "source": [ + "https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/2fdb18f9517675ea03695a0962bd19eece9c83f0/hyperliquid/utils/signing.py" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "HyperliquidTransaction:ApproveAgent": [ + { + "name": "hyperliquidChain", + "type": "string" + }, + { + "name": "agentAddress", + "type": "address" + }, + { + "name": "agentName", + "type": "string" + }, + { + "name": "nonce", + "type": "uint64" + } + ] + }, + "primaryType": "HyperliquidTransaction:ApproveAgent", + "domain": { + "name": "HyperliquidSignTransaction", + "version": "1", + "chainId": 421614, + "verifyingContract": "0x0000000000000000000000000000000000000000" + }, + "message": { + "hyperliquidChain": "Mainnet", + "agentAddress": "0x7FFC3DBF3B2b50Ff3A1D5523bc24Bb5043837B14", + "agentName": "keepkey", + "nonce": 1775803746126 + } + }, + "expected": { + "domain_separator": "feb1393ca4412a4ca577bd51d04f0a77033514c602f4d0a11490fb95f7428df6", + "message_hash": "0edbc16bac693bf270904285960c7a2aad984948616503c31b188cc314001f50", + "digest": "727fae5d7d7ffa519fb0f06bff340d863a9d366d3650feda6c25bc834d616b7e" + } + }, + { + "id": "hyperliquid-ApproveBuilderFee", + "source": [ + "https://github.com/hyperliquid-dex/hyperliquid-python-sdk/blob/2fdb18f9517675ea03695a0962bd19eece9c83f0/hyperliquid/utils/signing.py" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "HyperliquidTransaction:ApproveBuilderFee": [ + { + "name": "hyperliquidChain", + "type": "string" + }, + { + "name": "maxFeeRate", + "type": "string" + }, + { + "name": "builder", + "type": "address" + }, + { + "name": "nonce", + "type": "uint64" + } + ] + }, + "primaryType": "HyperliquidTransaction:ApproveBuilderFee", + "domain": { + "name": "HyperliquidSignTransaction", + "version": "1", + "chainId": 421614, + "verifyingContract": "0x0000000000000000000000000000000000000000" + }, + "message": { + "hyperliquidChain": "Mainnet", + "maxFeeRate": "0.1%", + "builder": "0xaab1d2c1fc1c5b4e8a1de8b3fe2f5fc6e2fb86d3", + "nonce": 1775803746127 + } + }, + "expected": { + "domain_separator": "feb1393ca4412a4ca577bd51d04f0a77033514c602f4d0a11490fb95f7428df6", + "message_hash": "40faac2eae76730154845cadb657e0eb6189a2e62d8996a3507384d37df30dd8", + "digest": "a157aa4e572a9371cd72316b9e56d5c6b870932d480b9c1c213ce754e27f0b7d" + } + }, + { + "id": "polymarket-Order", + "source": [ + "https://github.com/Polymarket/ctf-exchange/blob/ed5c7708b7be3aa98bf5f0c6602b57cc498e2ef4/src/exchange/libraries/OrderStructs.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Order": [ + { + "name": "salt", + "type": "uint256" + }, + { + "name": "maker", + "type": "address" + }, + { + "name": "signer", + "type": "address" + }, + { + "name": "taker", + "type": "address" + }, + { + "name": "tokenId", + "type": "uint256" + }, + { + "name": "makerAmount", + "type": "uint256" + }, + { + "name": "takerAmount", + "type": "uint256" + }, + { + "name": "expiration", + "type": "uint256" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "feeRateBps", + "type": "uint256" + }, + { + "name": "side", + "type": "uint8" + }, + { + "name": "signatureType", + "type": "uint8" + } + ] + }, + "primaryType": "Order", + "domain": { + "name": "Polymarket CTF Exchange", + "version": "1", + "chainId": 137, + "verifyingContract": "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E" + }, + "message": { + "salt": "479249096354", + "maker": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "signer": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "taker": "0x0000000000000000000000000000000000000000", + "tokenId": "71321045679252212594626385532706912750332728571942532289631379312455583992563", + "makerAmount": "50000000", + "takerAmount": "100000000", + "expiration": "0", + "nonce": "0", + "feeRateBps": "0", + "side": 0, + "signatureType": 0 + } + }, + "expected": { + "domain_separator": "1a573e3617c78403b5b4b892827992f027b03d4eaf570048b8ee8cdd84d151be", + "message_hash": "02d6222deeec96f60bb0fd5b2fa76da7f7a416223bbb77a5126bdd95c6ed4c27", + "digest": "4adaa897f6212a9a351ea8f1e30129c3746eccf4a02e2128961a152e7c10ce0d" + } + }, + { + "id": "polymarket-ClobAuth", + "source": [ + "https://github.com/Polymarket/clob-client/blob/7df8257dc95f99edb257b53a7873e273a9b4a9b3/src/signing/eip712.ts" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "ClobAuth": [ + { + "name": "address", + "type": "address" + }, + { + "name": "timestamp", + "type": "string" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "message", + "type": "string" + } + ] + }, + "primaryType": "ClobAuth", + "domain": { + "name": "ClobAuthDomain", + "version": "1", + "chainId": 137 + }, + "message": { + "address": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "timestamp": "1775803746", + "nonce": "0", + "message": "This message attests that I control the given wallet" + } + }, + "expected": { + "domain_separator": "cfc66be2a3b30464cb3b588324101f660c9a205fa76e8e5f83ee16a528e1c4cb", + "message_hash": "6998fb300c3c08c2bafe9e47d73aab51025acdf054307f4f4583d94404c97fcf", + "digest": "26632c686b33505c3e6f3001b3a7581f30dce9f94720ad249a2286fea252adff" + } + }, + { + "id": "compound-Ballot", + "source": [ + "https://github.com/compound-finance/compound-protocol/blob/a3214f67b73310d547e00fc578e8355911c9d376/contracts/Governance/GovernorBravoDelegate.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Ballot": [ + { + "name": "proposalId", + "type": "uint256" + }, + { + "name": "support", + "type": "uint8" + } + ] + }, + "primaryType": "Ballot", + "domain": { + "name": "Compound Governor Bravo", + "chainId": 1, + "verifyingContract": "0xc0Da02939E1441F497fd74F78cE7Decb17B66529" + }, + "message": { + "proposalId": "289", + "support": 1 + } + }, + "expected": { + "domain_separator": "d34731531481fb0dd71602b43ef32fd12c76bc12d76869812352236afaa4b346", + "message_hash": "91d8e3e95c41471dd654460e8b12395e1b0ee5682bfc1598134898b7a964a582", + "digest": "5e70ede1f42b52b245a35a58e2f2da91eef453a540c808247f785ed62dc7c9e2" + } + }, + { + "id": "oz-governor-v5-ExtendedBallot", + "source": [ + "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.4.0/contracts/governance/Governor.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "ExtendedBallot": [ + { + "name": "proposalId", + "type": "uint256" + }, + { + "name": "support", + "type": "uint8" + }, + { + "name": "voter", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "reason", + "type": "string" + }, + { + "name": "params", + "type": "bytes" + } + ] + }, + "primaryType": "ExtendedBallot", + "domain": { + "name": "Compound Governor", + "version": "1", + "chainId": 1, + "verifyingContract": "0x309a862bbC1A00e45506cB8A802D1ff10004c8C0" + }, + "message": { + "proposalId": "10770658185911291573227826359627312367262036453227096346419573049937349025001", + "support": 1, + "voter": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "nonce": "0", + "reason": "Supports the risk parameter update.", + "params": "0x" + } + }, + "expected": { + "domain_separator": "3abcb92fd8e6968fd834a2cc50bb05abdd78c74cd0fac3f017c47fcc8b743e87", + "message_hash": "05ce3e96fc27ffd71d0e36c688ecab14c00a49fac2c65bdfbd64af3ca49191f5", + "digest": "2f8c569b46c093b83c7ae290f31893ef6956910121ddcb1ca55f1326a6819845" + } + }, + { + "id": "oz-governor-v4-ExtendedBallot", + "source": [ + "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.6/contracts/governance/Governor.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "ExtendedBallot": [ + { + "name": "proposalId", + "type": "uint256" + }, + { + "name": "support", + "type": "uint8" + }, + { + "name": "reason", + "type": "string" + }, + { + "name": "params", + "type": "bytes" + } + ] + }, + "primaryType": "ExtendedBallot", + "domain": { + "name": "ENS Governor", + "version": "1", + "chainId": 1, + "verifyingContract": "0x323A76393544d5ecca80cd6ef2A560C6a395b7E3" + }, + "message": { + "proposalId": "42", + "support": 0, + "reason": "Against: needs an audit first.", + "params": "0x" + } + }, + "expected": { + "domain_separator": "7569da846e8a3624f3e70461387f0c2bae53ef7edc30b5d3a5fbe2b74fa7c741", + "message_hash": "d0e00d45e7cd483b28090a1512888e0bd907dce94815a2750c5064941e7bf76b", + "digest": "1d95b248abb2fc734628421684b93ce1980641738dca34190a3ee4916c20f6d8" + } + }, + { + "id": "compound-Delegation", + "source": [ + "https://github.com/compound-finance/compound-protocol/blob/a3214f67b73310d547e00fc578e8355911c9d376/contracts/Governance/Comp.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Delegation": [ + { + "name": "delegatee", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "expiry", + "type": "uint256" + } + ] + }, + "primaryType": "Delegation", + "domain": { + "name": "Compound", + "chainId": 1, + "verifyingContract": "0xc00e94Cb662C3520282E6f5717214004A7f26888" + }, + "message": { + "delegatee": "0x7FFC3DBF3B2b50Ff3A1D5523bc24Bb5043837B14", + "nonce": "0", + "expiry": "1775803746" + } + }, + "expected": { + "domain_separator": "97529df852bab657e1ffa498f205743fdd7e2b2dcbcc1173bacf2cd17133d278", + "message_hash": "ca7258eec1792ca72c50d4c4e2aca40d71485e77c86b39b9d04695ab7098500d", + "digest": "014c88c699f48e4680fdf86ab4a638876ff93812b0dd20106f3797caa8adb4ca" + } + }, + { + "id": "aave-SubmitVote", + "source": [ + "https://github.com/bgd-labs/aave-governance-v3/blob/497226efad1ca3723074034c8341fb90931fc2b2/src/contracts/voting/VotingMachineWithProofs.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "SubmitVote": [ + { + "name": "proposalId", + "type": "uint256" + }, + { + "name": "voter", + "type": "address" + }, + { + "name": "support", + "type": "bool" + }, + { + "name": "votingAssetsWithSlot", + "type": "VotingAssetWithSlot[]" + } + ], + "VotingAssetWithSlot": [ + { + "name": "underlyingAsset", + "type": "address" + }, + { + "name": "slot", + "type": "uint128" + } + ] + }, + "primaryType": "SubmitVote", + "domain": { + "name": "Aave Voting Machine", + "version": "V1", + "chainId": 137, + "verifyingContract": "0xc8a2ADC4261c6b669CdFf69E717E77C9cFeB420d" + }, + "message": { + "proposalId": "312", + "voter": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "support": true, + "votingAssetsWithSlot": [ + { + "underlyingAsset": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + "slot": "0" + }, + { + "underlyingAsset": "0x4da27a545c0c5B758a6BA100e3a049001de870f5", + "slot": "0" + }, + { + "underlyingAsset": "0xA700b4eB416Be35b2911fd5Dee80678ff64fF6C9", + "slot": "52" + } + ] + } + }, + "expected": { + "domain_separator": "ae1a4d19b2c550c11439cbcb934c88b16b8585f876d64fe1a6a6ae8e9e863c46", + "message_hash": "0fbe44b065abb8e818ecd154ba83b7a7f6f4824400446e44e97c426eb4989019", + "digest": "ad46f18dee90c1e41f3505c75706e0780e5b47ceb64e6406e85863ad17278f2c" + } + }, + { + "id": "dydx-DelegateByType", + "source": [ + "https://github.com/dydxfoundation/governance-contracts/blob/804a6e41f96f8b67daeff1d681b4c1c66ceb2547/contracts/governance/token/GovernancePowerDelegationERC20Mixin.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "DelegateByType": [ + { + "name": "delegatee", + "type": "address" + }, + { + "name": "type", + "type": "uint256" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "expiry", + "type": "uint256" + } + ] + }, + "primaryType": "DelegateByType", + "domain": { + "name": "dYdX", + "version": "1", + "chainId": 1, + "verifyingContract": "0x92D6C1e31e14520e676a687F0a93788B716BEff5" + }, + "message": { + "delegatee": "0x7FFC3DBF3B2b50Ff3A1D5523bc24Bb5043837B14", + "type": "1", + "nonce": "0", + "expiry": "1775803746" + } + }, + "expected": { + "domain_separator": "17e76dd75f3dfe3e50d31322433cc1405e898404a5724da2d4804cab81e0a88a", + "message_hash": "6dfed76dfede64f4af0c2768ac6d52b71f00d84ae5d32686448b7b5beb4c33a2", + "digest": "820d885ba6d39bf57def942b8c33ee3508d4fff87a09e50fb2f6ecaa486f8dd9" + } + }, + { + "id": "across-GaslessCrossChainOrder", + "source": [ + "https://github.com/across-protocol/contracts/blob/master/contracts/erc7683/ERC7683Permit2Lib.sol", + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitWitnessTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "GaslessCrossChainOrder" + } + ], + "AcrossOrderData": [ + { + "name": "inputToken", + "type": "address" + }, + { + "name": "inputAmount", + "type": "uint256" + }, + { + "name": "outputToken", + "type": "address" + }, + { + "name": "outputAmount", + "type": "uint256" + }, + { + "name": "destinationChainId", + "type": "uint256" + }, + { + "name": "recipient", + "type": "bytes32" + }, + { + "name": "exclusiveRelayer", + "type": "address" + }, + { + "name": "exclusivityPeriod", + "type": "uint32" + }, + { + "name": "message", + "type": "bytes" + } + ], + "GaslessCrossChainOrder": [ + { + "name": "originSettler", + "type": "address" + }, + { + "name": "user", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "originChainId", + "type": "uint256" + }, + { + "name": "openDeadline", + "type": "uint32" + }, + { + "name": "fillDeadline", + "type": "uint32" + }, + { + "name": "orderDataType", + "type": "bytes32" + }, + { + "name": "orderData", + "type": "AcrossOrderData" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 42161, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + "amount": "500000000" + }, + "spender": "0xB0B07055F214Ce59ccB968663d3435B9f3294998", + "nonce": "77", + "deadline": "1775803746", + "witness": { + "originSettler": "0xB0B07055F214Ce59ccB968663d3435B9f3294998", + "user": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "nonce": "77", + "originChainId": "42161", + "openDeadline": 1775803746, + "fillDeadline": 1775814546, + "orderDataType": "0x9df4b782e7bbc178b3b93bfe8aafb909e84e39484d7f3c59f400f1b4691f85e2", + "orderData": { + "inputToken": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + "inputAmount": "500000000", + "outputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "outputAmount": "499650000", + "destinationChainId": "1", + "recipient": "0x0000000000000000000000009858effd232b4033e47d90003d41ec34ecaeda94", + "exclusiveRelayer": "0x0000000000000000000000000000000000000000", + "exclusivityPeriod": 0, + "message": "0x" + } + } + } + }, + "expected": { + "domain_separator": "8a6e6e19bdfb3db3409910416b47c2f8fc28b49488d6555c7fceaa4479135bc3", + "message_hash": "a0e1fae1fef174757d2b9652c3d1721a9262a393e50904e7f51ba49c2e5e18a6", + "digest": "89121d721fbf3935ca58a646da33ae08eef33bdc61fb9584826bc68eb5b867d9" + } + }, + { + "id": "snapshot-Vote-uint32", + "source": [ + "https://github.com/snapshot-labs/snapshot.js/blob/ae2a8209e03ca1fa133a9105949a9386f3b539ed/src/sign/types.ts" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Vote": [ + { + "name": "from", + "type": "string" + }, + { + "name": "space", + "type": "string" + }, + { + "name": "timestamp", + "type": "uint64" + }, + { + "name": "proposal", + "type": "string" + }, + { + "name": "choice", + "type": "uint32" + }, + { + "name": "reason", + "type": "string" + }, + { + "name": "app", + "type": "string" + }, + { + "name": "metadata", + "type": "string" + } + ] + }, + "primaryType": "Vote", + "domain": { + "name": "snapshot", + "version": "0.1.4" + }, + "message": { + "from": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "space": "aavedao.eth", + "timestamp": 1775803746, + "proposal": "0x9e0b3d8a0b6b31c5c1b8c0b4e2c6b0bfa4f3dd1b0ec0e4b2d8a6b5c2e1f3a4d5", + "choice": 1, + "reason": "", + "app": "snapshot-v2", + "metadata": "{}" + } + }, + "expected": { + "domain_separator": "484fce18f892e8535a4b6700e197a8026f4213f809d23ae117da03b497e18670", + "message_hash": "7fabb0616963ab661ad570fdac5cd6dfc1896f62e75891bde5fdcd8e8fda9c30", + "digest": "2ebf83b128be8aa355a81a15424a3fc8966f27c7c2bceadf191be84304a16760" + } + }, + { + "id": "snapshot-Vote-uint32[]", + "source": [ + "https://github.com/snapshot-labs/snapshot.js/blob/ae2a8209e03ca1fa133a9105949a9386f3b539ed/src/sign/types.ts" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Vote": [ + { + "name": "from", + "type": "string" + }, + { + "name": "space", + "type": "string" + }, + { + "name": "timestamp", + "type": "uint64" + }, + { + "name": "proposal", + "type": "string" + }, + { + "name": "choice", + "type": "uint32[]" + }, + { + "name": "reason", + "type": "string" + }, + { + "name": "app", + "type": "string" + }, + { + "name": "metadata", + "type": "string" + } + ] + }, + "primaryType": "Vote", + "domain": { + "name": "snapshot", + "version": "0.1.4" + }, + "message": { + "from": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "space": "aavedao.eth", + "timestamp": 1775803746, + "proposal": "0x9e0b3d8a0b6b31c5c1b8c0b4e2c6b0bfa4f3dd1b0ec0e4b2d8a6b5c2e1f3a4d5", + "choice": [ + 1, + 3 + ], + "reason": "", + "app": "snapshot-v2", + "metadata": "{}" + } + }, + "expected": { + "domain_separator": "484fce18f892e8535a4b6700e197a8026f4213f809d23ae117da03b497e18670", + "message_hash": "445458b701f220e28b2f7851e2fb67d2d30f86ee9cfec5e947a86915c5b374fa", + "digest": "8942a9b491d57715c6becc2325fbe330fcb3c1fb1d4027267b64ca0bfadaa60a" + } + }, + { + "id": "snapshot-Vote-string", + "source": [ + "https://github.com/snapshot-labs/snapshot.js/blob/ae2a8209e03ca1fa133a9105949a9386f3b539ed/src/sign/types.ts" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Vote": [ + { + "name": "from", + "type": "string" + }, + { + "name": "space", + "type": "string" + }, + { + "name": "timestamp", + "type": "uint64" + }, + { + "name": "proposal", + "type": "string" + }, + { + "name": "choice", + "type": "string" + }, + { + "name": "reason", + "type": "string" + }, + { + "name": "app", + "type": "string" + }, + { + "name": "metadata", + "type": "string" + } + ] + }, + "primaryType": "Vote", + "domain": { + "name": "snapshot", + "version": "0.1.4" + }, + "message": { + "from": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "space": "aavedao.eth", + "timestamp": 1775803746, + "proposal": "0x9e0b3d8a0b6b31c5c1b8c0b4e2c6b0bfa4f3dd1b0ec0e4b2d8a6b5c2e1f3a4d5", + "choice": "{\"1\":60,\"2\":40}", + "reason": "", + "app": "snapshot-v2", + "metadata": "{}" + } + }, + "expected": { + "domain_separator": "484fce18f892e8535a4b6700e197a8026f4213f809d23ae117da03b497e18670", + "message_hash": "c5740e9f7b1eb3cc1c58a4c2e7ece755e59f657c58e707ecfb699847e787954e", + "digest": "13505558c6022d086c119463c0c57887a4de806e74d298e3b26b95b31bd763c8" + } + }, + { + "id": "snapshot-Vote-legacy-bytes32", + "source": [ + "https://github.com/snapshot-labs/snapshot.js/blob/2a13d16e/src/sign/types.ts" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Vote": [ + { + "name": "from", + "type": "address" + }, + { + "name": "space", + "type": "string" + }, + { + "name": "timestamp", + "type": "uint64" + }, + { + "name": "proposal", + "type": "bytes32" + }, + { + "name": "choice", + "type": "uint32" + }, + { + "name": "reason", + "type": "string" + }, + { + "name": "app", + "type": "string" + }, + { + "name": "metadata", + "type": "string" + } + ] + }, + "primaryType": "Vote", + "domain": { + "name": "snapshot", + "version": "0.1.4" + }, + "message": { + "from": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "space": "ens.eth", + "timestamp": 1775803746, + "proposal": "0x9e0b3d8a0b6b31c5c1b8c0b4e2c6b0bfa4f3dd1b0ec0e4b2d8a6b5c2e1f3a4d5", + "choice": 2, + "reason": "", + "app": "snapshot", + "metadata": "{}" + } + }, + "expected": { + "domain_separator": "484fce18f892e8535a4b6700e197a8026f4213f809d23ae117da03b497e18670", + "message_hash": "39aed682da209db5214780de752b4319204bb3e8d48faa0bc1698c2bb0147637", + "digest": "6672ce6256f657e4e9af0ac1eb6f7ca3e256cbdc9c8052f743da4bf899e211d0" + } + }, + { + "id": "metamask-Mail-v4", + "source": [ + "https://github.com/MetaMask/test-dapp/blob/v7.0.0/src/index.js" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person[]" + }, + { + "name": "contents", + "type": "string" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallets", + "type": "address[]" + } + ], + "Group": [ + { + "name": "name", + "type": "string" + }, + { + "name": "members", + "type": "Person[]" + } + ] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": [ + { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + } + ], + "contents": "Hello, Bob!" + } + }, + "expected": { + "domain_separator": "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f", + "message_hash": "eb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8", + "digest": "a85c2e2b118698e88db68a8105b794a8cc7cec074e89ef991cb4f5f533819cc2" + } + }, + { + "id": "eip712-spec-Mail", + "source": [ + "https://github.com/ethereum/EIPs/blob/master/assets/eip-712/Example.js" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } + }, + "expected": { + "domain_separator": "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f", + "message_hash": "c52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e", + "digest": "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2" + } + } +] diff --git a/tests/test_msg_eip712_corpus.py b/tests/test_msg_eip712_corpus.py new file mode 100644 index 00000000..791838ee --- /dev/null +++ b/tests/test_msg_eip712_corpus.py @@ -0,0 +1,92 @@ +# Real-world EIP-712 documents must sign. +# +# eip712_corpus.json holds one document per protocol (Permit2, UniswapX, +# Seaport, LooksRare, Blur, 1inch, CoW, 0x, Safe, EIP-2612/DAI, Hyperliquid, +# Polymarket, Compound, Aave, dYdX, Across, Snapshot, eth-sig-util), written +# from the type strings in each protocol's own source ("source" in each +# entry). It is generated by unittests/firmware/eip712_corpus_gen.py in the +# firmware repository, whose "expected" hashes were computed by a reference +# encoder and cross-checked against @metamask/eth-sig-util and ethers. +# +# Every document must be ACCEPTED: its hashes must equal both the embedded +# values and this suite's own independent encoder, its signature must +# recover to the device's address, and the final screen must name the whole +# primary type. + +import json +import os +import unittest + +import common +import oled_text +import test_msg_eip712_streaming as streaming +from keepkeylib import messages_ethereum_pb2 as eth +from keepkeylib import types_pb2 as types + +CORPUS = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'eip712_corpus.json') + + +def load_corpus(): + with open(CORPUS) as f: + return json.load(f) + + +class TestEip712CorpusReference(unittest.TestCase): + + def test_embedded_hashes_match_the_independent_encoder(self): + """The suite's own encoder agrees with the generator's values.""" + for entry in load_corpus(): + with self.subTest(entry['id']): + domain, message, digest = \ + streaming.reference_eip712_hashes(entry['doc']) + self.assertEqual(domain.hex(), + entry['expected']['domain_separator']) + self.assertEqual(message.hex(), + entry['expected']['message_hash']) + self.assertEqual(digest.hex(), entry['expected']['digest']) + + +class TestMsgEip712Corpus(common.KeepKeyTest): + + # The device-driven walk from the streaming suite; borrowed, not + # inherited, so its tests do not run twice. + _walk = streaming.TestMsgEip712Streaming._walk + + def setUp(self): + super(TestMsgEip712Corpus, self).setUp() + self.requires_firmware("7.15.0") + self.requires_fullFeature() + self.requires_structured_eip712() + self.setup_mnemonic_nopin_nopassphrase() + self.client.apply_policy('AdvancedMode', 0) + self.client.reset_screenshots() + + def test_every_real_world_document_signs(self): + address = self.client.ethereum_get_address(streaming.PATH) + for entry in load_corpus(): + with self.subTest(entry['id']): + doc = entry['doc'] + resp = self._walk(doc, max_steps=2000) + self.assertIsInstance(resp, eth.EthereumTypedDataSignature, + getattr(resp, 'message', resp)) + self.assertEqual(resp.domain_separator_hash.hex(), + entry['expected']['domain_separator']) + self.assertEqual(resp.message_hash.hex(), + entry['expected']['message_hash']) + digest = bytes.fromhex(entry['expected']['digest']) + self.assertEqual( + streaming.recover_signer(resp.signature, digest).hex(), + address.hex()) + # The final screen (and its continuation pages) names the + # whole primary type, however long. + codes = [code for code, _ in self.frames] + self.assertEqual(codes.count(types.ButtonRequest_SignTx), 1) + final = codes.index(types.ButtonRequest_SignTx) + self.assertIsNotNone(oled_text.find_text( + [layout for _, layout in self.frames[final:]], + 'Sign ' + doc['primaryType'])) + + +if __name__ == '__main__': + unittest.main() From 7726cc8eaf16228d2c20f6ce96dbcac29b57a3ba Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 03:57:54 -0500 Subject: [PATCH 13/37] test(eth): unlimited approvals and permits sign after a warning From erc20-unlimited-approve-review and erc20-unlimited-permit-review, 7.15 signs an unlimited approve after an UNLIMITED approval screen with the full spender, and shows an unlimited permit value as UNLIMITED on its own screen. The refusal tests now run only on firmware that lacks those capabilities (requires_capability_absent). The corpus gains three max approvals: the Uniswap default Permit2 PermitSingle (uint160 max), an EIP-2612 USDC permit of 2^256-1 and a DAI permit with allowed=true. They run where the firmware reports the capability, and before 7.16 their value screen must read UNLIMITED. --- tests/common.py | 9 + tests/eip712_corpus.json | 222 ++++++++++++++++++ tests/test_msg_eip712_corpus.py | 18 +- tests/test_msg_eip712_streaming.py | 36 +++ tests/test_msg_ethereum_clear_signing.py | 9 +- tests/test_msg_ethereum_erc20_approve.py | 45 ++-- ...st_msg_ethereum_erc20_uniswap_liquidity.py | 3 +- tests/test_msg_signtx_ethereum_erc20.py | 43 ++-- 8 files changed, 348 insertions(+), 37 deletions(-) diff --git a/tests/common.py b/tests/common.py index 0ca314f0..de156f25 100644 --- a/tests/common.py +++ b/tests/common.py @@ -278,6 +278,15 @@ def requires_release_capability(self, capability): "Staged release tree does not yet provide capability: " + capability) + def requires_capability_absent(self, capability): + """Skip when the firmware reports `capability`: for a refusal that + the capability's behaviour replaces within the same version.""" + enum = messages_pb2.Features.Capability + enum.Value("CAPABILITY_" + capability.upper().replace("-", "_")) + reported = self.firmware_capabilities() + if reported is not None and capability in reported: + self.skipTest("Firmware provides capability: " + capability) + def requires_taproot(self): """Skip unless the firmware reports taproot support. diff --git a/tests/eip712_corpus.json b/tests/eip712_corpus.json index 2dbd9213..6c9a254a 100644 --- a/tests/eip712_corpus.json +++ b/tests/eip712_corpus.json @@ -76,6 +76,86 @@ "digest": "8b4985b6263546864eb8921379708c29bf922304edf4fc0abe5e077f853a60b2" } }, + { + "id": "permit2-PermitSingle-unlimited", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/sdks/blob/main/sdks/permit2-sdk/src/constants.ts" + ], + "requires_capability": "erc20-unlimited-permit-review", + "unlimited_screen": "details.amount\nuint160: UNLIMITED", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitSingle": [ + { + "name": "details", + "type": "PermitDetails" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "sigDeadline", + "type": "uint256" + } + ], + "PermitDetails": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint160" + }, + { + "name": "expiration", + "type": "uint48" + }, + { + "name": "nonce", + "type": "uint48" + } + ] + }, + "primaryType": "PermitSingle", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "details": { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "1461501637330902918203684832716283019655932542975", + "expiration": "1778393946", + "nonce": "0" + }, + "spender": "0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af", + "sigDeadline": "1775803746" + } + }, + "expected": { + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "8cadbd22e396ba77b30a62c8219759ec221ed18cde6fe1c31204fb3d53c1ecf8", + "digest": "eb4da5f3381371818ca113f3f4697da6c5ee1bbb098effde0f8f399b208f7bd7" + } + }, { "id": "permit2-PermitBatch", "source": [ @@ -2113,6 +2193,77 @@ "digest": "9dfbe3c2bc66846b4e4d05cf917567d5448bac92b4b575285df4b198f459ed94" } }, + { + "id": "eip2612-Permit-unlimited", + "source": [ + "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/c98b7188338e3c5fce903747894561af78863bbb/contracts/token/ERC20/extensions/ERC20Permit.sol" + ], + "requires_capability": "erc20-unlimited-permit-review", + "unlimited_screen": "value\nuint256: UNLIMITED", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Permit": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + } + ] + }, + "primaryType": "Permit", + "domain": { + "name": "USD Coin", + "version": "2", + "chainId": 1, + "verifyingContract": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + "message": { + "owner": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "value": "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "nonce": "0", + "deadline": "1775803746" + } + }, + "expected": { + "domain_separator": "06c37168a7db5138defc7866392bb87a741f9b3d104deb5094588ce041cae335", + "message_hash": "33ffd631195880f28f81f273a3be51c770ca95311a3f90cf2caa81f3b338a26c", + "digest": "e8f07ca0d9556242ed695a7367e5ffe5d5e8b3913a82a763a83cf1be5d03ad1f" + } + }, { "id": "dai-Permit", "source": [ @@ -2182,6 +2333,77 @@ "digest": "ea585737a895795b5b84df7ff9082e15098bda1e22cc312b27ce64c6b656bde6" } }, + { + "id": "dai-Permit-allowed", + "source": [ + "https://github.com/makerdao/dss/blob/fa4f6630afb0624d04a003e920b0d71a00331d98/src/dai.sol" + ], + "requires_capability": "erc20-unlimited-permit-review", + "unlimited_screen": "allowed\nbool: UNLIMITED (allowed)", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Permit": [ + { + "name": "holder", + "type": "address" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "expiry", + "type": "uint256" + }, + { + "name": "allowed", + "type": "bool" + } + ] + }, + "primaryType": "Permit", + "domain": { + "name": "Dai Stablecoin", + "version": "1", + "chainId": 1, + "verifyingContract": "0x6B175474E89094C44Da98b954EedeAC495271d0F" + }, + "message": { + "holder": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "nonce": "3", + "expiry": "1775803746", + "allowed": true + } + }, + "expected": { + "domain_separator": "dbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7", + "message_hash": "d087cc737aa821393dd824ea87dcb51376c51b9815d52c6f5102c6e843b128c0", + "digest": "c2b81aa8b44a91816e41ff0810a9ccb9bf0226cbdfd901493205a18fc5eb94a4" + } + }, { "id": "hyperliquid-Agent", "source": [ diff --git a/tests/test_msg_eip712_corpus.py b/tests/test_msg_eip712_corpus.py index 791838ee..c9b24447 100644 --- a/tests/test_msg_eip712_corpus.py +++ b/tests/test_msg_eip712_corpus.py @@ -11,7 +11,9 @@ # Every document must be ACCEPTED: its hashes must equal both the embedded # values and this suite's own independent encoder, its signature must # recover to the device's address, and the final screen must name the whole -# primary type. +# primary type. A document with "requires_capability" (the max approvals) +# runs only where the firmware reports it; before 7.16 its value screen is +# titled UNLIMITED APPROVAL and shows "unlimited_screen". import json import os @@ -66,6 +68,10 @@ def test_every_real_world_document_signs(self): address = self.client.ethereum_get_address(streaming.PATH) for entry in load_corpus(): with self.subTest(entry['id']): + needs = entry.get('requires_capability') + if needs and needs not in (self.firmware_capabilities() or ()): + self.skipTest("Staged release tree does not yet provide " + "capability: " + needs) doc = entry['doc'] resp = self._walk(doc, max_steps=2000) self.assertIsInstance(resp, eth.EthereumTypedDataSignature, @@ -86,6 +92,16 @@ def test_every_real_world_document_signs(self): self.assertIsNotNone(oled_text.find_text( [layout for _, layout in self.frames[final:]], 'Sign ' + doc['primaryType'])) + warned = [layout for _, layout in self.frames + if oled_text.find_line(layout, 'UNLIMITED APPROVAL', + oled_text.TITLE_FONT) + is not None] + if 'unlimited_screen' not in entry: + self.assertEqual(warned, []) + elif not self.firmware_at_least("7.16.0"): + self.assertEqual(len(warned), 1) + self.assertTrue(oled_text.shows( + warned[0], entry['unlimited_screen'])) if __name__ == '__main__': diff --git a/tests/test_msg_eip712_streaming.py b/tests/test_msg_eip712_streaming.py index 0acf531a..7d1f4be6 100644 --- a/tests/test_msg_eip712_streaming.py +++ b/tests/test_msg_eip712_streaming.py @@ -749,6 +749,7 @@ def test_unlimited_permits_are_refused_before_the_amount_screen(self): instead (F-D, D-010): test_canonical_unlimited_permits_sign_after_a_ warning.""" self.requires_firmware_below("7.16.0") + self.requires_capability_absent("erc20-unlimited-permit-review") permit = copy.deepcopy(self.USDC_PERMIT) for doc, path, leaf, bits in ((permit, ("value",), "value", 256),): # A finite amount signs and shows the leaf; its position is where @@ -767,6 +768,41 @@ def test_unlimited_permits_are_refused_before_the_amount_screen(self): 'Unlimited ERC20 approval is disabled')) self.assertEqual(len(self.frames), before_leaf) + def test_unlimited_permits_sign_with_unlimited_on_the_value_screen(self): + """7.15 (owner decision 2026-10-07): an unlimited EIP-2612 value, a + DAI allowed=true and a Permit2 amount at its uint160 maximum are + signed. The value's own screen is titled UNLIMITED APPROVAL and reads + UNLIMITED; a finite value keeps the ordinary screen.""" + self.requires_firmware("7.15.0") + self.requires_firmware_below("7.16.0") + self.requires_release_capability("erc20-unlimited-permit-review") + usdc = copy.deepcopy(self.USDC_PERMIT) + usdc["message"]["value"] = str((1 << 256) - 1) + permit2 = copy.deepcopy(self.UNISWAP_PERMIT2) + cases = ((usdc, "value", "value\nuint256: UNLIMITED"), + (self.DAI_PERMIT, "allowed", + "allowed\nbool: UNLIMITED (allowed)"), + (permit2, "details.amount", + "details.amount\nuint160: UNLIMITED")) + for doc, leaf, body in cases: + self._assert_reference_signature(doc, self._walk(doc)) + warnings = [i for i, (_, layout) in enumerate(self.frames) + if oled_text.find_line(layout, 'UNLIMITED APPROVAL', + oled_text.TITLE_FONT) + is not None] + with self.subTest(leaf): + self.assertEqual(warnings, [self._leaf_index(leaf)]) + self.assertTrue(oled_text.shows(self.frames[warnings[0]][1], + body), body) + self._assert_reference_signature(self.USDC_PERMIT, + self._walk(self.USDC_PERMIT)) + self.assertIsNone(oled_text.find_text( + [layout for _, layout in self.frames], "value\nuint256: UNLIMITED")) + self.assertTrue(all( + oled_text.find_line(layout, 'UNLIMITED APPROVAL', + oled_text.TITLE_FONT) is None + for _, layout in self.frames)) + def test_canonical_unlimited_permits_sign_after_a_warning(self): """D-010 for permits (F-D): a canonical EIP-2612 permit of 2^255 or more, or a canonical DAI permit with allowed=true, is signed after diff --git a/tests/test_msg_ethereum_clear_signing.py b/tests/test_msg_ethereum_clear_signing.py index 8893484c..0be2bc30 100644 --- a/tests/test_msg_ethereum_clear_signing.py +++ b/tests/test_msg_ethereum_clear_signing.py @@ -1032,9 +1032,12 @@ def _clearsign_flow(self, flow, chain_id=1): self.assertEqual(resp.classification, CLASSIFICATION_VERIFIED) if (flow['key'] == 'erc20-approve-unlimited' - and not self.firmware_at_least("7.16.0")): - # 7.15 refuses an unlimited approve. 7.16 reviews and signs it - # (D-010), handled by the common path below. + and not self.firmware_at_least("7.16.0") + and "erc20-unlimited-approve-review" not in + (self.firmware_capabilities() or ())): + # 7.15 refused an unlimited approve until + # erc20-unlimited-approve-review; from then it is signed after a + # warning, handled by the common path below. # Drive the wire directly: the policy is refused on the FIRST # response, before any ButtonRequest. Expected-response helpers # would raise their own exception text containing the expected diff --git a/tests/test_msg_ethereum_erc20_approve.py b/tests/test_msg_ethereum_erc20_approve.py index c3f29356..f3a5c9e7 100644 --- a/tests/test_msg_ethereum_erc20_approve.py +++ b/tests/test_msg_ethereum_erc20_approve.py @@ -24,6 +24,8 @@ import keepkeylib.messages_pb2 as proto import keepkeylib.types_pb2 as proto_types from keepkeylib.client import CallException +from oled_text import TITLE_FONT, find_line, shows +from test_msg_display_disclosure import ScreenRecorder class TestMsgEthereumtxERC20_approve(common.KeepKeyTest): @@ -68,11 +70,13 @@ def test_approve_cvc_0(self): self.assertEqual(binascii.hexlify(sig_s), '0bc7319762281d839c436adb41c35f8de5f4db1aec953f677c3a83062d93fc51') def test_approve_cvc_all_refused(self): - # 7.14.2 through 7.15.x refuse an unlimited approve outright. 7.16 - # reviews and signs it instead (D-010): test_approve_cvc_all. + # 7.14.2 through 7.15.x refuse an unlimited approve outright, until + # erc20-unlimited-approve-review: from then it is signed after an + # UNLIMITED warning (test_approve_cvc_all). self.requires_fullFeature() self.requires_firmware("7.14.2") self.requires_firmware_below("7.16.0") + self.requires_capability_absent("erc20-unlimited-approve-review") self.setup_mnemonic_nopin_nopassphrase() with self.assertRaises(CallException): @@ -91,23 +95,32 @@ def test_approve_cvc_all_refused(self): def test_approve_cvc_all(self): self.requires_fullFeature() if self.firmware_at_least("7.14.2"): - # Refused on 7.14.2-7.15.x (test_approve_cvc_all_refused); - # reviewed and signed from 7.16 (D-010). - self.requires_firmware("7.16.0") + # Refused on 7.14.2-7.15.x (test_approve_cvc_all_refused) until + # erc20-unlimited-approve-review; signed after a warning since. + self.requires_firmware("7.15.0") self.requires_release_capability("erc20-unlimited-approve-review") self.setup_mnemonic_nopin_nopassphrase() - sig_v, sig_r, sig_s = self.client.ethereum_sign_tx( - n=[2147483692,2147483708,2147483648,0,0], - nonce=1, - gas_price=20, - gas_limit=20, - value=0, - to=binascii.unhexlify('41e5560054824ea6b0732e656e3ad64e20e94e45'), - address_type=0, - chain_id=1, - data=binascii.unhexlify('095ea7b3' + '0000000000000000000000001d8ce9022f6284c3a5c317f8f34620107214e545' + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') - ) + recorder = ScreenRecorder(self.client) + with recorder: + sig_v, sig_r, sig_s = self.client.ethereum_sign_tx( + n=[2147483692,2147483708,2147483648,0,0], + nonce=1, + gas_price=20, + gas_limit=20, + value=0, + to=binascii.unhexlify('41e5560054824ea6b0732e656e3ad64e20e94e45'), + address_type=0, + chain_id=1, + data=binascii.unhexlify('095ea7b3' + '0000000000000000000000001d8ce9022f6284c3a5c317f8f34620107214e545' + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') + ) + if self.firmware_at_least("7.15.0"): + # The UNLIMITED warning is the first screen, full spender. + self.assertIsNotNone(find_line(recorder.screens[0], + 'UNLIMITED APPROVAL', TITLE_FONT)) + self.assertTrue(shows(recorder.screens[0], + 'Allow 0x1D8CE9022f6284c3A5c317f8f34620107214E545 ' + 'to spend ALL your CVC')) self.assertEqual(sig_v, 37) self.assertEqual(binascii.hexlify(sig_r), 'bb4c640b79f946e1399450dfc615b0a6024b6724f167cef70cf2530408fc6339') diff --git a/tests/test_msg_ethereum_erc20_uniswap_liquidity.py b/tests/test_msg_ethereum_erc20_uniswap_liquidity.py index 8c02ebeb..f5a50729 100644 --- a/tests/test_msg_ethereum_erc20_uniswap_liquidity.py +++ b/tests/test_msg_ethereum_erc20_uniswap_liquidity.py @@ -37,6 +37,7 @@ def test_sign_uni_approve_liquidity_ETH_refused(self): self.requires_fullFeature() self.requires_firmware_below("7.16.0") self.requires_release_capability("erc7730-runtime-review") + self.requires_capability_absent("erc20-unlimited-approve-review") self.setup_mnemonic_nopin_nopassphrase() self.client.apply_policy("AdvancedMode", 1) @@ -66,8 +67,6 @@ def test_sign_uni_approve_liquidity_ETH_refused(self): def test_sign_uni_approve_liquidity_ETH(self): self.requires_fullFeature() - self.requires_firmware("7.16.0") - self.requires_release_capability("erc7730-runtime-review") self.requires_release_capability("erc20-unlimited-approve-review") self.setup_mnemonic_nopin_nopassphrase() self.client.apply_policy("AdvancedMode", 1) diff --git a/tests/test_msg_signtx_ethereum_erc20.py b/tests/test_msg_signtx_ethereum_erc20.py index 6567658e..0b557f1e 100644 --- a/tests/test_msg_signtx_ethereum_erc20.py +++ b/tests/test_msg_signtx_ethereum_erc20.py @@ -25,6 +25,8 @@ import keepkeylib.messages_pb2 as proto import keepkeylib.types_pb2 as proto_types from keepkeylib.client import CallException +from oled_text import TITLE_FONT, find_line, shows +from test_msg_display_disclosure import ScreenRecorder from keepkeylib.tools import int_to_big_endian class TestMsgEthereumSigntxERC20(common.KeepKeyTest): @@ -68,11 +70,13 @@ def test_approve_some(self): self.assertEqual(binascii.hexlify(sig_s), '5b8b0e5b7b8f6d5269ce4dc266e6901f3284079fa1f0cd358d2987336dc8ba3a') def test_approve_all_refused(self): - # 7.14.2 through 7.15.x refuse an unlimited approve outright. 7.16 - # reviews and signs it instead (D-010): test_approve_all. + # 7.14.2 through 7.15.x refuse an unlimited approve outright, until + # erc20-unlimited-approve-review: from then it is signed after an + # UNLIMITED warning (test_approve_all). self.requires_fullFeature() self.requires_firmware("7.14.2") self.requires_firmware_below("7.16.0") + self.requires_capability_absent("erc20-unlimited-approve-review") self.setup_mnemonic_nopin_nopassphrase() with self.assertRaises(CallException): @@ -90,22 +94,31 @@ def test_approve_all_refused(self): def test_approve_all(self): self.requires_fullFeature() if self.firmware_at_least("7.14.2"): - # Refused on 7.14.2-7.15.x (test_approve_all_refused); - # reviewed and signed from 7.16 (D-010). - self.requires_firmware("7.16.0") + # Refused on 7.14.2-7.15.x (test_approve_all_refused) until + # erc20-unlimited-approve-review; signed after a warning since. + self.requires_firmware("7.15.0") self.requires_release_capability("erc20-unlimited-approve-review") self.setup_mnemonic_nopin_nopassphrase() - sig_v, sig_r, sig_s = self.client.ethereum_sign_tx( - n=[2147483692,2147483708,2147483648,0,0], - nonce=1, - gas_price=20, - gas_limit=20, - value=0, - to=binascii.unhexlify('41e5560054824ea6b0732e656e3ad64e20e94e45'), - chain_id=1, - data=binascii.unhexlify('095ea7b3000000000000000000000000' + '1d1c328764a41bda0492b66baa30c4a339ff85ef' + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'), - ) + recorder = ScreenRecorder(self.client) + with recorder: + sig_v, sig_r, sig_s = self.client.ethereum_sign_tx( + n=[2147483692,2147483708,2147483648,0,0], + nonce=1, + gas_price=20, + gas_limit=20, + value=0, + to=binascii.unhexlify('41e5560054824ea6b0732e656e3ad64e20e94e45'), + chain_id=1, + data=binascii.unhexlify('095ea7b3000000000000000000000000' + '1d1c328764a41bda0492b66baa30c4a339ff85ef' + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'), + ) + if self.firmware_at_least("7.15.0"): + # The UNLIMITED warning is the first screen, full spender. + self.assertIsNotNone(find_line(recorder.screens[0], + 'UNLIMITED APPROVAL', TITLE_FONT)) + self.assertTrue(shows(recorder.screens[0], + 'Allow 0x1D1C328764A41BdA0492b66bAA30C4A339FF85EF ' + 'to spend ALL your CVC')) self.assertEqual(sig_v, 37) self.assertEqual(binascii.hexlify(sig_r), '3671acb6aed5241948de56635ef64554d5e834355e99d806c4ae30bf463eae57') From 5670d8f7e84cdf9bcd92b62192774811994b0f3f Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 05:57:03 -0500 Subject: [PATCH 14/37] test(eip712): one test per corpus document, and larger real documents Each corpus document is now its own test (test_doc_), so one slow document no longer exhausts a single test's timeout and a refusal names the document. The 7.15.0 gate is unchanged. New documents: a Seaport bundle of 10 offer and 6 consideration items, an order with 12 consideration items, a 32-item bundle, Permit2 batches of 20 and 32, a Snapshot vote whose reason holds a newline, a tab and a CR, and a Safe transaction whose data is an unlimited USDC approve, which must show the UNLIMITED warning first. --- tests/eip712_corpus.json | 2367 +++++++++++++++++++++++++------ tests/test_msg_eip712_corpus.py | 92 +- 2 files changed, 2001 insertions(+), 458 deletions(-) diff --git a/tests/eip712_corpus.json b/tests/eip712_corpus.json index 6c9a254a..39980742 100644 --- a/tests/eip712_corpus.json +++ b/tests/eip712_corpus.json @@ -260,7 +260,7 @@ } }, { - "id": "permit2-PermitTransferFrom", + "id": "permit2-PermitBatch-20", "source": [ "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol" ], @@ -280,59 +280,180 @@ "type": "address" } ], - "PermitTransferFrom": [ + "PermitBatch": [ { - "name": "permitted", - "type": "TokenPermissions" + "name": "details", + "type": "PermitDetails[]" }, { "name": "spender", "type": "address" }, { - "name": "nonce", - "type": "uint256" - }, - { - "name": "deadline", + "name": "sigDeadline", "type": "uint256" } ], - "TokenPermissions": [ + "PermitDetails": [ { "name": "token", "type": "address" }, { "name": "amount", - "type": "uint256" + "type": "uint160" + }, + { + "name": "expiration", + "type": "uint48" + }, + { + "name": "nonce", + "type": "uint48" } ] }, - "primaryType": "PermitTransferFrom", + "primaryType": "PermitBatch", "domain": { "name": "Permit2", "chainId": 1, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "message": { - "permitted": { - "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "amount": "250000000" - }, + "details": [ + { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "1000000", + "expiration": "1893456000", + "nonce": "0" + }, + { + "token": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "amount": "2000000", + "expiration": "1893456000", + "nonce": "1" + }, + { + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "amount": "3000000", + "expiration": "1893456000", + "nonce": "2" + }, + { + "token": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "amount": "4000000", + "expiration": "1893456000", + "nonce": "3" + }, + { + "token": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "amount": "5000000", + "expiration": "1893456000", + "nonce": "4" + }, + { + "token": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", + "amount": "6000000", + "expiration": "1893456000", + "nonce": "5" + }, + { + "token": "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "amount": "7000000", + "expiration": "1893456000", + "nonce": "6" + }, + { + "token": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + "amount": "8000000", + "expiration": "1893456000", + "nonce": "7" + }, + { + "token": "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", + "amount": "9000000", + "expiration": "1893456000", + "nonce": "8" + }, + { + "token": "0xc00e94Cb662C3520282E6f5717214004A7f26888", + "amount": "10000000", + "expiration": "1893456000", + "nonce": "9" + }, + { + "token": "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F", + "amount": "11000000", + "expiration": "1893456000", + "nonce": "10" + }, + { + "token": "0xD533a949740bb3306d119CC777fa900bA034cd52", + "amount": "12000000", + "expiration": "1893456000", + "nonce": "11" + }, + { + "token": "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32", + "amount": "13000000", + "expiration": "1893456000", + "nonce": "12" + }, + { + "token": "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0", + "amount": "14000000", + "expiration": "1893456000", + "nonce": "13" + }, + { + "token": "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", + "amount": "15000000", + "expiration": "1893456000", + "nonce": "14" + }, + { + "token": "0x4d224452801ACEd8B2F0aebE155379bb5D594381", + "amount": "16000000", + "expiration": "1893456000", + "nonce": "15" + }, + { + "token": "0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72", + "amount": "17000000", + "expiration": "1893456000", + "nonce": "16" + }, + { + "token": "0x111111111117dC0aa78b770fA6A738034120C302", + "amount": "18000000", + "expiration": "1893456000", + "nonce": "17" + }, + { + "token": "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2", + "amount": "19000000", + "expiration": "1893456000", + "nonce": "18" + }, + { + "token": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e", + "amount": "20000000", + "expiration": "1893456000", + "nonce": "19" + } + ], "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", - "nonce": "1997", - "deadline": "1775803746" + "sigDeadline": "1775803746" } }, "expected": { "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", - "message_hash": "a72b11929a9c4bff37f00eb63b3615519e00a8aa297140855c44cfff4da27146", - "digest": "ece4ce2c0d6c3f27171599e51e0b42287645bb3029f07ef9671ddca458007d5e" + "message_hash": "9be08a04cc2b5ee94ecba4347a8469ee3d3192f50b2613d59887f090f195142f", + "digest": "384af6a2fc07a1e70fa27fec8120723fb7ac8c922c53cfe2d8aaed6e6a401e33" } }, { - "id": "permit2-PermitBatchTransferFrom", + "id": "permit2-PermitBatch-32", "source": [ "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol" ], @@ -352,273 +473,254 @@ "type": "address" } ], - "PermitBatchTransferFrom": [ + "PermitBatch": [ { - "name": "permitted", - "type": "TokenPermissions[]" + "name": "details", + "type": "PermitDetails[]" }, { "name": "spender", "type": "address" }, { - "name": "nonce", - "type": "uint256" - }, - { - "name": "deadline", + "name": "sigDeadline", "type": "uint256" } ], - "TokenPermissions": [ + "PermitDetails": [ { "name": "token", "type": "address" }, { "name": "amount", - "type": "uint256" + "type": "uint160" + }, + { + "name": "expiration", + "type": "uint48" + }, + { + "name": "nonce", + "type": "uint48" } ] }, - "primaryType": "PermitBatchTransferFrom", + "primaryType": "PermitBatch", "domain": { "name": "Permit2", "chainId": 1, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "message": { - "permitted": [ + "details": [ { "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "amount": "1000000" + "amount": "1000000", + "expiration": "1893456000", + "nonce": "0" }, { "token": "0x6B175474E89094C44Da98b954EedeAC495271d0F", - "amount": "10000000" + "amount": "2000000", + "expiration": "1893456000", + "nonce": "1" }, { "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "amount": "100000000" + "amount": "3000000", + "expiration": "1893456000", + "nonce": "2" }, { "token": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "amount": "1000000000" + "amount": "4000000", + "expiration": "1893456000", + "nonce": "3" }, { "token": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "amount": "10000000000" - } - ], - "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", - "nonce": "1998", - "deadline": "1775803746" - } - }, - "expected": { - "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", - "message_hash": "b0347a078a3a77dee64d7c255ba56e39221a3d6c5b2375d2e89d09b28533b5ab", - "digest": "31227578aa0209aad26446c56711f4f4ad009a6befab3034ce466ad45e295090" - } - }, - { - "id": "uniswapx-ExclusiveDutchOrder", - "source": [ - "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", - "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/ExclusiveDutchOrderLib.sol", - "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol", - "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/DutchOrderLib.sol" - ], - "doc": { - "types": { - "EIP712Domain": [ + "amount": "5000000", + "expiration": "1893456000", + "nonce": "4" + }, { - "name": "name", - "type": "string" + "token": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", + "amount": "6000000", + "expiration": "1893456000", + "nonce": "5" }, { - "name": "chainId", - "type": "uint256" + "token": "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "amount": "7000000", + "expiration": "1893456000", + "nonce": "6" }, { - "name": "verifyingContract", - "type": "address" - } - ], - "PermitWitnessTransferFrom": [ + "token": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + "amount": "8000000", + "expiration": "1893456000", + "nonce": "7" + }, { - "name": "permitted", - "type": "TokenPermissions" + "token": "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", + "amount": "9000000", + "expiration": "1893456000", + "nonce": "8" }, { - "name": "spender", - "type": "address" + "token": "0xc00e94Cb662C3520282E6f5717214004A7f26888", + "amount": "10000000", + "expiration": "1893456000", + "nonce": "9" }, { - "name": "nonce", - "type": "uint256" + "token": "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F", + "amount": "11000000", + "expiration": "1893456000", + "nonce": "10" }, { - "name": "deadline", - "type": "uint256" + "token": "0xD533a949740bb3306d119CC777fa900bA034cd52", + "amount": "12000000", + "expiration": "1893456000", + "nonce": "11" }, { - "name": "witness", - "type": "ExclusiveDutchOrder" - } - ], - "DutchOutput": [ + "token": "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32", + "amount": "13000000", + "expiration": "1893456000", + "nonce": "12" + }, { - "name": "token", - "type": "address" + "token": "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0", + "amount": "14000000", + "expiration": "1893456000", + "nonce": "13" }, { - "name": "startAmount", - "type": "uint256" + "token": "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", + "amount": "15000000", + "expiration": "1893456000", + "nonce": "14" }, { - "name": "endAmount", - "type": "uint256" + "token": "0x4d224452801ACEd8B2F0aebE155379bb5D594381", + "amount": "16000000", + "expiration": "1893456000", + "nonce": "15" }, { - "name": "recipient", - "type": "address" - } - ], - "ExclusiveDutchOrder": [ + "token": "0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72", + "amount": "17000000", + "expiration": "1893456000", + "nonce": "16" + }, { - "name": "info", - "type": "OrderInfo" + "token": "0x111111111117dC0aa78b770fA6A738034120C302", + "amount": "18000000", + "expiration": "1893456000", + "nonce": "17" }, { - "name": "decayStartTime", - "type": "uint256" + "token": "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2", + "amount": "19000000", + "expiration": "1893456000", + "nonce": "18" }, { - "name": "decayEndTime", - "type": "uint256" + "token": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e", + "amount": "20000000", + "expiration": "1893456000", + "nonce": "19" }, { - "name": "exclusiveFiller", - "type": "address" + "token": "0xba100000625a3754423978a60c9317c58a424e3D", + "amount": "21000000", + "expiration": "1893456000", + "nonce": "20" }, { - "name": "exclusivityOverrideBps", - "type": "uint256" + "token": "0xc944E90C64B2c07662A292be6244BDf05Cda44a7", + "amount": "22000000", + "expiration": "1893456000", + "nonce": "21" }, { - "name": "inputToken", - "type": "address" + "token": "0xD33526068D116cE69F19A9ee46F0bd304F21A51f", + "amount": "23000000", + "expiration": "1893456000", + "nonce": "22" }, { - "name": "inputStartAmount", - "type": "uint256" + "token": "0x853d955aCEf822Db058eb8505911ED77F175b99e", + "amount": "24000000", + "expiration": "1893456000", + "nonce": "23" }, { - "name": "inputEndAmount", - "type": "uint256" + "token": "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", + "amount": "25000000", + "expiration": "1893456000", + "nonce": "24" }, { - "name": "outputs", - "type": "DutchOutput[]" - } - ], - "OrderInfo": [ - { - "name": "reactor", - "type": "address" + "token": "0xae78736Cd615f374D3085123A210448E74Fc6393", + "amount": "26000000", + "expiration": "1893456000", + "nonce": "25" }, { - "name": "swapper", - "type": "address" + "token": "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", + "amount": "27000000", + "expiration": "1893456000", + "nonce": "26" }, { - "name": "nonce", - "type": "uint256" + "token": "0x6982508145454Ce325dDbE47a25d4ec3d2311933", + "amount": "28000000", + "expiration": "1893456000", + "nonce": "27" }, { - "name": "deadline", - "type": "uint256" + "token": "0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1", + "amount": "29000000", + "expiration": "1893456000", + "nonce": "28" }, { - "name": "additionalValidationContract", - "type": "address" + "token": "0x5283D291DBCF85356A21bA090E6db59121208b44", + "amount": "30000000", + "expiration": "1893456000", + "nonce": "29" }, { - "name": "additionalValidationData", - "type": "bytes" - } - ], - "TokenPermissions": [ - { - "name": "token", - "type": "address" + "token": "0x92D6C1e31e14520e676a687F0a93788B716BEff5", + "amount": "31000000", + "expiration": "1893456000", + "nonce": "30" }, { - "name": "amount", - "type": "uint256" + "token": "0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0", + "amount": "32000000", + "expiration": "1893456000", + "nonce": "31" } - ] - }, - "primaryType": "PermitWitnessTransferFrom", - "domain": { - "name": "Permit2", - "chainId": 1, - "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" - }, - "message": { - "permitted": { - "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "amount": "3500000000" - }, - "spender": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4", - "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", - "deadline": "1775803746", - "witness": { - "info": { - "reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4", - "swapper": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", - "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", - "deadline": "1775803746", - "additionalValidationContract": "0x0000000000000000000000000000000000000000", - "additionalValidationData": "0x" - }, - "decayStartTime": "1775803680", - "decayEndTime": "1775803740", - "exclusiveFiller": "0xff8ba4d1fc3762f6154cc942ccf30049a2a0cec6", - "exclusivityOverrideBps": "100", - "inputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "inputStartAmount": "3500000000", - "inputEndAmount": "3500000000", - "outputs": [ - { - "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "startAmount": "1000000000000000000", - "endAmount": "990000000000000000", - "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" - }, - { - "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "startAmount": "1500000000000000", - "endAmount": "1485000000000000", - "recipient": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47" - } - ] - } + ], + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "sigDeadline": "1775803746" } }, "expected": { "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", - "message_hash": "9d5278e60026d517ef5795ddfa37fdbb3c45bb9aa1428e1100420191c4d582bf", - "digest": "5def1fbddaf0631deb17c013e1778be8a134da1e4f4ce1ed070479f12e4cdc67" + "message_hash": "26ac74547827726b6c9b707bd325892b633aaef21046c2f12937a075a47fe852", + "digest": "a85ffbece1abfb51170f9d17e6e63ed14b6a5e8ee6b6b39054fe5b8153a9f76a" } }, { - "id": "uniswapx-V2DutchOrder", + "id": "permit2-PermitTransferFrom", "source": [ - "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", - "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/V2DutchOrderLib.sol", - "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol" + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol" ], "doc": { "types": { @@ -636,7 +738,7 @@ "type": "address" } ], - "PermitWitnessTransferFrom": [ + "PermitTransferFrom": [ { "name": "permitted", "type": "TokenPermissions" @@ -652,37 +754,69 @@ { "name": "deadline", "type": "uint256" - }, - { - "name": "witness", - "type": "V2DutchOrder" } ], - "DutchOutput": [ + "TokenPermissions": [ { "name": "token", "type": "address" }, { - "name": "startAmount", + "name": "amount", "type": "uint256" + } + ] + }, + "primaryType": "PermitTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "250000000" + }, + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "nonce": "1997", + "deadline": "1775803746" + } + }, + "expected": { + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "a72b11929a9c4bff37f00eb63b3615519e00a8aa297140855c44cfff4da27146", + "digest": "ece4ce2c0d6c3f27171599e51e0b42287645bb3029f07ef9671ddca458007d5e" + } + }, + { + "id": "permit2-PermitBatchTransferFrom", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" }, { - "name": "endAmount", + "name": "chainId", "type": "uint256" }, { - "name": "recipient", + "name": "verifyingContract", "type": "address" } ], - "OrderInfo": [ + "PermitBatchTransferFrom": [ { - "name": "reactor", - "type": "address" + "name": "permitted", + "type": "TokenPermissions[]" }, { - "name": "swapper", + "name": "spender", "type": "address" }, { @@ -692,14 +826,6 @@ { "name": "deadline", "type": "uint256" - }, - { - "name": "additionalValidationContract", - "type": "address" - }, - { - "name": "additionalValidationData", - "type": "bytes" } ], "TokenPermissions": [ @@ -711,90 +837,55 @@ "name": "amount", "type": "uint256" } - ], - "V2DutchOrder": [ - { - "name": "info", - "type": "OrderInfo" - }, + ] + }, + "primaryType": "PermitBatchTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": [ { - "name": "cosigner", - "type": "address" + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "1000000" }, { - "name": "baseInputToken", - "type": "address" + "token": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "amount": "10000000" }, { - "name": "baseInputStartAmount", - "type": "uint256" + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "amount": "100000000" }, { - "name": "baseInputEndAmount", - "type": "uint256" + "token": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "amount": "1000000000" }, { - "name": "baseOutputs", - "type": "DutchOutput[]" + "token": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "amount": "10000000000" } - ] - }, - "primaryType": "PermitWitnessTransferFrom", - "domain": { - "name": "Permit2", - "chainId": 1, - "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" - }, - "message": { - "permitted": { - "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "amount": "3500000000" - }, - "spender": "0x00000011F84B9aa48e5f8aA8B9897600006289Be", - "nonce": "1993353164669688581970088190602701610056397261180510327052393484289", - "deadline": "1775803746", - "witness": { - "info": { - "reactor": "0x00000011F84B9aa48e5f8aA8B9897600006289Be", - "swapper": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", - "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", - "deadline": "1775803746", - "additionalValidationContract": "0x0000000000000000000000000000000000000000", - "additionalValidationData": "0x" - }, - "cosigner": "0x4449cd34d1eb1fedcf02a1be3834ffde8e6a6180", - "baseInputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "baseInputStartAmount": "3500000000", - "baseInputEndAmount": "3500000000", - "baseOutputs": [ - { - "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "startAmount": "1000000000000000000", - "endAmount": "990000000000000000", - "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" - }, - { - "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "startAmount": "1500000000000000", - "endAmount": "1485000000000000", - "recipient": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47" - } - ] - } + ], + "spender": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", + "nonce": "1998", + "deadline": "1775803746" } }, "expected": { "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", - "message_hash": "1d895c005457e7cdee8450d706c53a359a4176c12dd4d48f9e38339160809b83", - "digest": "7cc7576f193542ecfbed9254ff7785c3aa296d2382d317858aa13d87dd24d652" + "message_hash": "b0347a078a3a77dee64d7c255ba56e39221a3d6c5b2375d2e89d09b28533b5ab", + "digest": "31227578aa0209aad26446c56711f4f4ad009a6befab3034ce466ad45e295090" } }, { - "id": "uniswapx-PriorityOrder", + "id": "uniswapx-ExclusiveDutchOrder", "source": [ "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", - "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/PriorityOrderLib.sol", - "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol" + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/ExclusiveDutchOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/DutchOrderLib.sol" ], "doc": { "types": { @@ -831,91 +922,89 @@ }, { "name": "witness", - "type": "PriorityOrder" + "type": "ExclusiveDutchOrder" } ], - "OrderInfo": [ - { - "name": "reactor", - "type": "address" - }, + "DutchOutput": [ { - "name": "swapper", + "name": "token", "type": "address" }, { - "name": "nonce", + "name": "startAmount", "type": "uint256" }, { - "name": "deadline", + "name": "endAmount", "type": "uint256" }, { - "name": "additionalValidationContract", + "name": "recipient", "type": "address" - }, - { - "name": "additionalValidationData", - "type": "bytes" } ], - "PriorityInput": [ + "ExclusiveDutchOrder": [ { - "name": "token", - "type": "address" + "name": "info", + "type": "OrderInfo" }, { - "name": "amount", + "name": "decayStartTime", "type": "uint256" }, { - "name": "mpsPerPriorityFeeWei", + "name": "decayEndTime", "type": "uint256" - } - ], - "PriorityOrder": [ - { - "name": "info", - "type": "OrderInfo" }, { - "name": "cosigner", + "name": "exclusiveFiller", "type": "address" }, { - "name": "auctionStartBlock", + "name": "exclusivityOverrideBps", "type": "uint256" }, { - "name": "baselinePriorityFeeWei", + "name": "inputToken", + "type": "address" + }, + { + "name": "inputStartAmount", "type": "uint256" }, { - "name": "input", - "type": "PriorityInput" + "name": "inputEndAmount", + "type": "uint256" }, { "name": "outputs", - "type": "PriorityOutput[]" + "type": "DutchOutput[]" } ], - "PriorityOutput": [ + "OrderInfo": [ { - "name": "token", + "name": "reactor", "type": "address" }, { - "name": "amount", + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", "type": "uint256" }, { - "name": "mpsPerPriorityFeeWei", + "name": "deadline", "type": "uint256" }, { - "name": "recipient", + "name": "additionalValidationContract", "type": "address" + }, + { + "name": "additionalValidationData", + "type": "bytes" } ], "TokenPermissions": [ @@ -932,61 +1021,62 @@ "primaryType": "PermitWitnessTransferFrom", "domain": { "name": "Permit2", - "chainId": 8453, + "chainId": 1, "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" }, "message": { "permitted": { - "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", - "amount": "100000000" + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "3500000000" }, - "spender": "0x0000000000fbd5e2a41b6d0f3b0fe9ed9b2d0cc9", - "nonce": "1993353164669688581970088190602701610056397261180510327052393484290", + "spender": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", "deadline": "1775803746", "witness": { "info": { - "reactor": "0x0000000000fbd5e2a41b6d0f3b0fe9ed9b2d0cc9", + "reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4", "swapper": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", "deadline": "1775803746", "additionalValidationContract": "0x0000000000000000000000000000000000000000", "additionalValidationData": "0x" }, - "cosigner": "0x0000000000000000000000000000000000000000", - "auctionStartBlock": "24000000", - "baselinePriorityFeeWei": "0", - "input": { - "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", - "amount": "100000000", - "mpsPerPriorityFeeWei": "0" - }, + "decayStartTime": "1775803680", + "decayEndTime": "1775803740", + "exclusiveFiller": "0xff8ba4d1fc3762f6154cc942ccf30049a2a0cec6", + "exclusivityOverrideBps": "100", + "inputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "inputStartAmount": "3500000000", + "inputEndAmount": "3500000000", "outputs": [ { - "token": "0x4200000000000000000000000000000000000006", - "amount": "28000000000000000", - "mpsPerPriorityFeeWei": "1", + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "startAmount": "1000000000000000000", + "endAmount": "990000000000000000", "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" }, { - "token": "0x4200000000000000000000000000000000000006", - "amount": "42000000000000", - "mpsPerPriorityFeeWei": "0", - "recipient": "0x7FFC3DBF3B2b50Ff3A1D5523bc24Bb5043837B14" + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "startAmount": "1500000000000000", + "endAmount": "1485000000000000", + "recipient": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47" } ] } } }, "expected": { - "domain_separator": "3b6f35e4fce979ef8eac3bcdc8c3fc38fe7911bb0c69c8fe72bf1fd1a17e6f07", - "message_hash": "a641126490de075257de4fc0b0ade25f8fa4bfc12858d49bb388cdcf34fd0c78", - "digest": "c7b4e513709348a52a6b13dea66c18975713be646ec021d9d62fb632874c7eb5" + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "9d5278e60026d517ef5795ddfa37fdbb3c45bb9aa1428e1100420191c4d582bf", + "digest": "5def1fbddaf0631deb17c013e1778be8a134da1e4f4ce1ed070479f12e4cdc67" } }, { - "id": "seaport-OrderComponents", + "id": "uniswapx-V2DutchOrder", "source": [ - "https://github.com/ProjectOpenSea/seaport-core/blob/523097f9cee66c15d308c900c50f336b291cda08/src/lib/ConsiderationBase.sol" + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/V2DutchOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol" ], "doc": { "types": { @@ -995,10 +1085,6 @@ "name": "name", "type": "string" }, - { - "name": "version", - "type": "string" - }, { "name": "chainId", "type": "uint256" @@ -1008,123 +1094,1411 @@ "type": "address" } ], - "OrderComponents": [ + "PermitWitnessTransferFrom": [ { - "name": "offerer", + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", "type": "address" }, { - "name": "zone", + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "V2DutchOrder" + } + ], + "DutchOutput": [ + { + "name": "token", "type": "address" }, { - "name": "offer", - "type": "OfferItem[]" + "name": "startAmount", + "type": "uint256" }, { - "name": "consideration", - "type": "ConsiderationItem[]" + "name": "endAmount", + "type": "uint256" }, { - "name": "orderType", - "type": "uint8" + "name": "recipient", + "type": "address" + } + ], + "OrderInfo": [ + { + "name": "reactor", + "type": "address" }, { - "name": "startTime", + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", "type": "uint256" }, { - "name": "endTime", + "name": "deadline", "type": "uint256" }, { - "name": "zoneHash", - "type": "bytes32" + "name": "additionalValidationContract", + "type": "address" }, { - "name": "salt", + "name": "additionalValidationData", + "type": "bytes" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", "type": "uint256" + } + ], + "V2DutchOrder": [ + { + "name": "info", + "type": "OrderInfo" + }, + { + "name": "cosigner", + "type": "address" + }, + { + "name": "baseInputToken", + "type": "address" + }, + { + "name": "baseInputStartAmount", + "type": "uint256" + }, + { + "name": "baseInputEndAmount", + "type": "uint256" + }, + { + "name": "baseOutputs", + "type": "DutchOutput[]" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 1, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "amount": "3500000000" + }, + "spender": "0x00000011F84B9aa48e5f8aA8B9897600006289Be", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484289", + "deadline": "1775803746", + "witness": { + "info": { + "reactor": "0x00000011F84B9aa48e5f8aA8B9897600006289Be", + "swapper": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", + "deadline": "1775803746", + "additionalValidationContract": "0x0000000000000000000000000000000000000000", + "additionalValidationData": "0x" + }, + "cosigner": "0x4449cd34d1eb1fedcf02a1be3834ffde8e6a6180", + "baseInputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "baseInputStartAmount": "3500000000", + "baseInputEndAmount": "3500000000", + "baseOutputs": [ + { + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "startAmount": "1000000000000000000", + "endAmount": "990000000000000000", + "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" + }, + { + "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "startAmount": "1500000000000000", + "endAmount": "1485000000000000", + "recipient": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47" + } + ] + } + } + }, + "expected": { + "domain_separator": "866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28", + "message_hash": "1d895c005457e7cdee8450d706c53a359a4176c12dd4d48f9e38339160809b83", + "digest": "7cc7576f193542ecfbed9254ff7785c3aa296d2382d317858aa13d87dd24d652" + } + }, + { + "id": "uniswapx-PriorityOrder", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/PriorityOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitWitnessTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "PriorityOrder" + } + ], + "OrderInfo": [ + { + "name": "reactor", + "type": "address" + }, + { + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "additionalValidationContract", + "type": "address" + }, + { + "name": "additionalValidationData", + "type": "bytes" + } + ], + "PriorityInput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "mpsPerPriorityFeeWei", + "type": "uint256" + } + ], + "PriorityOrder": [ + { + "name": "info", + "type": "OrderInfo" + }, + { + "name": "cosigner", + "type": "address" + }, + { + "name": "auctionStartBlock", + "type": "uint256" + }, + { + "name": "baselinePriorityFeeWei", + "type": "uint256" + }, + { + "name": "input", + "type": "PriorityInput" + }, + { + "name": "outputs", + "type": "PriorityOutput[]" + } + ], + "PriorityOutput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "mpsPerPriorityFeeWei", + "type": "uint256" + }, + { + "name": "recipient", + "type": "address" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 8453, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "amount": "100000000" + }, + "spender": "0x0000000000fbd5e2a41b6d0f3b0fe9ed9b2d0cc9", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484290", + "deadline": "1775803746", + "witness": { + "info": { + "reactor": "0x0000000000fbd5e2a41b6d0f3b0fe9ed9b2d0cc9", + "swapper": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "nonce": "1993353164669688581970088190602701610056397261180510327052393484288", + "deadline": "1775803746", + "additionalValidationContract": "0x0000000000000000000000000000000000000000", + "additionalValidationData": "0x" + }, + "cosigner": "0x0000000000000000000000000000000000000000", + "auctionStartBlock": "24000000", + "baselinePriorityFeeWei": "0", + "input": { + "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "amount": "100000000", + "mpsPerPriorityFeeWei": "0" + }, + "outputs": [ + { + "token": "0x4200000000000000000000000000000000000006", + "amount": "28000000000000000", + "mpsPerPriorityFeeWei": "1", + "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" + }, + { + "token": "0x4200000000000000000000000000000000000006", + "amount": "42000000000000", + "mpsPerPriorityFeeWei": "0", + "recipient": "0x7FFC3DBF3B2b50Ff3A1D5523bc24Bb5043837B14" + } + ] + } + } + }, + "expected": { + "domain_separator": "3b6f35e4fce979ef8eac3bcdc8c3fc38fe7911bb0c69c8fe72bf1fd1a17e6f07", + "message_hash": "a641126490de075257de4fc0b0ade25f8fa4bfc12858d49bb388cdcf34fd0c78", + "digest": "c7b4e513709348a52a6b13dea66c18975713be646ec021d9d62fb632874c7eb5" + } + }, + { + "id": "seaport-OrderComponents", + "source": [ + "https://github.com/ProjectOpenSea/seaport-core/blob/523097f9cee66c15d308c900c50f336b291cda08/src/lib/ConsiderationBase.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "OrderComponents": [ + { + "name": "offerer", + "type": "address" + }, + { + "name": "zone", + "type": "address" + }, + { + "name": "offer", + "type": "OfferItem[]" + }, + { + "name": "consideration", + "type": "ConsiderationItem[]" + }, + { + "name": "orderType", + "type": "uint8" + }, + { + "name": "startTime", + "type": "uint256" + }, + { + "name": "endTime", + "type": "uint256" + }, + { + "name": "zoneHash", + "type": "bytes32" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "conduitKey", + "type": "bytes32" + }, + { + "name": "counter", + "type": "uint256" + } + ], + "ConsiderationItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + }, + { + "name": "recipient", + "type": "address" + } + ], + "OfferItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + } + ] + }, + "primaryType": "OrderComponents", + "domain": { + "name": "Seaport", + "version": "1.6", + "chainId": 1, + "verifyingContract": "0x0000000000000068F116a894984e2DB1123eB395" + }, + "message": { + "offerer": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "zone": "0x0000000000000000000000000000000000000000", + "offer": [ + { + "itemType": 2, + "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "identifierOrCriteria": "8520", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "identifierOrCriteria": "8521", + "startAmount": "1", + "endAmount": "1" + } + ], + "consideration": [ + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "19500000000000000000", + "endAmount": "19500000000000000000", + "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "500000000000000000", + "endAmount": "500000000000000000", + "recipient": "0x0000a26b00c1F0DF003000390027140000fAa719" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "1000000000000000000", + "endAmount": "1000000000000000000", + "recipient": "0xA858DDc0445d8131daC4d1DE01f834ffcbA52Ef1" + } + ], + "orderType": 0, + "startTime": "1775700000", + "endTime": "1778300000", + "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "salt": "24446860302761739304752683030156737591518664810215442929818227897836383814680", + "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", + "counter": "0" + } + }, + "expected": { + "domain_separator": "fce34bc6e1752c1117e5063116d25cad2fa2bdcf15ff2d2e275eece7dc31ba64", + "message_hash": "5bdedde642e259a1841ef18afaf47285be8da69b311260a27436eba16517c016", + "digest": "b6f6cd11f7cd0e8f8ed8964727b57e23d38680ad2d818d3a87b551e4762a1f69" + } + }, + { + "id": "seaport-OrderComponents-bundle-10x6", + "source": [ + "https://github.com/ProjectOpenSea/seaport-core/blob/523097f9cee66c15d308c900c50f336b291cda08/src/lib/ConsiderationBase.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "OrderComponents": [ + { + "name": "offerer", + "type": "address" + }, + { + "name": "zone", + "type": "address" + }, + { + "name": "offer", + "type": "OfferItem[]" + }, + { + "name": "consideration", + "type": "ConsiderationItem[]" + }, + { + "name": "orderType", + "type": "uint8" + }, + { + "name": "startTime", + "type": "uint256" + }, + { + "name": "endTime", + "type": "uint256" + }, + { + "name": "zoneHash", + "type": "bytes32" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "conduitKey", + "type": "bytes32" + }, + { + "name": "counter", + "type": "uint256" + } + ], + "ConsiderationItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + }, + { + "name": "recipient", + "type": "address" + } + ], + "OfferItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + } + ] + }, + "primaryType": "OrderComponents", + "domain": { + "name": "Seaport", + "version": "1.6", + "chainId": 1, + "verifyingContract": "0x0000000000000068F116a894984e2DB1123eB395" + }, + "message": { + "offerer": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "zone": "0x0000000000000000000000000000000000000000", + "offer": [ + { + "itemType": 2, + "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "identifierOrCriteria": "8520", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "identifierOrCriteria": "8521", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "identifierOrCriteria": "8522", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xED5AF388653567Af2F388E6224dC7C4b3241C544", + "identifierOrCriteria": "4100", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xED5AF388653567Af2F388E6224dC7C4b3241C544", + "identifierOrCriteria": "4101", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xED5AF388653567Af2F388E6224dC7C4b3241C544", + "identifierOrCriteria": "4102", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0x8a90CAb2b38dba80c64b7734e58Ee1dB38B8992e", + "identifierOrCriteria": "700", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0x8a90CAb2b38dba80c64b7734e58Ee1dB38B8992e", + "identifierOrCriteria": "701", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "3300", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "3301", + "startAmount": "1", + "endAmount": "1" + } + ], + "consideration": [ + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "118500000000000000000", + "endAmount": "118500000000000000000", + "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "625000000000000000", + "endAmount": "625000000000000000", + "recipient": "0x0000a26b00c1F0DF003000390027140000fAa719" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "2400000000000000000", + "endAmount": "2400000000000000000", + "recipient": "0x00000000000000000000000000000000a11cE000" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "1800000000000000000", + "endAmount": "1800000000000000000", + "recipient": "0x00000000000000000000000000000000A11ce001" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "1500000000000000000", + "endAmount": "1500000000000000000", + "recipient": "0x00000000000000000000000000000000a11CE002" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "1175000000000000000", + "endAmount": "1175000000000000000", + "recipient": "0x00000000000000000000000000000000A11ce003" + } + ], + "orderType": 0, + "startTime": "1775700000", + "endTime": "1778300000", + "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "salt": "24446860302761739304752683030156737591518664810215442929818227897836383814680", + "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", + "counter": "0" + } + }, + "expected": { + "domain_separator": "fce34bc6e1752c1117e5063116d25cad2fa2bdcf15ff2d2e275eece7dc31ba64", + "message_hash": "777e35ac429840a63b28d70ae83c7d0917f2c287fc0cf8fe659d2527510b93b1", + "digest": "af42126e4b5bfe74494b27766b259c698ba314a928d06ac1633b5ca1cf3e5e30" + } + }, + { + "id": "seaport-OrderComponents-consideration-12", + "source": [ + "https://github.com/ProjectOpenSea/seaport-core/blob/523097f9cee66c15d308c900c50f336b291cda08/src/lib/ConsiderationBase.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "OrderComponents": [ + { + "name": "offerer", + "type": "address" + }, + { + "name": "zone", + "type": "address" + }, + { + "name": "offer", + "type": "OfferItem[]" + }, + { + "name": "consideration", + "type": "ConsiderationItem[]" + }, + { + "name": "orderType", + "type": "uint8" + }, + { + "name": "startTime", + "type": "uint256" + }, + { + "name": "endTime", + "type": "uint256" + }, + { + "name": "zoneHash", + "type": "bytes32" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "conduitKey", + "type": "bytes32" + }, + { + "name": "counter", + "type": "uint256" + } + ], + "ConsiderationItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + }, + { + "name": "recipient", + "type": "address" + } + ], + "OfferItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + } + ] + }, + "primaryType": "OrderComponents", + "domain": { + "name": "Seaport", + "version": "1.6", + "chainId": 1, + "verifyingContract": "0x0000000000000068F116a894984e2DB1123eB395" + }, + "message": { + "offerer": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "zone": "0x0000000000000000000000000000000000000000", + "offer": [ + { + "itemType": 2, + "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", + "identifierOrCriteria": "9001", + "startAmount": "1", + "endAmount": "1" + } + ], + "consideration": [ + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "15000000000000000000", + "endAmount": "15000000000000000000", + "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "100000000000000000", + "endAmount": "100000000000000000", + "recipient": "0x0000a26b00c1F0DF003000390027140000fAa719" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "50000000000000000", + "endAmount": "50000000000000000", + "recipient": "0x0000000000000000000000000000000005B11700" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "100000000000000000", + "endAmount": "100000000000000000", + "recipient": "0x0000000000000000000000000000000005B11701" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "150000000000000000", + "endAmount": "150000000000000000", + "recipient": "0x0000000000000000000000000000000005b11702" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "200000000000000000", + "endAmount": "200000000000000000", + "recipient": "0x0000000000000000000000000000000005B11703" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "250000000000000000", + "endAmount": "250000000000000000", + "recipient": "0x0000000000000000000000000000000005b11704" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "300000000000000000", + "endAmount": "300000000000000000", + "recipient": "0x0000000000000000000000000000000005b11705" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "350000000000000000", + "endAmount": "350000000000000000", + "recipient": "0x0000000000000000000000000000000005B11706" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "400000000000000000", + "endAmount": "400000000000000000", + "recipient": "0x0000000000000000000000000000000005b11707" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "450000000000000000", + "endAmount": "450000000000000000", + "recipient": "0x0000000000000000000000000000000005b11708" + }, + { + "itemType": 0, + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "500000000000000000", + "endAmount": "500000000000000000", + "recipient": "0x0000000000000000000000000000000005b11709" + } + ], + "orderType": 0, + "startTime": "1775700000", + "endTime": "1778300000", + "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "salt": "24446860302761739304752683030156737591518664810215442929818227897836383814680", + "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", + "counter": "0" + } + }, + "expected": { + "domain_separator": "fce34bc6e1752c1117e5063116d25cad2fa2bdcf15ff2d2e275eece7dc31ba64", + "message_hash": "66e666071d1faf7d9f74130449bf619b95c9f52fd04eb7a435903319e8afaa20", + "digest": "bfb6130599bda35f4a32de5ec2c1f65d5024bae151657ad3445fd0042fb0f844" + } + }, + { + "id": "seaport-OrderComponents-bundle-32", + "source": [ + "https://github.com/ProjectOpenSea/seaport-core/blob/523097f9cee66c15d308c900c50f336b291cda08/src/lib/ConsiderationBase.sol" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "OrderComponents": [ + { + "name": "offerer", + "type": "address" + }, + { + "name": "zone", + "type": "address" + }, + { + "name": "offer", + "type": "OfferItem[]" + }, + { + "name": "consideration", + "type": "ConsiderationItem[]" + }, + { + "name": "orderType", + "type": "uint8" + }, + { + "name": "startTime", + "type": "uint256" + }, + { + "name": "endTime", + "type": "uint256" + }, + { + "name": "zoneHash", + "type": "bytes32" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "conduitKey", + "type": "bytes32" + }, + { + "name": "counter", + "type": "uint256" + } + ], + "ConsiderationItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + }, + { + "name": "recipient", + "type": "address" + } + ], + "OfferItem": [ + { + "name": "itemType", + "type": "uint8" + }, + { + "name": "token", + "type": "address" + }, + { + "name": "identifierOrCriteria", + "type": "uint256" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "endAmount", + "type": "uint256" + } + ] + }, + "primaryType": "OrderComponents", + "domain": { + "name": "Seaport", + "version": "1.6", + "chainId": 1, + "verifyingContract": "0x0000000000000068F116a894984e2DB1123eB395" + }, + "message": { + "offerer": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "zone": "0x0000000000000000000000000000000000000000", + "offer": [ + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5000", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5001", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5002", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5003", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5004", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5005", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5006", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5007", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5008", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5009", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5010", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5011", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5012", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5013", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5014", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5015", + "startAmount": "1", + "endAmount": "1" + }, + { + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5016", + "startAmount": "1", + "endAmount": "1" }, { - "name": "conduitKey", - "type": "bytes32" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5017", + "startAmount": "1", + "endAmount": "1" }, { - "name": "counter", - "type": "uint256" - } - ], - "ConsiderationItem": [ + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5018", + "startAmount": "1", + "endAmount": "1" + }, { - "name": "itemType", - "type": "uint8" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5019", + "startAmount": "1", + "endAmount": "1" }, { - "name": "token", - "type": "address" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5020", + "startAmount": "1", + "endAmount": "1" }, { - "name": "identifierOrCriteria", - "type": "uint256" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5021", + "startAmount": "1", + "endAmount": "1" }, { - "name": "startAmount", - "type": "uint256" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5022", + "startAmount": "1", + "endAmount": "1" }, { - "name": "endAmount", - "type": "uint256" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5023", + "startAmount": "1", + "endAmount": "1" }, { - "name": "recipient", - "type": "address" - } - ], - "OfferItem": [ + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5024", + "startAmount": "1", + "endAmount": "1" + }, { - "name": "itemType", - "type": "uint8" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5025", + "startAmount": "1", + "endAmount": "1" }, { - "name": "token", - "type": "address" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5026", + "startAmount": "1", + "endAmount": "1" }, { - "name": "identifierOrCriteria", - "type": "uint256" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5027", + "startAmount": "1", + "endAmount": "1" }, { - "name": "startAmount", - "type": "uint256" + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5028", + "startAmount": "1", + "endAmount": "1" }, { - "name": "endAmount", - "type": "uint256" - } - ] - }, - "primaryType": "OrderComponents", - "domain": { - "name": "Seaport", - "version": "1.6", - "chainId": 1, - "verifyingContract": "0x0000000000000068F116a894984e2DB1123eB395" - }, - "message": { - "offerer": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", - "zone": "0x0000000000000000000000000000000000000000", - "offer": [ + "itemType": 2, + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5029", + "startAmount": "1", + "endAmount": "1" + }, { "itemType": 2, - "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", - "identifierOrCriteria": "8520", + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5030", "startAmount": "1", "endAmount": "1" }, { "itemType": 2, - "token": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", - "identifierOrCriteria": "8521", + "token": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8", + "identifierOrCriteria": "5031", "startAmount": "1", "endAmount": "1" } @@ -1134,25 +2508,17 @@ "itemType": 0, "token": "0x0000000000000000000000000000000000000000", "identifierOrCriteria": "0", - "startAmount": "19500000000000000000", - "endAmount": "19500000000000000000", + "startAmount": "320000000000000000000", + "endAmount": "320000000000000000000", "recipient": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94" }, { "itemType": 0, "token": "0x0000000000000000000000000000000000000000", "identifierOrCriteria": "0", - "startAmount": "500000000000000000", - "endAmount": "500000000000000000", + "startAmount": "1600000000000000000", + "endAmount": "1600000000000000000", "recipient": "0x0000a26b00c1F0DF003000390027140000fAa719" - }, - { - "itemType": 0, - "token": "0x0000000000000000000000000000000000000000", - "identifierOrCriteria": "0", - "startAmount": "1000000000000000000", - "endAmount": "1000000000000000000", - "recipient": "0xA858DDc0445d8131daC4d1DE01f834ffcbA52Ef1" } ], "orderType": 0, @@ -1166,8 +2532,8 @@ }, "expected": { "domain_separator": "fce34bc6e1752c1117e5063116d25cad2fa2bdcf15ff2d2e275eece7dc31ba64", - "message_hash": "5bdedde642e259a1841ef18afaf47285be8da69b311260a27436eba16517c016", - "digest": "b6f6cd11f7cd0e8f8ed8964727b57e23d38680ad2d818d3a87b551e4762a1f69" + "message_hash": "b3b1ba0d3e233fb87c9d866c87b2ef5b77e85b80d64f1ba9b2e3d9dc98a14402", + "digest": "8f9f180ebb10f7799149df0dbdcf11edc50f7f3cce4e5b2c742a142f286c5ee0" } }, { @@ -2085,6 +3451,93 @@ "digest": "c6d5a71fd45369207a574d910998c708553d0a8960cbc8ea4515f04891f78878" } }, + { + "id": "safe-SafeTx-approve-unlimited", + "source": [ + "https://github.com/safe-global/safe-smart-account/blob/v1.4.1/contracts/Safe.sol", + "https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/GnosisSafe.sol" + ], + "requires_capability": "erc20-unlimited-approve-review", + "unlimited_screen": "Allow 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 to spend ALL your USDC", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "SafeTx": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "operation", + "type": "uint8" + }, + { + "name": "safeTxGas", + "type": "uint256" + }, + { + "name": "baseGas", + "type": "uint256" + }, + { + "name": "gasPrice", + "type": "uint256" + }, + { + "name": "gasToken", + "type": "address" + }, + { + "name": "refundReceiver", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + } + ] + }, + "primaryType": "SafeTx", + "domain": { + "chainId": 1, + "verifyingContract": "0x4F2083f5fBede34C2714aFfb3105539775f7FE64" + }, + "message": { + "to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "value": "0", + "data": "0x095ea7b300000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "operation": 0, + "safeTxGas": "0", + "baseGas": "0", + "gasPrice": "0", + "gasToken": "0x0000000000000000000000000000000000000000", + "refundReceiver": "0x0000000000000000000000000000000000000000", + "nonce": "43" + } + }, + "expected": { + "domain_separator": "facf7a68664ed3dabebe8e263b4cf03d3e1ba8bbda764c9697c80c7e38b41c62", + "message_hash": "52f96cddec6b14f4496fe53708bb359ef7fcc6f1ccedf63177fd6237b5054b4b", + "digest": "efbe9929ce3de6463119639b299b403e582c1113a2d2a9434b4ba914a1dcffb3" + } + }, { "id": "safe-SafeMessage", "source": [ @@ -3581,6 +5034,80 @@ "digest": "2ebf83b128be8aa355a81a15424a3fc8966f27c7c2bceadf191be84304a16760" } }, + { + "id": "snapshot-Vote-reason-multiline", + "source": [ + "https://github.com/snapshot-labs/snapshot.js/blob/ae2a8209e03ca1fa133a9105949a9386f3b539ed/src/sign/types.ts" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Vote": [ + { + "name": "from", + "type": "string" + }, + { + "name": "space", + "type": "string" + }, + { + "name": "timestamp", + "type": "uint64" + }, + { + "name": "proposal", + "type": "string" + }, + { + "name": "choice", + "type": "uint32" + }, + { + "name": "reason", + "type": "string" + }, + { + "name": "app", + "type": "string" + }, + { + "name": "metadata", + "type": "string" + } + ] + }, + "primaryType": "Vote", + "domain": { + "name": "snapshot", + "version": "0.1.4" + }, + "message": { + "from": "0x9858EfFD232B4033E47d90003D41EC34EcaEda94", + "space": "aavedao.eth", + "timestamp": 1775803746, + "proposal": "0x9e0b3d8a0b6b31c5c1b8c0b4e2c6b0bfa4f3dd1b0ec0e4b2d8a6b5c2e1f3a4d5", + "choice": 1, + "reason": "Supporting this.\n\n\tThe treasury numbers check out;\r\nsee the forum thread.", + "app": "snapshot-v2", + "metadata": "{}" + } + }, + "expected": { + "domain_separator": "484fce18f892e8535a4b6700e197a8026f4213f809d23ae117da03b497e18670", + "message_hash": "8b3db279e4027f639c6e9e69aeaa2760e4cbd76172e0508eb9d55504b0203dd5", + "digest": "fb036054282ff5c208db428da469c2aad81334ee2ea75713a848adefd932f8ae" + } + }, { "id": "snapshot-Vote-uint32[]", "source": [ diff --git a/tests/test_msg_eip712_corpus.py b/tests/test_msg_eip712_corpus.py index c9b24447..26b3cb61 100644 --- a/tests/test_msg_eip712_corpus.py +++ b/tests/test_msg_eip712_corpus.py @@ -8,7 +8,7 @@ # firmware repository, whose "expected" hashes were computed by a reference # encoder and cross-checked against @metamask/eth-sig-util and ethers. # -# Every document must be ACCEPTED: its hashes must equal both the embedded +# Each document is its own test (test_doc_). Every one must be ACCEPTED: its hashes must equal both the embedded # values and this suite's own independent encoder, its signature must # recover to the device's address, and the final screen must name the whole # primary type. A document with "requires_capability" (the max approvals) @@ -17,6 +17,7 @@ import json import os +import re import unittest import common @@ -64,45 +65,60 @@ def setUp(self): self.client.apply_policy('AdvancedMode', 0) self.client.reset_screenshots() - def test_every_real_world_document_signs(self): + def _sign_and_check(self, entry): + needs = entry.get('requires_capability') + if needs and needs not in (self.firmware_capabilities() or ()): + self.skipTest("Staged release tree does not yet provide " + "capability: " + needs) address = self.client.ethereum_get_address(streaming.PATH) - for entry in load_corpus(): - with self.subTest(entry['id']): - needs = entry.get('requires_capability') - if needs and needs not in (self.firmware_capabilities() or ()): - self.skipTest("Staged release tree does not yet provide " - "capability: " + needs) - doc = entry['doc'] - resp = self._walk(doc, max_steps=2000) - self.assertIsInstance(resp, eth.EthereumTypedDataSignature, - getattr(resp, 'message', resp)) - self.assertEqual(resp.domain_separator_hash.hex(), - entry['expected']['domain_separator']) - self.assertEqual(resp.message_hash.hex(), - entry['expected']['message_hash']) - digest = bytes.fromhex(entry['expected']['digest']) - self.assertEqual( - streaming.recover_signer(resp.signature, digest).hex(), - address.hex()) - # The final screen (and its continuation pages) names the - # whole primary type, however long. - codes = [code for code, _ in self.frames] - self.assertEqual(codes.count(types.ButtonRequest_SignTx), 1) - final = codes.index(types.ButtonRequest_SignTx) - self.assertIsNotNone(oled_text.find_text( - [layout for _, layout in self.frames[final:]], - 'Sign ' + doc['primaryType'])) - warned = [layout for _, layout in self.frames - if oled_text.find_line(layout, 'UNLIMITED APPROVAL', - oled_text.TITLE_FONT) - is not None] - if 'unlimited_screen' not in entry: - self.assertEqual(warned, []) - elif not self.firmware_at_least("7.16.0"): - self.assertEqual(len(warned), 1) - self.assertTrue(oled_text.shows( - warned[0], entry['unlimited_screen'])) + doc = entry['doc'] + resp = self._walk(doc, max_steps=2000) + self.assertIsInstance(resp, eth.EthereumTypedDataSignature, + getattr(resp, 'message', resp)) + self.assertEqual(resp.domain_separator_hash.hex(), + entry['expected']['domain_separator']) + self.assertEqual(resp.message_hash.hex(), + entry['expected']['message_hash']) + digest = bytes.fromhex(entry['expected']['digest']) + self.assertEqual( + streaming.recover_signer(resp.signature, digest).hex(), + address.hex()) + # The final screen (and its continuation pages) names the whole + # primary type, however long. + codes = [code for code, _ in self.frames] + self.assertEqual(codes.count(types.ButtonRequest_SignTx), 1) + final = codes.index(types.ButtonRequest_SignTx) + self.assertIsNotNone(oled_text.find_text( + [layout for _, layout in self.frames[final:]], + 'Sign ' + doc['primaryType'])) + warned = [layout for _, layout in self.frames + if oled_text.find_line(layout, 'UNLIMITED APPROVAL', + oled_text.TITLE_FONT) + is not None] + if 'unlimited_screen' not in entry: + self.assertEqual(warned, []) + elif not self.firmware_at_least("7.16.0"): + self.assertEqual(len(warned), 1) + self.assertTrue(oled_text.shows( + warned[0], entry['unlimited_screen'])) + + +# One test per document, so each gets its own timeout and failure line: +# test_doc_seaport_OrderComponents_bundle_10x6, ... +def _add_document_tests(): + for entry in load_corpus(): + spelled = entry['id'].replace('[]', '_array') + name = 'test_doc_' + re.sub(r'[^0-9A-Za-z]+', '_', spelled).strip('_') + assert not hasattr(TestMsgEip712Corpus, name), name + + def test(self, entry=entry): + self._sign_and_check(entry) + test.__name__ = name + test.__doc__ = entry['id'] + ' signs with the expected hashes.' + setattr(TestMsgEip712Corpus, name, test) + +_add_document_tests() if __name__ == '__main__': unittest.main() From 586c6267da358a51ade92d2dc24e161da11b1665 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 05:30:02 -0500 Subject: [PATCH 15/37] test(erc7730): an embedded unlimited approve signs after a warning An execTransaction that embeds approve(spender, 2^256-1) shows the UNLIMITED approval screen, with the full spender, before the inner review or the blind screen. Declining it signs nothing, a finite approve shows no warning, and a dirty spender word is refused. --- tests/test_msg_ethereum_erc7730_runtime.py | 92 ++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/tests/test_msg_ethereum_erc7730_runtime.py b/tests/test_msg_ethereum_erc7730_runtime.py index 5995b0a5..a7687fa6 100644 --- a/tests/test_msg_ethereum_erc7730_runtime.py +++ b/tests/test_msg_ethereum_erc7730_runtime.py @@ -665,6 +665,98 @@ def test_inner_call_is_clear_signed_with_its_own_definition(self): ]) self.assertIn("Inner signer", [s[0] for s in self.screens]) + APPROVE = "approve(address spender,uint256 amount)" + MAX = (1 << 256) - 1 + + def _exec_approve(self, amount, spender=None, with_definition=True): + """execTransaction carrying approve(OTHER_ADDRESS, amount) on USDC.""" + self._exec_setup() + inner = erc7730_compiler.compile_calldata( + {"display": {"formats": {self.APPROVE: { + "intent": "Approve", "fields": [ + {"path": "spender", "label": "Spender", + "format": "addressName"}, + {"path": "amount", "label": "Amount", "format": "raw"}]}}}}, + self.APPROVE, 1, self.USDC) + inner_def = erc7730.Definition(self._envelope(inner), 1, 1, self.USDC, + inner[38:42]) + outer_def = self._exec_outer[1] + call = (bytes.fromhex("095ea7b3") + + (spender or self._word(OTHER_ADDRESS)) + self._word(amount)) + arguments = (self._word(self.USDC) + self._word(0) + + self._word(128) + self._word(0) + + self._word(len(call)) + call + bytes(-len(call) % 32)) + catalog = erc7730.Catalog( + (outer_def, inner_def) if with_definition else (outer_def,)) + return arguments, catalog + + UNLIMITED = ("UNLIMITED approval", + "Allow 0x" + OTHER_ADDRESS.hex() + " to spend ALL your USDC") + + def _approve_screens(self): + return [s for s in self._first_pages() + if s[0] in ("UNLIMITED approval", "Contract action", + "Signer field", "Inner signer", "Inner action", + "Blind signature")] + + def test_inner_unlimited_approve_warns_before_the_inner_review(self): + self.requires_release_capability("erc20-unlimited-approve-review") + arguments, catalog = self._exec_approve(self.MAX) + self._exec_certified(arguments, catalog) + shown = self._approve_screens() + self.assertEqual(shown[:2], [ + ("Contract action", "sign multisig operation"), self.UNLIMITED]) + self.assertEqual(shown[2][0], "Signer field") + self.assertEqual(shown[3][0], "Inner signer") + self.assertEqual([s[0] for s in self._first_pages()].count( + "UNLIMITED approval"), 1) + + def test_declining_an_inner_unlimited_approve_signs_nothing(self): + self.requires_release_capability("erc20-unlimited-approve-review") + arguments, catalog = self._exec_approve(self.MAX) + outer, outer_def = self._exec_outer + + def preload(): + erc7730.preload(self.client, outer_def) + self._drop_setup_screenshots() + return b"" + self._declined(outer, arguments, "UNLIMITED approval", + preload=preload, catalog=catalog) + self.assertNotIn("Inner signer", [s[0] for s in self.screens]) + + def test_inner_finite_approve_shows_no_unlimited_warning(self): + arguments, catalog = self._exec_approve(self.MAX - 1) + self._exec_certified(arguments, catalog) + titles = [s[0] for s in self.screens] + self.assertNotIn("UNLIMITED approval", titles) + self.assertIn("Inner action", titles) + + def test_blind_inner_unlimited_approve_still_warns_in_715(self): + self.requires_firmware_below("7.16.0") # 7.16 refuses blind calls + self.requires_release_capability("erc20-unlimited-approve-review") + arguments, catalog = self._exec_approve(self.MAX, + with_definition=False) + self._exec_certified(arguments, catalog) + shown = self._approve_screens() + self.assertEqual(shown[:3], [ + ("Contract action", "sign multisig operation"), self.UNLIMITED, + ("Blind signature", "The inner call is not clear-signed")]) + + def test_inner_approve_with_a_dirty_spender_word_is_refused(self): + self.requires_release_capability("erc20-unlimited-approve-review") + arguments, catalog = self._exec_approve( + self.MAX, spender=b"\x01" + bytes(11) + OTHER_ADDRESS) + outer, outer_def = self._exec_outer + erc7730.preload(self.client, outer_def) + self._drop_setup_screenshots() + result, _, _, _ = self._walk( + self._audit_start(outer, 4 + len(arguments)), b"", + arguments=arguments, catalog=catalog) + assert_failure(self, result, types.Failure_SyntaxError, + "Invalid ERC-7730 embedded call") + self.assertNotIn("UNLIMITED approval", [s[0] for s in self.screens]) + self.assertNotIn(types.ButtonRequest_SignTx, self.button_codes) + def test_inner_call_without_a_definition_is_blind_in_715(self): self.requires_firmware_below("7.16.0") # 7.16 refuses instead start, arguments, outer_def, _ = self._exec_setup() From c4e0727b790a29c6c9bb9e446c5ecf913ba3eee3 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 05:30:57 -0500 Subject: [PATCH 16/37] test(zcash): a real NU6.3 transaction with a fabricated output signs From NU6.3 every Orchard spend is paired with a zero-valued output whose ciphertext is random bytes (ZIP 326). This bundle is built by the orchard 0.16.0 crate for the "all" seed, so its rk values are the device's own. The device must sign both wallet spends, show only the change and the fee, and show no 0 ZEC output. --- tests/test_msg_zcash_sign_pczt_device.py | 136 +++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/tests/test_msg_zcash_sign_pczt_device.py b/tests/test_msg_zcash_sign_pczt_device.py index 3e2daf61..075775cb 100644 --- a/tests/test_msg_zcash_sign_pczt_device.py +++ b/tests/test_msg_zcash_sign_pczt_device.py @@ -105,6 +105,112 @@ FLAGS = 3 +# --- a real NU6.3 Orchard bundle ------------------------------------------- +# Built by the orchard 0.16.0 crate (Builder with BundleVersion::orchard_v3() +# and Flags::CROSS_ADDRESS_DISABLED, then build_for_pczt) for the "all" seed, +# account 0: a 100000-zat spend and a 90000-zat change output, fee 10000. +# With cross-address transfers disabled the builder pairs the spend with a +# fabricated zero-valued output to the spent note's own receiver whose +# enc_ciphertext is random bytes (ZIP 326), and the change with a fabricated +# zero-valued wallet spend. Both spends are the wallet's, so rk is this seed's +# ak re-randomized by alpha and the device signs both actions. +FABRICATED_BUNDLE = [ + { + 'nullifier': bytes.fromhex('aed05ebddc392dda842311207fbc3fa84fb454f43fcfac292d87a3e8300ee112'), + 'rk': bytes.fromhex('2c1b8779ac1aca3abec73dfe9b8635be2fae970dfe76237495042fed96c3849f'), + 'alpha': bytes.fromhex('f081a5920a807a9189bc48f6ef5916b48effd77745c905def34316c2af87290c'), + 'cv_net': bytes.fromhex('e80cb524dfe437bebaaeffec6c4543a7f64f48fa4bfa03e1847641b184c3da1d'), + 'cmx': bytes.fromhex('6685ca96b57a11cae02c5c562b3f999713d06fadc697444e4c34f022a1df752a'), + 'epk': bytes.fromhex('e56229ea7072ff2dac9e9d0d3545bc13511e55b849089d3244e3d6667466872c'), + 'enc': bytes.fromhex( + '8807db653fc5d70f3382f580c2283ed388a87134aec35d3f1108efea6df55ef0' + 'b61eaa33b79f8d53b6a3fb791c83b75876186f6e404ca22e3246bd3d6ee956f6' + '983d2f7efb6cbb27571832127afd4f48136bdab4ebc68bc2a7c667a4ab231030' + '19ecd43212a1b6e47307949de23f82998fb33d3f3d15abefaa8052026b17d172' + '1a508d8af11baa74df8f2f67c3be1a92bea530039b4920a0efedc8ee934485b2' + 'aecd88b5af781c8614d968236504d40d58d64875a808aae0995bb14577c89a26' + '564f7be2d8a8e871ca8caaeb04c7af0023b25d6930745b0fce3d1b01575d68b9' + '8965500c6de13f84b14052e24cca3bbe8aa0570ca4196b18a4b2cada1ddd039c' + '4d6038c809426d1eed7674efb6990b040035cc1b92952dae4d642f9c4c613572' + '59dc961c5f208a14387b7d8f9b4f5e502a09aa1d292d33e72b489bb5e5cf4ede' + '7e239d96d07139f9270ec5a3305da1013f0a5b6de6860af11a212c77fe5c1cb9' + '73888cbc1100e0e07423e879e40365844c2ca9a75b16ed4d032127470ca0e0bd' + '5de907a32b3aea36512929f5b83863bc1d63b40fce26dc4d58472ec15f2fadd2' + '24c16985bd5ab01e388502dd22596fe85012a9f74e2ea6c9b9aca2b7bc2298ed' + '779c61285753e6ba92f0a1a963f33750c105122c7e5ec92d95e2f454e0556682' + '25ebf956231f22a139c3c31fdb7e4a4a214d296765353e0e45269cef2eebdbf7' + 'e53e3743c4266eb705d30ea2cb567e370eda979b44f33ee2f6233dc8e1f5e844' + 'ff66891d37165675e63b2a10722381a55a92b006dd793ca832010a002d3021ab' + '5e526af9'), + 'out': bytes.fromhex( + 'a5fe4a96ac820c57db11db73741c6fee879c90635e16c5fd60967be70c58f27d' + '85e4a135ac87ecc858d88b34df846b2af6035683c138b97876c373328076e8ce' + 'f61c5989e4fee41ed2334b932f947f82'), + 'recipient': bytes.fromhex( + 'da973031634a8938ad1c480f978780693ec7709ba5caf58d8a7eb945586cbed6' + '45520f17387437bcfdc216'), + 'rseed': bytes.fromhex('c29d5ab216448f6ebf6388a6251160e7c9fe2413c85ac1cd2340363807ba8840'), + 'value': 0, + }, + { + 'nullifier': bytes.fromhex('31aafc80063db2af87ffdee21267ccd1f8a6eda982f7c5816b8f1d99ee580217'), + 'rk': bytes.fromhex('defbdcf42d021aa013d14a391b3cefa8ce94ac9cac96481bfddecab3f2065e06'), + 'alpha': bytes.fromhex('a16c066adf9a10e1696c4fa884a67f93b24d858e8cf06743304ad6cae69a1b06'), + 'cv_net': bytes.fromhex('fd260057cb02e71c9d37554c7fb496b48d5412c1c2707fcf0c8601946dd04c12'), + 'cmx': bytes.fromhex('e35568cf21a2befafa3d8bbd2bf6a19bcdbfe6b6234c303004ba38ba76afed3f'), + 'epk': bytes.fromhex('9b9efea983df2470cfc8d28bcf8c5bfd4f714e6bc08b0a15ab3611b0c7b56101'), + 'enc': bytes.fromhex( + 'b82f1b13e501868055a3f88433481172ee1173402227637b46be3e63cd3fa939' + '93af3074450b558a4d6ff8ee65bdb04962077060b5c258b3d0f7bc21ec7df69a' + 'a0898c47b6d913186a13fa21c420f0da48e72d167bc3e08bffa1f16d99a11aad' + '65cf98cc0a0ce37ec679c03dc7c6f1fe9feb619b4eca0c5823f6d413c7a5be12' + '2aee48f070770d163fde8ad4862df692d4433afe053e6b9e1a50e6cc8cbd37aa' + '54ce3e014e3dd342496855226cd65896442300161e242a8c4d7b2c19c83df04f' + 'f3ab7c7afcb9fb486d28bda503260b758ac473b618cce25b9d0daf8bafbe64ee' + '3d52f804c33ee78e25fe5bd6a105d4a1cde143e2d047f0fee8e9cb3645964688' + '2eb5ade60a1ff79284b81ee817d638ad5e1da8052266171b320a8bf4ec337df3' + 'ebcf087de768b46404f45a23208c8358c644e626152d67cf4e2efa94ff9ac2dd' + 'b8e3c670360523c238f1cc3b961dedade8e90de90927841745c5398f140ac053' + '8d48561bb372a367933147d239c500c238cf294d05e93c473378f59708f5aefb' + '375d23df2d0fc1a8f0de90c8fdb6cbb0036eef778c1bdca2731c7fe23a0331ee' + '4ceb357a52ce552ca0626891f5362c1983fb2656f91b372f0559de8943a9e555' + '30bc009ede7c222f8748ade9762270f937725a25401ad5a4fdc01a3f1f100289' + 'c5a291b9bfc3bc696e0af71e7199f6555b53ed875aa7ebdfeba1cd3f8c711190' + 'c6d5c64059483983834faf47283ca15682c4966070425d0f305cad882901d70f' + 'd222a8bfeb2077541449b73ce56a8d08aa623c06aa8b38e675eb17fd162b862d' + '5f777396'), + 'out': bytes.fromhex( + '28c960f201269936a006ae1cc2cd358b29f981dc52d10448a3c02479855bf89c' + 'ff2a654493f29035ff20daf3ce97d13d514bca41d0d298d0c40834248d1f617f' + '3db9516428eeca01be53af1da897071e'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('c53e0dc6ce5562fbf35fd57698514207df3cb42161093b070ba16bffcb93fdd9'), + 'value': 90000, + }, +] + + +def fabricated_action(v): + return { + 'alpha': v['alpha'], + 'nullifier': v['nullifier'], + 'cmx': v['cmx'], + 'epk': v['epk'], + 'enc_compact': v['enc'][:52], + 'enc_memo': v['enc'][52:564], + 'enc_noncompact': v['enc'][564:], + 'cv_net': v['cv_net'], + 'rk': v['rk'], + 'out_ciphertext': v['out'], + 'is_spend': True, + 'value': v['value'], + 'recipient': v['recipient'], + 'rseed': v['rseed'], + } + + def _b2b(person, data): return hashlib.blake2b(data, digest_size=32, person=person).digest() @@ -503,5 +609,35 @@ def test_absent_user_address_shows_the_orchard_address(self): self.assertEqual(shown, [('Orchard address', EXPECTED_UA)]) + FABRICATED_OUTPUT_FIRMWARE = "7.15.0" + + def test_fabricated_zero_value_output_signs(self): + """A real NU6.3 wallet transaction is not refused. + + From NU6.3 every Orchard spend is paired with a fabricated zero-valued + output whose ciphertext is random bytes (ZIP 326), so it cannot decrypt + to its note. Its cmx still binds value 0, so it pays no one: the device + accepts it without a screen, shows only the change output and the fee, + and signs both wallet spends. + """ + self.requires_firmware(self.FABRICATED_OUTPUT_FIRMWARE) + actions = [fabricated_action(v) for v in FABRICATED_BUNDLE] + kwargs = sign_kwargs(actions, fee=10000, orchard_value_balance=10000, + total_amount=90000) + kwargs['orchard_digest'] = bundle_digest(actions, False, 5, + value_balance=10000) + screens = self._capture_confirm_text() + + result = self.client.zcash_sign_pczt(**kwargs) + + self.assertIsInstance(result, zcash_proto.ZcashSignedPCZT) + self.assertEqual(len(result.signatures), 2) + outputs = [body for code, _, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput] + self.assertEqual(len(outputs), 2) # the change's amount and address + self.assertIn('0.00090000 ZEC', outputs[0]) + for _, _, body in screens: + self.assertNotIn('0.00000000 ZEC', body) + if __name__ == '__main__': unittest.main() From b56cbc13aa6c98539d01098f7a62e51186539f69 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 05:56:08 -0500 Subject: [PATCH 17/37] test(zcash): a zero-value padding output is not shown Wallets pad an Orchard bundle with a dummy zero-valued output to a random address whose ciphertext decrypts. The device showed it as a 0 ZEC send. Like Keystone and Ledger it now shows only the change and the fee. The bundle is a real NU6.2 one built by the orchard 0.16.0 crate. --- tests/test_msg_zcash_sign_pczt_device.py | 111 +++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/tests/test_msg_zcash_sign_pczt_device.py b/tests/test_msg_zcash_sign_pczt_device.py index 075775cb..6bd8ff7b 100644 --- a/tests/test_msg_zcash_sign_pczt_device.py +++ b/tests/test_msg_zcash_sign_pczt_device.py @@ -192,6 +192,88 @@ ] +# The same spend and change as an NU6.2 bundle (orchard 0.16.0, +# BundleVersion::orchard_v2(), Flags::ENABLED), as wallets build today. The +# builder pads it with a dummy spend (dummy_sk, host-signed) and a dummy +# zero-valued output to a random address whose ciphertext really decrypts. +PADDING_BUNDLE = [ + { + 'nullifier': bytes.fromhex('aed05ebddc392dda842311207fbc3fa84fb454f43fcfac292d87a3e8300ee112'), + 'rk': bytes.fromhex('2c1b8779ac1aca3abec73dfe9b8635be2fae970dfe76237495042fed96c3849f'), + 'alpha': bytes.fromhex('f081a5920a807a9189bc48f6ef5916b48effd77745c905def34316c2af87290c'), + 'cv_net': bytes.fromhex('ba35dd670b2f19d39a44f2fb871e35ec8b9a444d49bf84a8e9bf6c5556e69d0c'), + 'cmx': bytes.fromhex('087796ff6b2f7943ea69464410a8b2710c2b90316fd5c748f41e3a4793b59d0a'), + 'epk': bytes.fromhex('81f9bbeaf78f326630fd4526ab67289a44d0d09972ef30682d4f5ffeb9417798'), + 'enc': bytes.fromhex( + '7635af9d8406383ee8dd92f37ef87a8216b57a8004ea779251263d8665916e2c' + 'ad6139ad2266484d70f79806e431c7531de4c5d620b14b585070871e1c50aeca' + 'dda4ae9a45216132c33e3ec304c1edaa29d1a18687e546b95139bce8f57de0f1' + '1e200bad146f9580434d6471084df7b0cb8b54f09098923e821939c6769f33f4' + '1add1d82c3660dcf065c8bf43e3f69925270ef175fa2f595166dbeeb00d2eed8' + 'd19280580081a541ff0db45894f0af53c82f85c4d83e139a98dd99b5544ab23a' + '0ee74c33a416bc4ab28d28d5717463f9e100f7cbe2f7334d93a6f8715e49791f' + '3e1efb1a6c285b9e9aaca988ea3d4dd90ec5307a7e8067d7926bbc664c049968' + 'aac677fd0497c68555809c7f34888b5aae50ba8c4bc509a1a5773eecfaf57e32' + '7bbcc042059d61e60c37bbe6b9981f8c862c08714ced7143e480d33373bc2871' + '0a6cc670f85d68400fc49e5581b2a918bdac988ec720a9fc2196a0833db5026e' + '753924001effd0ca209bdf8fab61c4cbb8e666d408979ad351f9de8e3551a032' + 'a471b1842cb2534c8f98996d964b67344a96f5baa24db77ebc581a252d242933' + 'd413c4cfe3dc9b60b7821d4f02d95a09321cbd677e9311f0acfb6e2e97d54ca7' + '3280e64c2d216e3b190a44b5b6c26da1a9f8ec1dac3d5d494e4c650562ad5594' + '6736bb8ea447b4905a1602a67c988c5aba1fa81c03e33b1af82f60de2c23d1ac' + 'afcd6dcfb1dddbe12b695295a694e893f2279a271039ba3eb1ab48446047bef2' + '9efa519bb08cb6f0c0a753f9a70c2b683e55b783a8a79310e990cb9fbef3d1fa' + '255602bc'), + 'out': bytes.fromhex( + '72d9526061fc8f2088c225996dde720ab8f19502950b052f9c8d9e8e00f06532' + 'ba4ffff2ecd176e5836af72a44a1d58cefd03dd089b66b4dc0d92d3860e1d437' + '5595655e7abbb43afcea3a8b570f1987'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('c29d5ab216448f6ebf6388a6251160e7c9fe2413c85ac1cd2340363807ba8840'), + 'value': 90000, + }, + { + 'nullifier': bytes.fromhex('82b4ace6f48bca884543cca25633e137f3791e3ed655c775f8b6f84187c8082d'), + 'rk': bytes.fromhex('ffc4c2218a0382a530909566f44a7ab4421df95cd12e0590a19c01eb6e06b83e'), + 'alpha': bytes.fromhex('14de74940160f3450f3f8fad83c3d50dc9fc766bcdd4d5968a13ad8245613138'), + 'cv_net': bytes.fromhex('21f87ec22c92674f97585415535b5cdade848408fb729765bdcf2c36118e463f'), + 'cmx': bytes.fromhex('b46b4f684d39569dcd24db0db912406dff3fbc78466e590260d729313499230d'), + 'epk': bytes.fromhex('a7d08787142ac449a74203ccf11ad1e927b8fb4356f33ae8f81b1c9e7b3c01a6'), + 'enc': bytes.fromhex( + '43fed1e4afbd93923651a18623245c91b2717cbbf89ef9a4905bd0da58f3a578' + 'bd2330a347bc32526261273feeb11120746ce964aac12d82209183195eeafa8d' + '54ac7b02e4c107aee4ec1db2452a50bcf53ebb7902827bfef0ba144ca16c0f9a' + '1ef79116b5f86a740a68c46b28f5b1235235ed347244ff7beb65898e74f139d5' + '975965a2f786e60a9b081f96f576a087070bb7a8e1a182fbf7366467005a051e' + '28ed1509aa2177a684417e618e0a07a4f157f1152308ef7f6757533f5f45cd67' + 'a21cb1b26944fb266fd2bd6e892bc9bc672d2eed09afcd7119074b0c8639b13c' + '548df077595390a7aa4b4181341804c793644d9bd19b92e0e322d87b64075eac' + '7e6a908c9fee485175ec7c85ffd3e010a85fb67adf6435bd66f45464f7ace727' + '7471eed60135e19d420fb5b9af89957b23f33e458e09c393a02d9403eabef6c8' + 'f11f4002cd2c877197f573bc0146bd6eef0bc00283f29ad0ae500ea80c84e221' + '81f9470a4d1e8c60b4680efbd5dd47fa73cf82de930b304afe0686d11b963c8b' + 'e5a3731e7f74b245958c46c3e0b591278ec8e4206f291a80193ec325f06742ee' + '3de56bd11121df9432eb2456ae090f64e840d2ea17365abb6c6ec9fd51f94036' + 'c0ace903ae39ef5a0824799b1b4ba529c3403d240d7f18cb90559af4252f1fd2' + '27eefabfcb830b94c3c3e5114e41265cef100aad103bd25505279a39cb294db4' + '0ccfe4d4b8c13539b975564008c113da36d67d50568769751fb9c775f363c969' + '4d834ba1ea03e956a6036dc2a676918044a1fc76c777caad479de213ef856055' + '5407b7ca'), + 'out': bytes.fromhex( + 'e41a17506e1af63acd8edc563c195819e26e740b5f3fb4830242d804bcee8a56' + '2572f52e73d190ef270ecf7aaf309e93b2ab9eb6200b8466cd1aa7889b42cfcc' + '2fa9b853f9e136be5e04f9020ac82d4f'), + 'recipient': bytes.fromhex( + 'df8789f215ca59a4fc59ce4e9b694f87b354d01cc397c47b594db69dc0fd4ad0' + '0e11a41d7751189db93210'), + 'rseed': bytes.fromhex('983d2f7efb6cbb27571832127afd4f48136bdab4ebc68bc2a7c667a4ab231030'), + 'value': 0, + }, +] + + def fabricated_action(v): return { 'alpha': v['alpha'], @@ -639,5 +721,34 @@ def test_fabricated_zero_value_output_signs(self): for _, _, body in screens: self.assertNotIn('0.00000000 ZEC', body) + def test_zero_value_padding_output_is_not_shown(self): + """A padding dummy output is not shown, even though it decrypts. + + Wallets pad an Orchard bundle with a dummy zero-valued output to a + random address (protocol spec 4.8.3). Its cmx binds value 0, so like + Keystone and Ledger the device shows only the change and the fee, and + signs only the wallet spend. + """ + self.requires_firmware(self.FABRICATED_OUTPUT_FIRMWARE) + actions = [fabricated_action(v) for v in PADDING_BUNDLE] + actions[1]['is_spend'] = False # the dummy spend carries dummy_sk + kwargs = sign_kwargs(actions, fee=10000, orchard_value_balance=10000, + total_amount=90000) + kwargs['orchard_digest'] = bundle_digest(actions, False, 5, + value_balance=10000) + screens = self._capture_confirm_text() + + result = self.client.zcash_sign_pczt(**kwargs) + + self.assertIsInstance(result, zcash_proto.ZcashSignedPCZT) + self.assertEqual(len(result.signatures), 1) + outputs = [body for code, _, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput] + self.assertEqual(len(outputs), 2) # the change's amount and address + self.assertIn('0.00090000 ZEC', outputs[0]) + for _, _, body in screens: + self.assertNotIn('0.00000000 ZEC', body) + + if __name__ == '__main__': unittest.main() From fe14695371623256f903bf497f29565ff01c03f5 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 06:26:47 -0500 Subject: [PATCH 18/37] test(zcash): sign Orchard to Ironwood migration transactions From NU6.3 Orchard funds can only leave the Orchard pool. ZIP 318 moves them to Ironwood with a v6 transfer that has both bundles, after 16-action note-preparation transactions. zcash_sign_pczt takes the Ironwood actions of a crossing transaction and streams them after the Orchard ones. The tests sign a transfer and a preparation transaction built by librustzcash's zcash_pool_migration. Repins device-protocol for the crossing fields. --- device-protocol | 2 +- keepkeylib/client.py | 25 +- keepkeylib/messages_zcash_pb2.py | 79 ++- tests/test_msg_zcash_sign_pczt_device.py | 843 ++++++++++++++++++++++- 4 files changed, 911 insertions(+), 38 deletions(-) diff --git a/device-protocol b/device-protocol index 71a2a239..e8947b36 160000 --- a/device-protocol +++ b/device-protocol @@ -1 +1 @@ -Subproject commit 71a2a239940656e420850e59d62c96b4e1fc89e5 +Subproject commit e8947b360557df2cf51bd58732b214eddaa18ff3 diff --git a/keepkeylib/client.py b/keepkeylib/client.py index 72489770..72b6c09c 100644 --- a/keepkeylib/client.py +++ b/keepkeylib/client.py @@ -2068,7 +2068,9 @@ def zcash_sign_pczt(self, address_n, actions, account=None, expiry_height=None, transparent_outputs=None, transparent_inputs=None, expected_seed_fingerprint=None, - return_transparent_signatures=False): + return_transparent_signatures=False, + ironwood_actions=None, ironwood_flags=None, + ironwood_value_balance=None): """Sign a Zcash Orchard-family shielded transaction via PCZT protocol. Streams transparent outputs, then transparent inputs, then shielded @@ -2089,6 +2091,9 @@ def zcash_sign_pczt(self, address_n, actions, account=None, orchard_digest: 32-byte orchard digest shielded_pool: ZcashShieldedPool value (Orchard by default) ironwood_digest: 32-byte Ironwood digest for transaction v6 + ironwood_actions: for a ZIP 318 pool-crossing v6 transaction, the + Ironwood actions, streamed after the Orchard `actions`, with + ironwood_flags and ironwood_value_balance for their bundle orchard_flags: bundle flags byte (enables digest verification) orchard_value_balance: signed i64 value balance orchard_anchor: 32-byte anchor @@ -2106,11 +2111,13 @@ def zcash_sign_pczt(self, address_n, actions, account=None, ZcashSignedPCZT with compact Orchard signatures and optional txid, or a tuple including transparent signatures when requested. """ - n_actions = len(actions) - if n_actions == 0: + if len(actions) == 0: raise ValueError("Must have at least one action") + # Orchard actions first, then any Ironwood ones; indices count on. + stream = list(actions) + list(ironwood_actions or []) + n_actions = len(stream) - for idx, action in enumerate(actions): + for idx, action in enumerate(stream): if 'is_spend' not in action or not isinstance(action['is_spend'], bool): raise ValueError( "Orchard action %d must explicitly set boolean is_spend" % idx) @@ -2127,7 +2134,7 @@ def zcash_sign_pczt(self, address_n, actions, account=None, # explicitly if the caller passed it. kwargs = dict( address_n=address_n, - n_actions=n_actions, + n_actions=len(actions), total_amount=total_amount, fee=fee, branch_id=branch_id, @@ -2166,6 +2173,10 @@ def zcash_sign_pczt(self, address_n, actions, account=None, kwargs['n_transparent_inputs'] = len(transparent_inputs) if expected_seed_fingerprint is not None: kwargs['expected_seed_fingerprint'] = expected_seed_fingerprint + if ironwood_actions: + kwargs['n_ironwood_actions'] = len(ironwood_actions) + kwargs['ironwood_flags'] = ironwood_flags + kwargs['ironwood_value_balance'] = ironwood_value_balance resp = self.call(zcash_proto.ZcashSignPCZT(**kwargs)) @@ -2231,7 +2242,7 @@ def zcash_sign_pczt(self, address_n, actions, account=None, # include an index. The device controls stream order, just as it # does for transparent inputs and outputs above, so discard any # caller copy before supplying the requested index. - action = dict(actions[idx]) + action = dict(stream[idx]) action.pop('index', None) resp = self.call(zcash_proto.ZcashPCZTAction(index=idx, **action)) sent_actions.add(idx) @@ -2269,7 +2280,7 @@ def zcash_sign_pczt(self, address_n, actions, account=None, if not signature: raise Exception("Device returned an empty transparent signature") - expected_signatures = sum(1 for action in actions if action['is_spend']) + expected_signatures = sum(1 for action in stream if action['is_spend']) if len(resp.signatures) != expected_signatures: raise Exception( "Device returned %d Orchard signatures for %d real spends" diff --git a/keepkeylib/messages_zcash_pb2.py b/keepkeylib/messages_zcash_pb2.py index ae1c7cb5..a31e647e 100644 --- a/keepkeylib/messages_zcash_pb2.py +++ b/keepkeylib/messages_zcash_pb2.py @@ -20,7 +20,7 @@ name='messages-zcash.proto', package='', syntax='proto2', - serialized_pb=_b('\n\x14messages-zcash.proto\"\xd9\x04\n\rZcashSignPCZT\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x11\n\tpczt_data\x18\x03 \x01(\x0c\x12\x11\n\tn_actions\x18\x04 \x01(\r\x12\x14\n\x0ctotal_amount\x18\x05 \x01(\x04\x12\x0b\n\x03\x66\x65\x65\x18\x06 \x01(\x04\x12\x11\n\tbranch_id\x18\x07 \x01(\r\x12\x15\n\rheader_digest\x18\x08 \x01(\x0c\x12\x1a\n\x12transparent_digest\x18\t \x01(\x0c\x12\x16\n\x0esapling_digest\x18\n \x01(\x0c\x12\x16\n\x0eorchard_digest\x18\x0b \x01(\x0c\x12\x15\n\rorchard_flags\x18\x0c \x01(\r\x12\x1d\n\x15orchard_value_balance\x18\r \x01(\x03\x12\x16\n\x0eorchard_anchor\x18\x0e \x01(\x0c\x12\x12\n\ntx_version\x18\x0f \x01(\r\x12\x18\n\x10version_group_id\x18\x10 \x01(\r\x12\x11\n\tlock_time\x18\x11 \x01(\r\x12\x15\n\rexpiry_height\x18\x12 \x01(\r\x12\x46\n\rshielded_pool\x18\x13 \x01(\x0e\x32\x12.ZcashShieldedPool:\x1bZCASH_SHIELDED_POOL_ORCHARD\x12\x17\n\x0fironwood_digest\x18\x14 \x01(\x0c\x12\x1d\n\x15n_transparent_outputs\x18\x1d \x01(\r\x12\x1c\n\x14n_transparent_inputs\x18\x1e \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x1f \x01(\x0c\"\xb9\x02\n\x0fZcashPCZTAction\x12\r\n\x05index\x18\x01 \x01(\r\x12\r\n\x05\x61lpha\x18\x02 \x01(\x0c\x12\x0f\n\x07sighash\x18\x03 \x01(\x0c\x12\x0e\n\x06\x63v_net\x18\x04 \x01(\x0c\x12\r\n\x05value\x18\x05 \x01(\x04\x12\x10\n\x08is_spend\x18\x06 \x01(\x08\x12\x11\n\tnullifier\x18\x07 \x01(\x0c\x12\x0b\n\x03\x63mx\x18\x08 \x01(\x0c\x12\x0b\n\x03\x65pk\x18\t \x01(\x0c\x12\x13\n\x0b\x65nc_compact\x18\n \x01(\x0c\x12\x10\n\x08\x65nc_memo\x18\x0b \x01(\x0c\x12\x16\n\x0e\x65nc_noncompact\x18\x0c \x01(\x0c\x12\n\n\x02rk\x18\r \x01(\x0c\x12\x16\n\x0eout_ciphertext\x18\x0e \x01(\x0c\x12\x11\n\trecipient\x18\x0f \x01(\x0c\x12\r\n\x05rseed\x18\x10 \x01(\x0c\x12\x14\n\x0cuser_address\x18\x11 \x01(\t\"(\n\x12ZcashPCZTActionAck\x12\x12\n\nnext_index\x18\x01 \x01(\r\"3\n\x0fZcashSignedPCZT\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"N\n\x12ZcashGetOrchardFVK\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\"Q\n\x0fZcashOrchardFVK\x12\n\n\x02\x61k\x18\x01 \x01(\x0c\x12\n\n\x02nk\x18\x02 \x01(\x0c\x12\x0c\n\x04rivk\x18\x03 \x01(\x0c\x12\x18\n\x10seed_fingerprint\x18\x04 \x01(\x0c\"N\n\x16ZcashTransparentOutput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x15\n\rscript_pubkey\x18\x03 \x01(\x0c\"\xb0\x01\n\x15ZcashTransparentInput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0f\n\x07sighash\x18\x02 \x01(\x0c\x12\x11\n\taddress_n\x18\x03 \x03(\r\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x04\x12\x14\n\x0cprevout_txid\x18\x05 \x01(\x0c\x12\x15\n\rprevout_index\x18\x06 \x01(\r\x12\x10\n\x08sequence\x18\x07 \x01(\r\x12\x15\n\rscript_pubkey\x18\x08 \x01(\x0c\"J\n\x13ZcashTransparentAck\x12\x19\n\x11next_output_index\x18\x01 \x01(\r\x12\x18\n\x10next_input_index\x18\x02 \x01(\r\",\n\x16ZcashTransparentSigned\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\"\x8b\x01\n\x13ZcashDisplayAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x07 \x01(\x0cJ\x04\x08\x03\x10\x04J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07R\x07\x61\x64\x64ressR\x02\x61kR\x02nkR\x04rivk\"9\n\x0cZcashAddress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x18\n\x10seed_fingerprint\x18\x02 \x01(\x0c*V\n\x11ZcashShieldedPool\x12\x1f\n\x1bZCASH_SHIELDED_POOL_ORCHARD\x10\x00\x12 \n\x1cZCASH_SHIELDED_POOL_IRONWOOD\x10\x01\x42\x31\n\x1a\x63om.keepkey.deviceprotocolB\x13KeepKeyMessageZcash') + serialized_pb=_b('\n\x14messages-zcash.proto\"\xad\x05\n\rZcashSignPCZT\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x11\n\tpczt_data\x18\x03 \x01(\x0c\x12\x11\n\tn_actions\x18\x04 \x01(\r\x12\x14\n\x0ctotal_amount\x18\x05 \x01(\x04\x12\x0b\n\x03\x66\x65\x65\x18\x06 \x01(\x04\x12\x11\n\tbranch_id\x18\x07 \x01(\r\x12\x15\n\rheader_digest\x18\x08 \x01(\x0c\x12\x1a\n\x12transparent_digest\x18\t \x01(\x0c\x12\x16\n\x0esapling_digest\x18\n \x01(\x0c\x12\x16\n\x0eorchard_digest\x18\x0b \x01(\x0c\x12\x15\n\rorchard_flags\x18\x0c \x01(\r\x12\x1d\n\x15orchard_value_balance\x18\r \x01(\x03\x12\x16\n\x0eorchard_anchor\x18\x0e \x01(\x0c\x12\x12\n\ntx_version\x18\x0f \x01(\r\x12\x18\n\x10version_group_id\x18\x10 \x01(\r\x12\x11\n\tlock_time\x18\x11 \x01(\r\x12\x15\n\rexpiry_height\x18\x12 \x01(\r\x12\x46\n\rshielded_pool\x18\x13 \x01(\x0e\x32\x12.ZcashShieldedPool:\x1bZCASH_SHIELDED_POOL_ORCHARD\x12\x17\n\x0fironwood_digest\x18\x14 \x01(\x0c\x12\x1a\n\x12n_ironwood_actions\x18\x15 \x01(\r\x12\x16\n\x0eironwood_flags\x18\x16 \x01(\r\x12\x1e\n\x16ironwood_value_balance\x18\x17 \x01(\x03\x12\x1d\n\x15n_transparent_outputs\x18\x1d \x01(\r\x12\x1c\n\x14n_transparent_inputs\x18\x1e \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x1f \x01(\x0c\"\xb9\x02\n\x0fZcashPCZTAction\x12\r\n\x05index\x18\x01 \x01(\r\x12\r\n\x05\x61lpha\x18\x02 \x01(\x0c\x12\x0f\n\x07sighash\x18\x03 \x01(\x0c\x12\x0e\n\x06\x63v_net\x18\x04 \x01(\x0c\x12\r\n\x05value\x18\x05 \x01(\x04\x12\x10\n\x08is_spend\x18\x06 \x01(\x08\x12\x11\n\tnullifier\x18\x07 \x01(\x0c\x12\x0b\n\x03\x63mx\x18\x08 \x01(\x0c\x12\x0b\n\x03\x65pk\x18\t \x01(\x0c\x12\x13\n\x0b\x65nc_compact\x18\n \x01(\x0c\x12\x10\n\x08\x65nc_memo\x18\x0b \x01(\x0c\x12\x16\n\x0e\x65nc_noncompact\x18\x0c \x01(\x0c\x12\n\n\x02rk\x18\r \x01(\x0c\x12\x16\n\x0eout_ciphertext\x18\x0e \x01(\x0c\x12\x11\n\trecipient\x18\x0f \x01(\x0c\x12\r\n\x05rseed\x18\x10 \x01(\x0c\x12\x14\n\x0cuser_address\x18\x11 \x01(\t\"(\n\x12ZcashPCZTActionAck\x12\x12\n\nnext_index\x18\x01 \x01(\r\"3\n\x0fZcashSignedPCZT\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"N\n\x12ZcashGetOrchardFVK\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\"Q\n\x0fZcashOrchardFVK\x12\n\n\x02\x61k\x18\x01 \x01(\x0c\x12\n\n\x02nk\x18\x02 \x01(\x0c\x12\x0c\n\x04rivk\x18\x03 \x01(\x0c\x12\x18\n\x10seed_fingerprint\x18\x04 \x01(\x0c\"N\n\x16ZcashTransparentOutput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x15\n\rscript_pubkey\x18\x03 \x01(\x0c\"\xb0\x01\n\x15ZcashTransparentInput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0f\n\x07sighash\x18\x02 \x01(\x0c\x12\x11\n\taddress_n\x18\x03 \x03(\r\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x04\x12\x14\n\x0cprevout_txid\x18\x05 \x01(\x0c\x12\x15\n\rprevout_index\x18\x06 \x01(\r\x12\x10\n\x08sequence\x18\x07 \x01(\r\x12\x15\n\rscript_pubkey\x18\x08 \x01(\x0c\"J\n\x13ZcashTransparentAck\x12\x19\n\x11next_output_index\x18\x01 \x01(\r\x12\x18\n\x10next_input_index\x18\x02 \x01(\r\",\n\x16ZcashTransparentSigned\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\"\x8b\x01\n\x13ZcashDisplayAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x07 \x01(\x0cJ\x04\x08\x03\x10\x04J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07R\x07\x61\x64\x64ressR\x02\x61kR\x02nkR\x04rivk\"9\n\x0cZcashAddress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x18\n\x10seed_fingerprint\x18\x02 \x01(\x0c*V\n\x11ZcashShieldedPool\x12\x1f\n\x1bZCASH_SHIELDED_POOL_ORCHARD\x10\x00\x12 \n\x1cZCASH_SHIELDED_POOL_IRONWOOD\x10\x01\x42\x31\n\x1a\x63om.keepkey.deviceprotocolB\x13KeepKeyMessageZcash') ) _ZCASHSHIELDEDPOOL = _descriptor.EnumDescriptor( @@ -40,8 +40,8 @@ ], containing_type=None, options=None, - serialized_start=1784, - serialized_end=1870, + serialized_start=1868, + serialized_end=1954, ) _sym_db.RegisterEnumDescriptor(_ZCASHSHIELDEDPOOL) @@ -199,21 +199,42 @@ is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( - name='n_transparent_outputs', full_name='ZcashSignPCZT.n_transparent_outputs', index=20, + name='n_ironwood_actions', full_name='ZcashSignPCZT.n_ironwood_actions', index=20, + number=21, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='ironwood_flags', full_name='ZcashSignPCZT.ironwood_flags', index=21, + number=22, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='ironwood_value_balance', full_name='ZcashSignPCZT.ironwood_value_balance', index=22, + number=23, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='n_transparent_outputs', full_name='ZcashSignPCZT.n_transparent_outputs', index=23, number=29, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( - name='n_transparent_inputs', full_name='ZcashSignPCZT.n_transparent_inputs', index=21, + name='n_transparent_inputs', full_name='ZcashSignPCZT.n_transparent_inputs', index=24, number=30, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( - name='expected_seed_fingerprint', full_name='ZcashSignPCZT.expected_seed_fingerprint', index=22, + name='expected_seed_fingerprint', full_name='ZcashSignPCZT.expected_seed_fingerprint', index=25, number=31, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, @@ -232,7 +253,7 @@ oneofs=[ ], serialized_start=25, - serialized_end=626, + serialized_end=710, ) @@ -374,8 +395,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=629, - serialized_end=942, + serialized_start=713, + serialized_end=1026, ) @@ -405,8 +426,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=944, - serialized_end=984, + serialized_start=1028, + serialized_end=1068, ) @@ -443,8 +464,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=986, - serialized_end=1037, + serialized_start=1070, + serialized_end=1121, ) @@ -488,8 +509,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1039, - serialized_end=1117, + serialized_start=1123, + serialized_end=1201, ) @@ -540,8 +561,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1119, - serialized_end=1200, + serialized_start=1203, + serialized_end=1284, ) @@ -585,8 +606,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1202, - serialized_end=1280, + serialized_start=1286, + serialized_end=1364, ) @@ -665,8 +686,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1283, - serialized_end=1459, + serialized_start=1367, + serialized_end=1543, ) @@ -703,8 +724,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1461, - serialized_end=1535, + serialized_start=1545, + serialized_end=1619, ) @@ -734,8 +755,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1537, - serialized_end=1581, + serialized_start=1621, + serialized_end=1665, ) @@ -779,8 +800,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1584, - serialized_end=1723, + serialized_start=1668, + serialized_end=1807, ) @@ -817,8 +838,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1725, - serialized_end=1782, + serialized_start=1809, + serialized_end=1866, ) _ZCASHSIGNPCZT.fields_by_name['shielded_pool'].enum_type = _ZCASHSHIELDEDPOOL diff --git a/tests/test_msg_zcash_sign_pczt_device.py b/tests/test_msg_zcash_sign_pczt_device.py index 6bd8ff7b..36c62e61 100644 --- a/tests/test_msg_zcash_sign_pczt_device.py +++ b/tests/test_msg_zcash_sign_pczt_device.py @@ -293,6 +293,802 @@ def fabricated_action(v): } +# ZIP 318 migration transactions built by librustzcash's zcash_pool_migration +# (build_transfer_pczt, build_prep_tx; orchard 0.16.0) for the "all" seed, +# account 0: v6, NU6.3 branch, lock time 0, expiry 69120. The transfer moves a +# 1,015,000-zat Orchard note into 1,000,000 zat at the account's internal +# Ironwood address (fee 15,000): a 2-action Orchard bundle and a 1-action +# Ironwood bundle in one transaction. The preparation transaction splits one +# note into fifteen 1,015,000-zat notes (fee 80,000) in exactly 16 Orchard +# actions. is_spend is False for a protocol dummy, which the host signs; the +# sighash is librustzcash's (also checked by the firmware unit tests). +MIGRATION_TRANSFER = { + 'expiry': 69120, + 'sighash': bytes.fromhex('37d64f27ae739dcb68087da7d8a2d61abdb2fb1fb89e3d4f0e029d80965f4edf'), + 'orchard': { + 'flags': 0x03, 'value_balance': 1015000, + 'actions': [ + { + 'is_spend': False, + 'nullifier': bytes.fromhex('1864599aa7e56f8c0399922334bc322b1e5c0e9614056e705f5dca13652e3e30'), + 'rk': bytes.fromhex('8e0da71eb98174f038bcce8dd232ccf690e8fcdc68e2c571310626f44d897307'), + 'alpha': bytes.fromhex('d60207f13eebd372f8f9e79b966230dac87839f33a68c21b1a7adf5602b1c024'), + 'cv_net': bytes.fromhex('ccb4e6427d30b8b910fc733ba30a1a4a6f43bc339ba4e7d38e731aa8c79553ab'), + 'cmx': bytes.fromhex('e8a96150abbdd4a657639d23e8202c621eb422a46f115a4f9b329203783daa27'), + 'epk': bytes.fromhex('76b0f4ff80bf7a981e2f127086358c3eb65cf4ecf855dc7153f50d41421648b7'), + 'enc': bytes.fromhex( + '8087f8103c81159a18755a0dc6506283ee6872d2ec050051f9c52403b831aa74' + '2965708f3c466aedd08dd8db5d2df018ef5ba744a23ea84822b715e4b1c89524' + '49264e6d548e4cfabc9dee6d5ded01f80c137f5b08282ab151356382e8fa7c70' + '722d6d4f8de0503eca664b27bfe415a0f9413898b39215669179f4f2ec0a80d9' + '4c624179133386574e2aa4a4c9702c697493c95ae442996cbcae044eea1b1d0d' + 'df037aa81b779f894dc86c17965ce4362b60cb2e314634a49398e1f43ad80c94' + 'e64573bfb2bbc5d5f398c5c9dc57133a9b6d3ce5a7392ff9d0e716f8b6f0f3c2' + '5732e8c108f4bdfc316eccb9202d1455316b32d4a5bb5f427d2ad69a8f1d1a0c' + 'b87ae8ddd870ba00d37e246b758845e147ce19ebad1485617bf0e51d92f9f9fe' + '0aa9804091e0ae89dcb815f3238df56accf9b3a25703f481462d8ca43d3a1673' + 'a1e9053fed093ef5bc3c74e31c99cc0343d8aa6fb8ca56f63676b07e4ec898ae' + 'c8b82eb90194d47b914669435a4b3161e76724774cdf3bab2390a3d31fe18a16' + '06fb060728b6db876472f7d75cbfb6e2f75065ef7b8507a54a5fb219e01e2bf5' + '81af63ba475347a0a75bb8b6cc16652188fd315b73e8155492e6b1bf78ad79da' + '6ddb9b19599d9e2ed73b17ca2f5281c78bedcd215918290c3c3ee7bb44ab3ae3' + 'a88f5065a7585905bacf2c1af0ed7a98aac974b992945faddf8e07f11aa259ee' + 'c891e55cfc9924d44da5fef14b823e7c2c246c4daf1ac01e4e1ecfd00d1efdda' + 'bb181a2b9598ebb42edb69d116a77820b46482034ee7f54bf0f556bc9cccf2db' + '3d16f60a'), + 'out': bytes.fromhex( + '608c408a0545c24615106484531cfaea8fdd107f2ccd5007cc3a74640c1a058b' + 'b3e8e52a668aca85c8be3780496ef53bcc0a768496ef089c3db2382f75904882' + '9e1447576fc7df7626a1e76db2a620af'), + 'recipient': bytes.fromhex( + '937bcbdf0e9bbc3a056d4b840825fa3adf31c338eef5d0a57d56e432455cbdc5' + 'daa4f738338166c7b9ed07'), + 'rseed': bytes.fromhex('809144435bbd53c9d16a3944e5355a512a875cd5177d6f97ce291debc4684229'), + 'value': 0, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('489a7ca2cfced841420598f2dc0236f637652fc3fa410fbe448e28aadb5b9c0f'), + 'rk': bytes.fromhex('a618a687af32bb8d148bd5745c8b6f8b8f40d18d5b2bbe733d10e3b024966b01'), + 'alpha': bytes.fromhex('b3abb9d87a35fe5d9697cf96d9eca2e802988bd6bc2de2ebba30eda0eb10a31e'), + 'cv_net': bytes.fromhex('a122f0fc50ec9b8653a3ddcd4e12bd9d9910f879f6a4881f958852b0797f7e2d'), + 'cmx': bytes.fromhex('eb709cbf300754e5f17f1d6947d0854575305e487d96344b55ff2de9821b5f36'), + 'epk': bytes.fromhex('1374c5379cc8daa943f521558d7a4c0a7ac8ae21ab311f275b051a8631f799a4'), + 'enc': bytes.fromhex( + 'f714bbc0dc6f64674d579ff6a85de299a4a98788b2cf473d687b142e2501ae94' + '9309bd2295c8b93afa7d0607e8663f516e855c9104341f09dd649e406bf8f21c' + '687c5706b1d5afd74fd8e063921a05ebaa89cee7f7251d518aa271533c5ce752' + '4074b7f32dcabfb6fb4309bf86e9cc00b3cb2424b7a2ae6ecb83e14b4c4fc4bf' + '86d494b2d528d7d9281a8f7af1e78fb674327782b178d1469119199b3d9de1d9' + 'd381a4600b85323cdc93f43f89ffa9cdc94e0095eaed60ee68047b015b57c362' + '159f43a0bbfa21c4be1856dc1256a04b27d3ae52962bd9a8661b74f0bd7a4a9b' + 'f7e53fd02b7b33038f824e20a3d21f3d6b152df460bbb720f7597d267707cd00' + '14d4099952ad1195076d521da378de8b14261efac4e1bd2b3deae15c899de20f' + '090210d3ab853d7f25dc01522b725574282f45bbc78c0213fbe6f040f2b3355a' + 'ff909180bfdc21dd8c1cbcb7ace23d4a5575633bfe561ca018f23beba644761e' + '70e87d03280b6677d1e7af24783d23476a6e5958789eca3f738cdd6683a2b762' + '3606de12b9a75ce127169c02fb37e22549451ce3d1912d0f6d546b541c2a5e93' + '5e18d924d43629c5a0ae13b20498592cbf6daf2f180acb7c7cfe401b37e808dc' + 'e2fc54227a523fd8b973a4f261ee6dc3c48ea4ad10d2d4a07807723b6a8ade44' + 'fa3a236fbba8a0f7f8b7b62e82985b6a6e63ca95f01cf597eba7c839571d5d77' + '31eeeb9a90961537bb0301497c20ae735f21715bc37ca3ca1626aec076ec3b73' + 'a8001204d3d59ba1a078c996bcc97ad89f7ac7890b89145e4527bcd76355bef7' + '43eae3ac'), + 'out': bytes.fromhex( + 'fa9051ee2d97442f3618721452e5433399d53c5f3b42ecbbb6e5ab0cd4ca6457' + 'e7444ccece08e96d282fc184fe08e23d3b8084bf63a5061e21f7fae609987704' + 'd50c3954228af752f5e00abb61b65f3c'), + 'recipient': bytes.fromhex( + 'da973031634a8938ad1c480f978780693ec7709ba5caf58d8a7eb945586cbed6' + '45520f17387437bcfdc216'), + 'rseed': bytes.fromhex('259c76a04a44a10defc8b3de279aaa2f57f453c4843932e07ab89e8d08a7045d'), + 'value': 0, + }, + ], + }, + 'ironwood': { + 'flags': 0x07, 'value_balance': -1000000, + 'actions': [ + { + 'is_spend': False, + 'nullifier': bytes.fromhex('436effc6778e973a5cc77c202d0a396e5b932905346443ddaaadc8253ab4db27'), + 'rk': bytes.fromhex('6c05eb42cca028e4ed2740bb176d1bc6ea64a72d8012437d3ee4e488c0116e06'), + 'alpha': bytes.fromhex('ca3ea1b2ed99993558b786870350dd0b3a6bb2c096e9f3d9e6d744743ad7563f'), + 'cv_net': bytes.fromhex('8ea1ceccc9a86e011ecfabe68f68b6aaeff93e57d7fd16fef5befd15edc5a382'), + 'cmx': bytes.fromhex('a8cb867f1ae569fef09e6b6642f4795e37992ece4e4fbdc33f1354b379c97c20'), + 'epk': bytes.fromhex('ae9f613cf5a2bc3c5e480a4af39b36e6d40c8bf7a664edb0548c4fd2a5bf8414'), + 'enc': bytes.fromhex( + '75022c701b77054ebe6886efba031ceb9d0a48c46f34a58b9a9f4b4dbaaa63ef' + 'e9445158db413bf37b22901cf4334f1bdc8acf15626e8f442998fbd1ac7e42d5' + '6639800df728a431f3496d1235dc1888ef296d4f8b63db81721a8e4895b43886' + 'ed342c7226c0265281a7e5701d1946ce25e06f521ded2a9be3db6b926ec554f6' + '83821c46fd19a342d7b883c1bf7e385643e947ec0dc4d7f41de7f54131ea60f5' + 'fbc1c9f9e454dc1a5434bea7956f7a6e404aaaef73b481ce8184c44cb0fe9bd3' + '2ee18a8d7da8e2493fc88c9e6c6dcd601073f7ba8b0fc77cc6f6fb9ce74962ae' + '6dfd10fba03197e2a488633e479cebace7d99a5fb55c73114db764bc9ac54264' + '0e4747fc2d81ed02f8297cc4fe52fc1fa9e5402fbe9ba2f6ec74c59f1102d082' + '2d4d3c0a0a371dfacd541477f8c920e36cfe8f9e7d712b6bb2230963e07b16a6' + '439b116b6d510e5e4f3ca5af8da8597fde93dfe2bad94570786367da4de38937' + '797ade29d01a762edd85e01fae2cd8d8ce3161ae9dafa80121579e20076e6293' + 'c25de93a3b7ba07f3be7a305c6193ee53668d6039d4aebc56207ea22582f3d68' + '4c7df5fd48c726d9fe0179015cdf7f88572adfd1897150c38d059a02204b2b72' + 'ad982e83400f75a397eb2ba67a793a8d1a179c3355b2a9f4ce2bdc0124ceffe2' + '93fe0d00f9fcc072be2fb0b279b6bc6c67e03ca396dcd0827dd9485d1a518399' + '2ae4425ac5cf3f7629c6cd0cfdda397b1098a1a41e386c223182af6823e847fb' + '753a18610d24ffd130cc015d569cae682273e20498f7240972a9c26f26981159' + 'edf6104c'), + 'out': bytes.fromhex( + '86d9ffaf8df7203a5db69ec59ebeb544b34a5863dc7f4db848c70bb8678b959f' + 'f86c5224265c690e5ec56910c66dd513fba56a4c946f63d7009a0f86f9537b54' + '8bb3e19cb69d2ce349246b2c352adf88'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('7ecfb512ece0d14ff72733c55f85dcb3717ec0196cb6248fee63c590ebc6e8ba'), + 'value': 1000000, + }, + ], + }, +} + +MIGRATION_PREP = { + 'expiry': 69120, + 'sighash': bytes.fromhex('9ef92705d1598a058ba70b3254653e7c0c2efa108cf8cf611b0a71266d7d12de'), + 'orchard': { + 'flags': 0x03, 'value_balance': 80000, + 'actions': [ + { + 'is_spend': True, + 'nullifier': bytes.fromhex('054ec0e73431e55a5c9282a41ebd2af81a3dc0515c849dadb4817c7c397a9b24'), + 'rk': bytes.fromhex('49f1ba4aa6ac6a111632ec4c1a799988cd0f595d10379a2980f45b0ebad74f32'), + 'alpha': bytes.fromhex('e2b209a4c7ece0b87a6d0149b919dc636e89accc183c03489a75781e7abcac30'), + 'cv_net': bytes.fromhex('026b83c63c03b0eafa8bb82f70e2f557136ca082d35563bdf1e044d667c805b7'), + 'cmx': bytes.fromhex('5e2d87ac1d8a59bed84174c856e713acf1bcf806aefdff44725c88bc517d4025'), + 'epk': bytes.fromhex('9639beb15a85b79ae394cd9b394ed82364908b33a54908ede023f88aa1822280'), + 'enc': bytes.fromhex( + '405978bbdac91023104dd3885b96a8355108413bc16e82db5fb5c2728b941a5b' + '07447a4b52c1e3bde81eb3ebf44f2481598779059dec907d310a633703181059' + 'b52a010b2a0b4f8ecbdd1a04108bfd4971fbc326209229219bd951a8806428e8' + 'af01859da5a5941a5183b292cc03a7ba7f69bc4359e33bb4cb6d843e261ffb07' + '77bf92f6841e4fb646cd9e65d1443cf582138a2c8472dd4701a0a5ebe1396c42' + '3315b630ef409388a3db83ba35ff52ab7ecd3155048f05411c06d73aafc6d747' + 'f94e6806faec548fcab05a630217f520a2cdada7906d14406fa93211529c3edd' + 'd5f3803aca5fa86527668a5a19d01fa5f7b9091d09048b9292ea195fc001a002' + 'd709a57c94b7ff4e043337f392b59831a8467c61a9b358528f4427a1d78e0556' + '4e5a570d6afcc995ec737a62de866d8989c3fd652d7174444319dfa8e5493250' + 'c6018149fb15507f2a35f13d58bd8a695fabcc8a07174b3f6e675ab0ee95480d' + '2308aaa7a3c5e3a5affecece3375ce4fc2d0a6c6e156fa7764fa6a68fbf89c84' + '640daba42cf345408d18bb88eff1a13f3567688a46fba77720047f255a792af2' + 'c4046a50bb904b4e2bd885f82bb7085979d11b8502dc09ada9291ca9bcd1017f' + '0a5e6a09344bf783976d69ef787ba6b394a6ee2c6563fba02bc0ff7ae7d6f499' + 'da6ba692cc5eca6fb2a1eb6c1e9e2c9f05209675278d3e0d51302927dd6eca2e' + 'c10918646f9ffd5b1e57640464fb75d33034132933fd1526d8c28ce6d6e70667' + '8d044a52659bc654559e90d440b546f317b295b1183fdeb26dba6bd7ae7ed937' + '9fddd84a'), + 'out': bytes.fromhex( + '1e2d399e94df4bc9369536a5640049af19013052ca97c9c3446ab25da81cf049' + '8a0b71bb55c66ba5f435c190a3ea047f49e617dff4a6ee3e765bc9c696cf1361' + '23bc52f37fb084443d8c2ca5e52d7e73'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('4472e5d068978450f247eba631f080a34ca5265be231e1ea01d76b1fa0ad022f'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('1e9167c36ccddea1bfeae395089b3c9c084ec497f1cc1a6064163eca850c920c'), + 'rk': bytes.fromhex('0ba435bf58e44f0fc5df77904f7500ae6173bd5b115ec49a17b2948ba9c362a0'), + 'alpha': bytes.fromhex('99188cc6049c504b58fb3107a5095270083002032d24923353ea1b5ac238e721'), + 'cv_net': bytes.fromhex('ac8bc004cff0fa6ada12acd1c2b67629dccd83305f6292c1d46306d614b03b23'), + 'cmx': bytes.fromhex('6c8b9235b5ab488414e47cbb78a1471d5523b62228bc469980e8288d3e0bea34'), + 'epk': bytes.fromhex('6adcdd0c83ea1841e96ee48c1c820f4366e5672646742999810783c9da6d7b92'), + 'enc': bytes.fromhex( + '5b19ca83d72fe4914b5a0eb56508f186cfd3417d72764b09b2923da9554b4f44' + '8f76bcfcb2bbb1df591b1cc83d8d92a748cef4d8e401fd9ad62e824768bba2a1' + 'f72c54553365dcc119863ddb43b262948cfa19ede52c0159a3f7f5b6f8bd265d' + 'dd642fd50dffc2ab8e1767c6e94f278c2ad09583825388239f0230d0cbb50e83' + 'fe19c0da47b450a94ab7b3cc8ad03ba14ed6144249d61660e2ef177e1f2d1ffc' + 'bef9142ff2cd192ee059363bf67bf1d267b3db1616b238399c7829c95c914780' + '0fb6946a3d3560c85f4acee07b3dde868bc5068ee169a30bd9a1cb8be4e1451a' + '123036f6644fe5cf372ef9f14a5fdb5b7a22bc44b2bcd1fef077960b367247de' + 'd0f3e6f7b0a2bd59418059c03713d9458f9b2bbb1c01d13948c0ae2824017710' + 'ded86dea4f2e2bbb28ffcbee53b2cb2d9348998d62a99d7094e85b49ca5ba583' + 'f5394c7407bc8d8a356334993b13bf490c2fcb554dea2120e1724deb3fa2ecf4' + 'bfd120e6557f0ba0d3f5d0c8e6934357751b13dc3c2d301297d02f336eae4534' + '06bfae54262ad3e4abad97b00c0da2048acdb7686c4d3cd9057e9744e108b4dc' + 'c394579b602735ee1b4d3692057fff978e274f2ba9cd73b096999cc9336ac263' + '7c88542d087b3990881e3a92cd823fbaa497bd9af6017f0f32c82fea44c2c167' + 'af21df07910c2fc92c6fc6509695bc7b6f6b36e760c7b8f890f68f2143577bea' + '2b52b4e79e4e1a46ce46a7aebfc9e0694b4b27fde0ce0e369f8e072644ab1042' + 'dca5c6cd3c9a6d3e0e2c7f75f5d395bc5344dd11ae36da88e854c8d87d1faa30' + '9e1f4942'), + 'out': bytes.fromhex( + 'bffa9f4a10c74c0921509df29e7d2d48d33eb0bb9a4bccc39025fa02bc59d9fc' + '8ef211d81dea3a431c81d5f4d300144853e480ae179562bb3562ffad91ed1139' + 'ab45b62e3895ce45599de0ce4ed53c8e'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('df5ffe7fb55357586aff35971495391edc50b0f3aa13b2e86301e7e51a72b62f'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('bd279975aa116b7a6ab041d9b7025d5d7d711510657413469c4fec8482469d0c'), + 'rk': bytes.fromhex('3a3bbaf10cba4710f6b6aa292570b8618fc918a1bd6acce6a76cbb3fdf3fa1af'), + 'alpha': bytes.fromhex('aac41becf3358cd8f0c1e9833ba9303398436077ed870e471598e6f0a9af8325'), + 'cv_net': bytes.fromhex('7e741c1968cfe1a44879f786827d09bc8d4c1784090dbf15ca7dcc87f151babe'), + 'cmx': bytes.fromhex('10fd8bebba556d433f407718ac2753c2be129c8d76da8c56a484bb14503a7117'), + 'epk': bytes.fromhex('4ffdf1afd2e75c0e2104e18061f338a3d3071fd5af823c9b634264dde4a2b31d'), + 'enc': bytes.fromhex( + 'de190d24706ff2cbae88088d97490349ab087c40fc5a0785dea63b5cb5749f14' + 'a2fb2bdf3538c17d198d4486e7c0c2eb8cd4f39cbcb7aaa721c7608abcb6120e' + 'd9bceb204f0c5c67c25cc3caf0b77f099e95a9644adf9adae0ae7ceef8f5e343' + '1aa83a705bbfd751901e8ce9adb9c080130021caeab3150de2b10716791219a4' + '44dbe13e861d0c1c0eba7dc52b16d59ca84b7044e88917a7e0a5bd5c21ae814a' + '7b61754662bccc774ece3e83ae3dc40bbde4044601291fa4611be64122ba75c5' + '7c396b13745a097a5b0622423d35a53d81606a0e4e0d8a5508baeebade8e2a84' + '7a1292f18b7c8f763fa39ae54a725707efb1282fe2fc380550c8e201b304c2a4' + '28631f04602249f35cb04b92d89d3f5ee0687f15f581fd1421e5aa3504f4fa6c' + '560253bac75b3951df2b871090e077835dbbba5c083ad8fa6e16529d6cd764f0' + 'e970ab155e61789a3c95a9889639a3479766f5cb75c03133636cb915a5fa86be' + '71bcc1a4fa561074fd5b2f9a48a9654733a9d472901197c96b8a54e40cdd9f9e' + '4ae9a1c82af52b45f0b02c82956c250546f732cf1ed22abf01523714b21f3c1a' + 'f3868a236343cca4ad81d227f46eae69e95f966cbed0331bd195ff23451896bf' + '95405921fe1e0ba8cdf010807cb6c10d1ab22d0e2a0e6368af8c77dc266319e7' + '13b552df2c0f6912915e6cfacee9eef6c752897de533419ff0c1b6b2be63322a' + 'eaa6c246989916123d45aa7669487bc30576a9c2727c6bb6c07daa58d07a0931' + '3b5858d75b2d48b94fcab0a8d4fd2fb7ee6af73ca8d116f3353455f9600b70e8' + 'e36b3361'), + 'out': bytes.fromhex( + '465b8b2660a00bc32d67f26c501fa6ddc2457664c49e1f6e7fc09e8e371cc7b3' + '807a800fceb01c6afc79b0d153b23d6b85276c703b41e371d2f3f741964d5843' + 'e2f4cb7c403d75664c536acb6dfaff08'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('f3cc731a88be61db608e0e647d65669174778a9c7523d86e9ce96960af2404d1'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('46ec8306c0fe282e2ffebe4a7dfb99bb5d66594376402310e6d329ad1fa72039'), + 'rk': bytes.fromhex('9a7515f062b707fb218b763b2af09fcb696af3ee5e72acbfa569130c5e8bab8e'), + 'alpha': bytes.fromhex('134a9c395fb85dacaf5394c35be9df6f4ff69d5e967ac21b22fc1e2a1b06681b'), + 'cv_net': bytes.fromhex('32671fedf628dee6bdbcc4c861949b9dfb7aca84d2cb09f3b7276facd33166ae'), + 'cmx': bytes.fromhex('a6bf790e0eff686ef6561743880e609aa2ac9ac2fafb809cb536428387155034'), + 'epk': bytes.fromhex('38a2115c524ac490a5c656742e85ec3f4870a6c6207576b35ce35f5e9a304c00'), + 'enc': bytes.fromhex( + '4d653b68299ecaedc541dfe5e48498a7d8d363d56554e851030ac55781178d0e' + 'cbe7ba151f99195e49ea6898676ac4eda0e12f55e56bd6909fff7bd4c4a3de8f' + 'c34fa83472065f620db37d16f7efa36e024366062b11ef9e7cd33da638e906bf' + 'ea92031869fcbd8d4e37f3094484993e67574116033c1a80ee1212fd01cb938a' + '0e671134e42ef954a4568e23f672f58ef64d937273e7a6a0b8d0673c2fa9eba1' + 'f060db60cc7344cd81778e04a2049fbaf3dc160acd039c5c4fa1d2b93eb851e1' + 'ab34f64e9a562a72a5dcd7c8f2ae9b991760a26cf9f966fafb272a82e7da0c9e' + 'bfddba790abc62691bc6c9739224764911d941f68c5f9209f0047356f2c7d042' + '06156872a3ef82394739d5a7921ce81de2d2b4a5bacc84a864bf75d0dcdc8b1a' + 'ab6eb5eeccbaee53518c491dc88672ed1774b950e39cd26590d8bf7894ad5790' + '2cf4ffb611650fbda399dfca0049829a278a5de46921f6e8bf9c358be2d1dbfd' + '806e89bc619c2492ef13fd09ee925fdb0bfc54581bda16828415e3ce4873c05f' + '7f555382ea943125b673b233b9612a9b63e6e78ddb27c180f69fcbe60e2d9d64' + '29cf273c1245c2b4cc56c9daed238a962e283eb1bcb5b31a81b7e42d4da91f02' + '0e4b51f20232d77ffae9e387515e99daf456a61cfc141bf0390257380797b154' + '567d335ef6376101b2d9feff939768f81ffd3f97f34bb92f8d6439a51801af33' + '8dfabf9c9779de2cd587381f58835ce491704359e15e668a3606f80c51fb95fa' + '45524400320920ce3c32668bc9bea9000053e2de0607e37608d649630af0991d' + '2b26a11a'), + 'out': bytes.fromhex( + '17527eab850e901d0fca69dfd95d0bdabeeae5a05f20a8ba8ad6bce615f8a1f1' + '0166a0777aed3bb3536eaa639e9b9cac6056508a5fb8f585182b48b9c166beeb' + 'b0f30af96885078502cdc000181a698f'), + 'recipient': bytes.fromhex( + 'da973031634a8938ad1c480f978780693ec7709ba5caf58d8a7eb945586cbed6' + '45520f17387437bcfdc216'), + 'rseed': bytes.fromhex('b28fb566a83a3e62b49a4577d3a299d1c5e9692649917f4a495da4f590e45069'), + 'value': 0, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('84b1e3f25066dc3531ec282f8ea0a1fd2f8cbec31e0a5c60ce1b5eaeb820521d'), + 'rk': bytes.fromhex('88a5cc84f7483c9f775bb0d5c70409d05a44db5e6262f4a687468d4349a7d58c'), + 'alpha': bytes.fromhex('3527420fcbab8f9580730144813546c1cb50f608d06aab0c145f59634f7bdd2f'), + 'cv_net': bytes.fromhex('85c7f1af6a5226cf953e4dcc5ce531205fdc1594f8aab38ff62b497e80413f17'), + 'cmx': bytes.fromhex('94988df15b79910e7c217b069e8175588b3844c839b1cd07b4bb238552acc22b'), + 'epk': bytes.fromhex('6fff64e1409564630a7efddbe75f4f7f6a5411126cee98a6a184187b218a7793'), + 'enc': bytes.fromhex( + '9523ee3dfd23d9d418333d4d4f541d6e1ffb6708a57847858944ef9c043da78e' + 'b2a834dda0dd3c26704566cd823928438af12b279851a73204795d370efb3928' + '2e41ecec65eace5b96f7e79cf0bbf763bde3aa2672976899e487fea37fbcf250' + '179f895ba59ca2cb33699cabd073b05af5585e2f34c88d5432dd39c9e28622a3' + '12f2fb32fc7dcc3445b9f8044ceaac5e3828a36339576c502004e1b619cfa2b2' + '148f8b4d04c8f8df85b9e32496b15f2ba6c398b502f9bc1394fe99a4d3538b2e' + '5a22d86afc32d6704820a80495be317dcc97ac9a8932f8e2e5a1c7bbef99a613' + 'f51374ba41d6fa0d7a45f2a4cf9450a888e6b586c7371c91afae1c30067b6019' + '74ca0494458efd07b26fe402fa5e9fb2e9ed45072f0de676329d0acd34de67f0' + '0e5cb863798568991cfeb3a3647b516cbf80442ed830966035ced2e8153ef7bc' + '9e7c848c08f5106e507c198f4c463cf5cf33147d2eb0285ea23d978bbbb2f2e4' + '653c75c3a67f31048d2ad7458ec3bcfe65ccf33e08d7f869d4c10b5e8dc51797' + 'b4b4aba646ae031ce13503d37c23202322f68a1de641539ab8906ca1cc2116bb' + '02f3e23b2484a50ad557ca59beb12210ad6af7c3361a915baf8da33021893eb7' + '3ecfbaa020a5800332ce1ebdb3678d25dea61f32f04596ca6e672023288c59e5' + '61e451ba05cc7b382c25248d5ade7af1c7304f26b230f60fc73cb1655175a8af' + 'db4d0734f001ee35661433b2928526482079d11d485be8c8fa3a066f12e4b213' + '5d738667bdf461d78466d0f88f72d10163ca7e7f53f0e36ff4061e6a15824144' + 'ca80aee2'), + 'out': bytes.fromhex( + 'dc650d9efce2e1fdccce0e0631c7513ac9cdc3967d66b91de7899ef244f76c9d' + '211b2a01d51da32fd1cde8ad79e93b7f95e74125443a30c07e2a51f7c7f0c596' + 'd13b074003319a0d4d73869c06e6de24'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('fe195185577caffc9b3214d0e82bef90ff91a1e2164ffec23bfca96b4e2da6b7'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('2eecf6557ebe7d63d7e8c8e2d8c63e73a4a626a4ed7be4dcda98a5ecd3de0a08'), + 'rk': bytes.fromhex('c3bec9b4b5453deda20a3b8c88a4b1f99047b74ba2004aab2c93c243088da713'), + 'alpha': bytes.fromhex('5a02fc4ecc2201be9955971281892dfc183eeab06488d07fcdcbfd5afa8a462e'), + 'cv_net': bytes.fromhex('fafb1a6b93bb4868690dede7f7d76e3639ee37e9ed020e166c2eb20cdf3feebe'), + 'cmx': bytes.fromhex('982d0cec5df92480e143a33b672a5fcc9860ab0589919beb24072b102e01802e'), + 'epk': bytes.fromhex('d5d01af305d54fd71f9b0eb44f44b00b61bcbd8cb0811d70f97af5a9e9a09225'), + 'enc': bytes.fromhex( + '67884b80a71f55d3160a7e8026497c433aebd9f73b9499e3eebc19b48268c32f' + '3d2303bd2d47c298a29b0ca51678b7f416ae1527c9e297beb539333cbcbade4b' + '0358c9da9be0c8d23bebf6b01cfb292d9071307e41c07cc27027807dadd1b729' + '144ec0033a6d31a7a3969e5ce0b0217768a3a8a746df3c87967a378b98ae200b' + 'c398c17681916fb41c702966e51d61c09711405f2056053d98bbf97641b232e0' + 'bf789215e0f95d11e10fdb1a4c1893d370eaf1784750132d2a0206cf30f98d66' + 'e832fa03305f6f06dcd4f084dd5a70d62f5e9170062882ae165bfc8878842c03' + '64b97575e72df61bbcccefad9b85a68df47a984761e6a0f0c792ff89208bfd45' + '1ae6ca32eea30eef4f8e23e08246cd848d335d2d86cf44e6e3348e3cfb8d35f8' + '609609ff5fd4469d2dbb38a5e204f4065b3dbcb1e1835dafc9314a56aee71278' + '319ad917fe547eaf4b6105d8d3b9c7680e3a1ae426d952ea90fb8cc013d7ec47' + 'fad46d554c105726d27496d1af967675ef665922c2fb647b29bba606da802cd6' + '36a54b3dd42a19be2e6801003a7765b9a9b466ac85040fe1479b1fac30e47f7f' + '349dc867f0eecbd693128975dc1fd4015a4c253ddb5164fffb0556ffc77db5a2' + '5fbfc692ac6d710c4b96050bcf56a4a03ca881c2f7901cec7b878ea8c056e3eb' + 'bb8d40b9ad16ef66fb8a48793f27d54847b020a2922a41297475d2d48656ba2e' + 'dccad8140c68014eafc307bc1446ffc8549b03067cdfb52ea7763f4eb0e337eb' + 'd22093c41aea70fd5b5d8b35eeca6d15b876b8abf01c4a8192ef3961a01eefcb' + '70acfd14'), + 'out': bytes.fromhex( + 'c10219cf368ddfb680e526e11a1b5e7359215b84e4a64d57ba00b04512a717ae' + '4daba78f715883e538483a46907e77bbb113308e6d26bd69ce6ce069e055b685' + 'f3e36f9e0c14a5d7d1abb77b23a7f5e0'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('01dba523114310d50eb4e53c91820c72a69eb9152c87d5f109993481ba973971'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('93230114c4c52e75ec9dc038aaca803fb603e93a97d93fccee4828efb45a153e'), + 'rk': bytes.fromhex('9d66f8dc6fd1a77a333218e706d32bc39cec8762d1d03d1d1875618c654d0e80'), + 'alpha': bytes.fromhex('7cb83df4bace45f805b2b3fd11cc619e4fa3680bf02b1ffab81b9597307ba50d'), + 'cv_net': bytes.fromhex('e096705eb994c3a642b5ad9cf6101f97ae21f598f6df2dd92b4065e0aa410f8a'), + 'cmx': bytes.fromhex('1fcc601ca83b576c969a946cc0fc839fa5f4a5c79028e401e57ae28a876f0421'), + 'epk': bytes.fromhex('98e9e62857ecbf63fa5ac554771da098fbbc0916b229581454bf2ed4110e9812'), + 'enc': bytes.fromhex( + '3250ad1c60f1431694c2cf131eb22762386d0caaaeab3e8e3d204098f2ee9a7a' + '71b1c0c442b32812f09715fe328fa2be96a049d0551cecaab3f53fbf1e06c67f' + '506b68987986448df8fc52738722d4c6974b9ded1980488e7afe0ada81ff9ea7' + 'c9b1a487735c5c793b6942e61ccb4c60a32dc3c481112d781948db61130f4af8' + '21eb5cf087f18eb64899026a14d5841db228ce27c9496db2272c06b2d9f97fcf' + '9070358c7627a5711e34870510c92895761f7520a3d8e465e8536694f54a5352' + '73390a4aa53aa861edadeb6bd2cb490615c0c9a162ddd981f96d968ef7957e31' + '2c7da312c3038944aeeda1f111345f57557e103f8503d5d6144a0ebdd873115a' + '1bff4fcb83791d5c4f1c5d21c71b316f2daa868fd2bd100b82c3ed86505db9c3' + '9a2f2aa618eb77791b4a10a7e5a8e59c63aa5cfa211591651af39a2208fffb87' + '2b4d42e0183fd8a4a7c00575cbd5f3d6ef4e0ed10505d8af5eecc75bb8b576b7' + '7c8ea652f6454151666438bea8c325ef65a8c8d2bcf262be8d11e6e2aae117e2' + '8020b31faca1ea6b56ee0ed0eaa0d55e10242a77a3b95be92597ef8b2584d9a8' + '5f8f9f0df5c142ae50b106913c0a64fa2ff5eec1d2c475fd2e82f57fd4512160' + '60cb7030e4e5696d1bb5e33c5b2065474b596e55ab818cd7a5426493618f0775' + '7c13382f6c50931230a0e9fe5f1bbadc8713c057db04b7c8e3139a0eed550bfd' + '634533e279b9fe9dc3ecd6489119db4e3181dc912c567a29cb237f940ccefa77' + 'f01c3c5e835a1bdef8596348fd6070eadfc9773d875292a5adf3baacbe1321d6' + 'ab6c75e9'), + 'out': bytes.fromhex( + '9e205080da9edb1fd3b6e085573af34227aec4ecad864410de66490b60c8a62c' + 'd4a47033b4e4cd8588ab3179001ff4c2d740e0a6d3bf07e6876385a46a5ef69b' + '65d5a6779a8f93ade6be5d06e1b562e9'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('f6acf873f135d7e04bf21774693489e45932921041c3199618800927f88314a3'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('a8f517b0c5ad9c9661b5cbb54575e35debebf8494330d3de71b21d35f8f18735'), + 'rk': bytes.fromhex('d9580d672676562530ffb3c2af3c930d51486210c17ecbb0d2719f37fbac06a5'), + 'alpha': bytes.fromhex('b9400ad4f74f9b3bab7dba7490fe19e1658f376c4d05176f1f55a46c98ee6d17'), + 'cv_net': bytes.fromhex('434b0e3c18eccc0c9537b2182cdc06f37f567ca8ce83546bd00c5d2d0ad8f098'), + 'cmx': bytes.fromhex('7b45c8f7a62254eb30e6d0d650314276487a57d596e9fc2876f3768bc6cfea17'), + 'epk': bytes.fromhex('9dfbabd58f6d0efe50d452a99ef1e4641013f9e06927354f00de88939ce87486'), + 'enc': bytes.fromhex( + '432df485cecaeb63e5951044143da40415e2312a340e4d907b3466f7393f22c2' + '9aa7cc39d3f39fb0e33dd6ca0a888565f112c3bdcec136d585e399637384cc4b' + '3f98e55d2eedc76b13bf7b8eed1cdcbe06032a1be7a6c0fdff3d53b745a93e74' + '79aaf87585d9a7c05d8243be062d5a067e179fd1b1ca433317ac6152bc45b72a' + 'edb5cfed7cabbecdbf67f51636de3763d597b11c6c2ebfe15ab0879293e96320' + '652732a93fe627df9760c095bd72cec8e11c3a007148e4d6b787a1c4ebb990b8' + 'f5c2ee794b3feabdc443b21e50262798dfd4bc2e7e20be77daf4ac1a250c38ef' + '3fbf43514c62bfe2c58e17cdf3f6a3209ece9b38052a9bd5b396a2e817b80bb2' + '70646ff00cac2d6e03ce396809dd8f71d4f943f2d6c11cd9265c3a8d3f51503c' + '91d5f028b5cf68fec248621f38a17a71dfee52e25ad6660b8a1825ffb9f8fbbf' + '391e4038d4ba51e06e38972ef597ecfd62bba8a43fd92847b9bd56dc6a1370d0' + '463e1a70642098d68b673d530f8400ae5b083506db5b6b613740c371bb1e6180' + '9398b72e1859b3d723ca8bfd3ecfffa22f14838a0d9047be360aa795f0f60a9d' + '368ced599ca8e899e3c2313c08a2162a5e748168b44f5014564e34b4679db0d2' + '9f9ed884b31884ad561c137feab5e94f2abead3a4f7845293ecc8cb55c33e995' + '4b82fd4e5db3f3ae072e542049249aa55b6f9d4b779e22d305b6d722f11ea90f' + '47931532e763aac84f8475e3f16fd761c51a1c1c6c2f8d5607a9b8e878451825' + '5f1f492083ecfdb8ee096407cb960f85594908801d0e158bc1706fbce13d4435' + 'ad4b716e'), + 'out': bytes.fromhex( + 'b9e292eb7b5a72ef9ff2eb20781bea6cade7b2e08a48c76b16428ff68cd5958f' + 'a2fc90eaab442d6ca2308c79b4f6c06084fdfd95feeb6368eeb8104fde692e7c' + 'f4fc22fff2a6169052589a0e90717198'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('ea010bd93d0fc3dab9df9dda4c97d5eb97239ef14125990db577b7d2e6255a48'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('8be3f4d69956566229a1c9eb93706d6cfacea806d84c10917f74e188a9cc943c'), + 'rk': bytes.fromhex('80c9e3061bf4459060267edf1dae4c65ffef5d195877f0db23731543663f2f82'), + 'alpha': bytes.fromhex('f082eabd7c9c5f61b451344c08464e50b80b5e37aed95f2f91c4b72452e5f114'), + 'cv_net': bytes.fromhex('8921d8eb9dadbf0305ef3fc6181aab0eb5279349e6230a72fbdd3527c1a7e088'), + 'cmx': bytes.fromhex('366eba72bbf033716b9c8bddfcd0d1e1579beb762c75c20ebaa1889de454a307'), + 'epk': bytes.fromhex('4d70467ce11de44d408fa3d70615365116075dad52a88ced8b626652d10f692d'), + 'enc': bytes.fromhex( + 'd1c554ff7a8a890588b74863ced1524c9c8c70f7b40596869c694df71970ff09' + '5bf8033f956c3f89ca53ddc6fcd8b55335483a4a7c93431b739d66ed1531a792' + '0ba2a7067d21ebd12119560fe7de48aa729e96daec16e4b7873528252e2f14f9' + '06851bd05df7a80745fc83ba8658434054063536c93c9947cfdd143c9f44d02d' + 'ca9fd3cdc8379ba1fcbc985220ee2c91210f33f6a6f7190b4a48f9084a54ec55' + '0e6359acd7ec940d1434ed6c6b56d5e297592f4b03925e0e5fdd646052ab458b' + '85cad33b27ab5f5bfb719b92010f58b136e14672bf4e5ba5d864979fbb50fa60' + '965de41f9d605f20a42f23274dab99002bb469a72f6a89e77417abdaa1a59b7f' + '7ceb374d3876496359672a55ba8fb366273041d48713a06177eec5c9f5780184' + 'e4ce2ac57a6d54a4e2092ff8216786536d2e142bd67433d35278b459cbe40f58' + 'd4260691e19b5233f9ebac6f4f834c58fbccc3a6f2396f0644979a9b9098cacf' + '06b96fcf275df4b4e9dd927a8be3fbcd24c1e16e0fc8df973034691989d10057' + '99e2242496d3e1654015479eaa948dc5bae0c71f8b7f3105ce3c77020b7da5be' + '55e55d5ed3fcf764f0f5a405fe89f3470ad8b2d912efe99c8923a208fe6ab155' + '60a246bb18b01b1afe31d3902f2ccfae9c2fddd346058ba94b82f5baff187576' + '3b3befca7fead99325f80cf6dfcc23723356b855106fdf259bf7fb8504826ea4' + 'cd29ec1e695f3049c24f42cd919ac3553acfe75004febac20c35d92d1a21c12f' + 'b6d906e18741fb8bbfbd3dbd1293fbff197385f55adf1e6b3ef3af80edc63110' + 'caabf9c3'), + 'out': bytes.fromhex( + 'c17ba0c208ed0dd0411da64971b00cf404eb980599c45faebc6b3f1cbae311b7' + 'c262a8a222b20d386e990c657dcca6471e79fa5b36edd7acc40a6d5294ef7aa2' + 'f8b18f3f7566218067227cb7574ef6c0'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('f1d5593b77bb5d5918c0bdecc5651d301e6a7b435461809626a741d119bb0b35'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('1b54c34cc8292f6b79f51d2f91b5c9bda3627beb0e675add3fb37b3ab939c516'), + 'rk': bytes.fromhex('4e3b84aa7957fa0a0ba3c22782067599a2b70003878393f914f07f19eeadc832'), + 'alpha': bytes.fromhex('98e18b95741626e10f4872c434db7ec7390750689b51803adcf291d1318d4f0b'), + 'cv_net': bytes.fromhex('22ce1208b3d6638d0cecce265a815cdb57d5bfaec4bac5a14a2ef992417c303f'), + 'cmx': bytes.fromhex('9f65f0016b01015217c5abb05fcae0b1a1d2822057add19b35076b2e05f0192c'), + 'epk': bytes.fromhex('7ab81a60e6d8c6a2490f8ecd724c46711c542375dadfbcd1e5c70019420c2282'), + 'enc': bytes.fromhex( + '6c802a261b0941f99873b3e209466b249a272a139963bafadd71cc5b64afc98d' + 'b6f2706189467a74c05ae8a02ad328de8aa12d4e5dee060135e3b94e1cc0efbc' + '5866c91b3301724f7131ead81efd7a0c520cd1474028e3407488893b6b45b9de' + 'f8f753122f7ec2cd0c49a3eb06b64d4d2835e94c380051af5486012f401b8c81' + '4c6331b976b0b65527d56e136655766cd08084f4fd7399a17171c1879330bb98' + 'afbb5cc756168c44e4b0df9b52ab7fa35bc193c99f5ce6df6313e5da0b445d94' + 'fc12f631a3bd231fdf0c0be8fd2cc98df50b4fc5c4853177f4f22e270c59c6d0' + '7556ec5f5e82244bd5ee9bc148ca02daa3ae2bc460c65ac73b0d5a803878ec6e' + 'ba1e91ce0574b386d28a1738d004ac2bd93f4c9c1d5f90fa1e6ade99a87635c0' + '711a7f740dcc0c45210f06adb4ca90f54e8668fcd1a45260de2f3fd967ca4986' + 'ee9c90767f9734e448061d72886f800b20a783989a33b46c960078fd5beb0638' + 'db8d24907b5d8c15ee8eda056fc187d8eceb2d1245c5f7cba5b98b9bd1b56c2d' + 'f35757f87ed07dc5e6214ea25f1d8384afc00b60894fae3e17ee619975794cdd' + '6de4eb21c3d92d57d019b2bc3c98f08d35c1cd12382cab10f9f7532736767bb2' + 'a2e8cf5c6d51c8526652c00598e97695d8ba522dda0c50e8eb1b446e6d00c2ca' + '820fddde2c4c5b4902cb9aff51d7d102cfa7bcee0202fe0d958e88a00373d5b4' + '69af521e264d6b6a201c0f6559d89884acb4a481d9b56fabafb4e59c70d846c2' + '815a090dcc98cbe5a7d0cfa5287d9315056581cd89667a9c085c6e77ab7577af' + '30ed55b8'), + 'out': bytes.fromhex( + '9644bb917c9eebd52f057e1a863889f4992574bf57b5166073b245f2aa6bbcc8' + '4dca318ea3d0b80e193e382938c8b41fcec4757929dac9d443c67000b29f7eec' + '6c7ce44a19afab60dbed39300731fecd'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('b7de09450e2fed78853c39a0fc18af601066bccb870003c4db47aed37040bb00'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('762eba63803d81c90d7d38e08f9c62ae3de0ea98495cebfbc212fcb0b625f231'), + 'rk': bytes.fromhex('de1c617b2474661d10fa5e472538f2c289ca63a39c637f8988c3532172364816'), + 'alpha': bytes.fromhex('526f668e2286b53400e4f4bc12fa99559319be6d14f15ffc00086c7215b9a314'), + 'cv_net': bytes.fromhex('a062251a60672e947dcdb248efe064196c2f1ed962708672f67a28c0ea541d1a'), + 'cmx': bytes.fromhex('e0e01ee861405e4f11b27828183084a1a76006719679e8dfe05c06b2d9b9c81d'), + 'epk': bytes.fromhex('8f9acad54e4c4ceb63626ce76d7dccb975480934ae7348137d01f98636c4bdae'), + 'enc': bytes.fromhex( + '378ac9089167edecc577b1b28c02580ca55f8c64acd6a747eb21a9fb59375b70' + '7ab5ad0cd0a7b5c59fae6fd0b8047206d97c59fca7ec148cf31907bad0080204' + '72ea9a00c2df60df5ca53402671edd36902573d6a91d81b10486dcbfb5f49736' + '858f913f65233143b58bf06aea901994440b94d6b1dba85be0c453c45bd8a88b' + '4378559d4f68cd5e65ed2a57182adb0ae93728c912cb3ab513f037f7c5216095' + '4db465d49eaa6c6f3025fbe703c37b4c9bd0d354bf0016527a69c64c2adf4e43' + '0a4ceabd84b0f021df50cd5f40d197d07e414e9e9743ae9350f17d37abd688b6' + 'a2316a63d6863972f07b2a400969f3e558c9ebe375e31096fecc3c05c8e55f83' + '5d7d40a7e1f9b26d4f8a0669a5eda32e7c7317c5fc9799986acd96c52b565094' + '9d44932609978ef3ff85fb9f62072213d493b5a6e8343132aac03396c706de43' + '11ebece4ac91e1b6c361a1cc244e8af083657bf84bc9ebb09dc617c339c23e1d' + '7e763fd1413f0e5182ee523b6fc3438eaef2ba931574bd72dbd45ac6eba2d161' + '43ed70093c7e2fd7e1db14054c9cfb7ec6bc89f3bb7e99ab9aff08467c624072' + '5ed0bf560bc30687921500b4b4b030984a2d3b186517e7d19493d2ef6a566711' + '153c020b3b3859a4eef0929348016997139608167db4c84b5854869454548357' + 'f324dfd8ee8c4b19b0ce6dab70954c87160c0f9828abea8eb05841c08646d725' + '32ce65bae94ecd1a48f3a0114b65eeee4aec7003687ca8dd28800cb1ce80c759' + '0b1b62becf1be211375b4fe438710d38fbd7889096995e35347f91eca6b5e8e5' + 'fec6ba19'), + 'out': bytes.fromhex( + '88e413bcd8f75391cf3df89bd64ce8dc7a0d741679223f02c8546d50856a3cf4' + '48365f976b567eea5aa1219a66b30eb04ee2e3c472a6e45aff2ea22b0eb1d14b' + '9e6fbb2a1b5e699d05e3036b28d832a3'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('211558a82b0684b453222b1171beef68aeda93b143b9368b5a0c40b1df7322cb'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('05aa1878dfd335dbce602e1a8cd23986ee6a85c83e77e886c6acc5f2330f682c'), + 'rk': bytes.fromhex('4e54d29b80fa7f535d2a8c5a2784f7ad152c44a2a87b2d817ae3361fccb0d4a1'), + 'alpha': bytes.fromhex('f133a7eb65d87ff41ba8f909d0fac69cebf863f40d3225c559a823def2772b04'), + 'cv_net': bytes.fromhex('c3c2670c08676d45b580a8ccfa206e3683feb88a5f9406e20ed30eb355190409'), + 'cmx': bytes.fromhex('057543e8f38759e6cf094ccae696826f4eac83e64779ecf3f74ffba3b6a41c16'), + 'epk': bytes.fromhex('7aceaf5b50896ae5c8e5856313b0313b9257e89c4140feab46cfffd2261b2a81'), + 'enc': bytes.fromhex( + 'd0295cdb43aead4bafd89cd3e796ed7d205576af427f1665fcf890fddac1d94a' + '1b422fee2b1a39631fcc49056bf3fb333c2dd9aa7096bd8382c48a614be6b35a' + '5f236a6b3a7148d2c2259be05db67fbf008106e4277f39b8ccf479e4c132e4cb' + '6d38b956eedbf98b1f33ca2da57949dcc305460b7e279cba6ad6b189e0a74dbe' + '52ce18f32fc9096b3eb58a71637b472b1da4d0359a654101fa0ab40412212134' + '903ee4a996d877b43f292381c01919484a54d927dbb6bfab98fbaf4a5d655241' + '4ee223155459322855ba6b4dc8d28336ecc9b4ba66580bfd93541f0d2100ce6e' + '272ba130a05d73c7190f11148f4443181e6ef2214e96af0a4a79c5c61e84c946' + '2aa6a4b552f6364051a87c674e9306bd06256c2f3cfb382980b57cc9f542cf24' + 'b122763931c17ec1083b2056a55f50383421e56d9c293a919c40684cc9d2c60e' + '4d0b83a1b80abbd373e35028cb01e11bb2eb6a6dc1f4210090e1edb8ba318b12' + '56081c5f861ca3d005b72fb3fef89032703c12a4e0f4c314c214e77b87a12af4' + '2b72289d61334aac4e6abf13ee47ebe459ec2a5a14253a58687c7b52fe67d919' + 'c2484f933374e09d746da0d16929151a5fd22dd39955fd1feebc7d648f3924ef' + '93b309990d9ce740d388caef3e0f5935e34c17670241393702f1b8d9fc7efa38' + '83d67eed63b477887c3dc8356fdeabd11574b108a06695801457bd53eb9fcf2b' + '1360423b82eab0401b89fb956f8fd2615d037d2380b2c6e1cdbc33c3fe376601' + 'be0eefdc6e98e5ce2929cd7e7c70ee4ff8ef7988b57e4da5b5b17b5da29fd03a' + 'f6d75d99'), + 'out': bytes.fromhex( + '61a814cb4fbe7c77bc339a4f3bff21ce26a9dee3c8ea1ad8b886ee424601f976' + 'a956e84c22097081763ed29f3a8b11a8a88824015424c29958c5d8b7cacd0962' + 'd1e9310d25cf0a2694f883eb0007cb78'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('c857fac23f3c2410be8c8a1000915edd2e1b781493e0b4e4a07f539da5f9355f'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('80bd69243039fd9f893a1db9bfa2a90bb42b0435c68a4de69c44ee96397e7217'), + 'rk': bytes.fromhex('b75f29780411e873a54f66e2196e6dfbb2692b74f880a0c16622211ef2cf690c'), + 'alpha': bytes.fromhex('06509facde7313b6f42003afc3ada326f487c1bb51daede42037fd2a1c313106'), + 'cv_net': bytes.fromhex('84f5d51ecc7d7f6861c295d83b4cfd5a12cd25c97be1d194cbe4eb048d0764a7'), + 'cmx': bytes.fromhex('b3fd458b9111b4944613a695c8396c19be004562e41aeb9e2d091e1457d3f83f'), + 'epk': bytes.fromhex('be4d055f42218ccbe8be8bf130b950fe6672243d49d2ca91a9056e08e5554f2e'), + 'enc': bytes.fromhex( + '88b493cbe854943130f7d811e2003fd70fb606e12ebcd3fa5e920d949743dffd' + 'a421643863cf8e51c71c894173b213e8d59479674fc1a4493397b14bc81467c6' + 'ee4f97f29a938bae7646b89e8900f0d9ee043b1e19bd0aa56c14496ce88794a0' + '59723ef024431c0279b49fceaf6bb81370998520a5c710f1349e53dbc567463c' + '339553e375c575eea58dac685350c389b3b1658d0221432ed3722c94ccf5d6df' + '0fa7fe60a1b4615ef9b7667cbb9bb012f1a4fbdc54e47ade84393514fc4ac359' + '2159af250081a3a8317b9904928959553224017b709f7819e1bc676baf44dc51' + '1710988d90c2dd14f8fa30e81efea9ef7ff3d748ae2de43f0ff8726d88f140ef' + 'd178611768224388f62ad1cd4893650d352d1fdab6c7825e1e6fa08125f9cc98' + '862eb48fc4e5de2dfe7e5297a7ba2cf2ccc94b37d06fac064c4a4a20d4c61352' + '2ee7251fd22e8a3cf6ac9cbfdb631cca65ca201615d10637fc5326ebd1732e44' + '1f342bfc5fd510e99a83ecb10a1e5e1f4cb75e2062f851de7828f317af783321' + '0459a8784a52d01a5405ad28ae93b509d672d3376c7cf1386426992e32fe1191' + '69eb5ceab02f44d1bf2fb7c1d8f7d9cb5edf47fe68cd91091e34879a00a834e8' + '77d8ed8841ae776259ae2225f4d93614896630e74635655fff3eafaf3317039f' + '53afae9aed77b71cc33098ca84bf8df70d7e10133ac5b40f05fd1b5a81eb3772' + '1e3448d624ff301c69f69cdacbca8ec077033939af9fadb05b601514b024f476' + '373afe9dc31334eb2afffeb47bc1b79245d064324ef1e6185bdf865ea71e43a3' + 'f4d5a341'), + 'out': bytes.fromhex( + 'c5153d598e573e369a83048eeb7f7a33fbcae9fc38c01cc86450cbbb79ddc15a' + '38768fe782efbdc2d595c7ac047669c5da4f12ad60f8e83ac679c6931089304e' + '7e00ed59ef7aa2943ed9b57e6b5e6c48'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('fccf09a55f9a8bcd8cef78fda81e7327042fc4b5cb89f98d082e1536bdb134b8'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('9626584a16d66fe07ebf4f3c882686e408209f3b0466dc1acff98bdb5083881a'), + 'rk': bytes.fromhex('3d193701dd72c6e5b91c5ce173283391a9b838db3e322f8726e7b9f70f8f0830'), + 'alpha': bytes.fromhex('ff5e8f768ac3da0c5fe125323d57b107348dfae35f7814038c57bf9b72a50229'), + 'cv_net': bytes.fromhex('46877a8ce2afb3ded4da748f3165368d1e6161ac95fc40c3841a093107a3970f'), + 'cmx': bytes.fromhex('959c475be8bf479932a20fc31ea661c23c47d5f4ca6a9634e9efad8da528c70e'), + 'epk': bytes.fromhex('c5aa46b209c2bb664a085844288d2712b1358c0e134cd6de9e8341bbbbf3bd33'), + 'enc': bytes.fromhex( + 'f3cee24d5ea281f27a88653b3d81079e339e6c54427c88444b9843a4818a6dd4' + 'e2f0de12b80e02fe95801d3cc2032c68395625a84763daca66f4803405ac2633' + 'abd46fe0b5b2d1f1ddffe1b9e459e5ec55562ace3d4a1d282ffe55bdbe5edab1' + 'a09efb2556d3642922c25dc9827601a6257ee34d07f03ed5495e8f7a5e808e4d' + 'c63b8cae4755079f99775e7ac7242cd3793352a813c13381fb5758d798be1899' + 'ef901d6c715303e66ee7506ca15d84a78461518aec81cfcb820c998e73838213' + '5c99d9daac438d71d24f732a92d2e139c824650cc73a8cb0e3c0430cfe46d375' + '3ee7b8ba452fa54dafe52d84946b9bec8c42d2d9e434c2ca62fd6fba7f1decb5' + 'c14de077b2aabc1a83e80eb78c300fef35e0e781cb0d7727bcebe09efe1f0985' + 'ef9cf15ea4814d3a7836207ee6769ba72b5ca957100739587c1b9fe9473b0f74' + '05eb94e2c5d4b3f4ae886815da966ad7338529e05cb1a1733e6266a530c4302b' + '8a9c3601f7ba26ee9756938983b7bd41b3405794c2768c6c9dded0500f4dbdd4' + 'f0eaa9161e553674c0791c13ed7f8b233778b3a93ab366166bb0fb2ab3a03e47' + 'a5decfef4c224640458d5b4d85cc91edcb799e445b1331e49e0e8325c41103fb' + 'd54e4dd45a9f71c445323e99a65db21e9aace73e1cf8d9b91dcd481714bd8c3f' + '133132c30a3f3613eb6b08d905f104244c3952b953439c237c59dec6596164da' + '45d3d456d8c283b8759f7367ed232c7b3e7ea23c5d401dc09f3a8717b8e3a621' + '489c2095c1198268237b589ac9831f43dc082f14aea899d807ba60ffeb56e7d5' + '6256542e'), + 'out': bytes.fromhex( + '662b7206d372e142414db811f3bf16a63147976e8ebba60a006676c92639634d' + '65581d26fdeada20db03b26b6ad1dc617daf3c4b26b177eff9193ba6c8aa8304' + '3680feb5f3e25273658ccb5ec54207d6'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('6701cfd14370c25c7bf281f3ac777386621102d8ea16e0e124ca72c49a08943f'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('b717ab43b4030e502789837104b0f1d17c52fc85e9feb67597cc37ea6fefdb06'), + 'rk': bytes.fromhex('4b383511daa4696049c4a62841907f4b2a9f73391f399fb5028d8c4f3bea7117'), + 'alpha': bytes.fromhex('e621c7864db5823238401a62fcaa693cba9b0a8d207b0e43a34e9e9ec4d1b536'), + 'cv_net': bytes.fromhex('b7458fa5c8f2a3e9ca396d936220f7dedb5a8461d5964525179ccef4e39a6f09'), + 'cmx': bytes.fromhex('de7008c29a058bd3ca41759f3d3d45da1b7aff891e79abcd592004437a7a0617'), + 'epk': bytes.fromhex('d1136d16826a6ea26d99f2a54ff4b8f1c3c6c615d8bb325ca798eda9de93113b'), + 'enc': bytes.fromhex( + '39d375985cad35237c13c10db3d8b11f8670b49a9de9dcde98ce544129cb83da' + '345b5393e2949ac27d6efbb02b37e23a1db879fb4678f38e46c2e6b9925c4c04' + '8fd9193b4a46cfe0d6fb5d211e9f18955aa8c66ea362508abf10aa77b019da10' + '435c8a68dafab7a9647af4773a3dd01e36587d184c32efb6f4dc93a9f1ccec76' + '9c6307f1280ccca365cd736b298ece2212193de887ca3a79a064931ff0bf65f4' + 'dab1876205388fd4238f9a47f431d1534ba716de658671368260a1eec4606589' + 'c6a0727ab692d0cfddc5c4ca317d3e5e19109c2952629282d53f1b6fc6afd2bc' + 'f09e2d18a2510689dc087564813a8e27161a49569f311ae8672f648229cea4f3' + 'f8fe6f1e4b7f6b00c67c8ba9b392b66a3f275c6488c048e5cc50900551090a06' + 'b2432b717cf09a7b23dd9d872af1bc967dcaf03e50032ec55938aebe011fe6b0' + '27497fd69caeeffdd0b1dab2eadc28b362bc845a9a409b94ed428bd7601b6520' + 'e5992534ecbbff182de27914255658ac9f7cc6d30d5ec413c44a4f24f2d51425' + '47e0e27fda435b89008160eab76874016396c996a3a16d78a8903e1195bb8045' + '01a2053556cda4ae5ecfae92a02939f06b028727c09d874b5b403a17fe04880c' + 'e579b628e619af2f506e12207dd3a9cc5f0e08839945c42d912db74d325a1dc7' + '161e978b9ddb39fa6944f1776c946f2c7ac450e6e533cd8535a0150547746257' + 'dae1bae72232adf3eeab141f99d40aaacfd30f26714a1ac0f78d4793de75fff5' + '848b71d81440eb9e772dbde5cb3ca5d7c7b10fb39420b443a210abfe29d0ed77' + '1e44a561'), + 'out': bytes.fromhex( + 'aae26bb3b088a37a3442125fe2bc61d0699a77bc86b82a224096c82c60296711' + '12b3e888482afdd1085e11bfc08ac01fbf1cc359420bc7f99f9b47d1237e12f0' + '3757fa7bf2ffd0967c6e23b60009f9aa'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('e4ee1456a223dfa31cb714824dfeb95a2234ce811ea308b63a4fe4786407a33b'), + 'value': 1015000, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('e1e8dabd82a6bcba2107fc4a8be64f0ede30355e5eed5bd365db848908fda23b'), + 'rk': bytes.fromhex('64b31d813e4fa32ad2413109882805a1b6d825eed19dc58a86b4486a4671e39e'), + 'alpha': bytes.fromhex('f08196949dd1c2293d8f45a1919ba6d3d51e03e4a79ffce03df30a9aa6c6a527'), + 'cv_net': bytes.fromhex('0cb9920d9da9d1ab56dc2fe03fe7ba0e99d6680319dab7e79480b38ab781751c'), + 'cmx': bytes.fromhex('df52e0d1e9ef1a81c53c00dfdd98ec1063caeb40f796607dacc0a940ce3c2a0b'), + 'epk': bytes.fromhex('a9d9c634dea3e7077601a14ec78ed750260202728d517eae494670da0cbda6a8'), + 'enc': bytes.fromhex( + 'b01527e05660d0d77f0f23cd2192bf470ea7104ac9219ea44c9992ec5090cce3' + '0b930b56cf37e77c034b6ff110f09b8894796e5840309b92071896d5b44674ec' + '8af09965c555fe34eb6df782a992c3b1bcccbd6bafefda52095061d3f26ca90b' + '000e8096a350a00422d1f85b39c73ddb22b8138bb88ea1b5a69c71c51d487a7e' + '1c3b7be3e11c18341483e2df8151505c05eb3315d717b973874a22e2f9164d7b' + 'cca4cd6465845075084bc4647065df093ba4f521332196fc22488b53f22ce39e' + '8088cbff2b5a320c44191472c72d07d343e057c37ccfc2c30e44759ee53fab33' + 'c3c1a69a63567a862415b0116a6199b0d0a3155a72e9d67295955d835201b65d' + '3d5267bcafaaef444f4c7cabc2dfa444ca4589eb0b7a595370a8ffb172c47f7e' + '1a87745b78badc91840ec56ae11463b12b10450573891b2edbbec61c5e8b5443' + 'bf23feb0649c434860d01d2bc3d07a6879ef38a01d09de4b3a1b9ec173accb3b' + '682c6476e24c9accbd71d9aad42a1a52871d906b7878cd72d1e90b38665f1900' + '258d12da147e1ac2156af74bc8823b9fadb47ba20d369c5c54f575640abb383d' + '5723378a9357086241f43c01133081f7d6fdc3aeaaf4f763478c0f6ef20030a2' + 'a128bfef33b05ea8e3b9565c2b35b49745aeeb9ab43f543469c79ffb28f63673' + '645ff957db246104df1984b26de1ef5de5eb0e98562822c21750ff97efa716e4' + 'a050d027244fc2d76fb1e521547caf6f19f05ae5d6cbdbaef872a2cee284859a' + 'd173fe53d282c4598725a069c7490963b1524e1ca01bc9890fe42e58fcbdc80b' + '8d3af351'), + 'out': bytes.fromhex( + '892b762b0d42e42da9a64a52c82c1e609cb94efe8089c75c8b93cf85464748af' + '812446c0d38a737e2450d1ac9560b2a7bfca9835a3aad344c04ee19f3ab3e491' + '44af0486105188941f2e2294a05f915d'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('bdd6672d18bc9ddbe590b177c010c17470a5263b05c9b770448173b7a336debe'), + 'value': 1015000, + }, + ], + }, +} + + +def migration_action(v): + action = fabricated_action(v) + action['is_spend'] = v['is_spend'] + return action + + +def migration_kwargs(tx, fee): + lock_time, expiry = 0, tx['expiry'] + orchard = [migration_action(v) for v in tx['orchard']['actions']] + kwargs = { + 'address_n': ADDRESS_N, + 'actions': orchard, + 'account': 0, + 'fee': fee, + 'lock_time': lock_time, + 'expiry_height': expiry, + 'orchard_flags': tx['orchard']['flags'], + 'orchard_value_balance': tx['orchard']['value_balance'], + 'orchard_anchor': ANCHOR, + 'header_digest': header_digest(IRONWOOD_TX['tx_version'], + IRONWOOD_TX['version_group_id'], + IRONWOOD_TX['branch_id'], lock_time, expiry), + 'orchard_digest': bundle_digest( + orchard, False, 6, flags=tx['orchard']['flags'], + value_balance=tx['orchard']['value_balance']), + } + kwargs.update(IRONWOOD_TX) + if 'ironwood' in tx: + ironwood = [migration_action(v) for v in tx['ironwood']['actions']] + kwargs.update( + ironwood_actions=ironwood, + ironwood_flags=tx['ironwood']['flags'], + ironwood_value_balance=tx['ironwood']['value_balance'], + ironwood_digest=bundle_digest( + ironwood, True, 6, flags=tx['ironwood']['flags'], + value_balance=tx['ironwood']['value_balance'])) + return kwargs + + def _b2b(person, data): return hashlib.blake2b(data, digest_size=32, person=person).digest() @@ -316,7 +1112,9 @@ def bundle_digest(actions, ironwood, tx_version, b'ZTxIdIrnActNH_v6', b'ZTxIdIronwd_H_v6') else: pc, pm, pn, pb = (b'ZTxIdOrcActCHash', b'ZTxIdOrcActMHash', - b'ZTxIdOrcActNHash', b'ZTxIdOrchardHash') + b'ZTxIdOrcActNHash', + b'ZTxIdOrchardH_v6' if tx_version == 6 + else b'ZTxIdOrchardHash') compact = _b2b(pc, b''.join( a['nullifier'] + a['cmx'] + a['epk'] + a['enc_compact'] for a in actions)) @@ -749,6 +1547,49 @@ def test_zero_value_padding_output_is_not_shown(self): for _, _, body in screens: self.assertNotIn('0.00000000 ZEC', body) + MIGRATION_FIRMWARE = "7.15.0" + + def test_migration_transfer_signs_both_pools(self): + """A ZIP 318 Orchard to Ironwood transfer signs. + + From NU6.3 Orchard funds can only leave the Orchard pool. The transfer + spends an Orchard note and creates an Ironwood note in one v6 + transaction; the device verifies both bundles, shows the Ironwood + output and the fee, and signs the one wallet spend. + """ + self.requires_firmware(self.MIGRATION_FIRMWARE) + screens = self._capture_confirm_text() + + result = self.client.zcash_sign_pczt( + **migration_kwargs(MIGRATION_TRANSFER, fee=15000)) + + self.assertIsInstance(result, zcash_proto.ZcashSignedPCZT) + self.assertEqual(len(result.signatures), 1) + outputs = [body for code, _, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput] + self.assertEqual(len(outputs), 2) # the Ironwood amount and address + self.assertIn('0.01000000 ZEC', outputs[0]) + for _, _, body in screens: + self.assertNotIn('0.00000000 ZEC', body) + + def test_migration_preparation_signs_sixteen_actions(self): + """A ZIP 318 note-preparation transaction of 16 actions signs. + + Every action carries a wallet spend (one real, fifteen fabricated), so + the device returns sixteen signatures. + """ + self.requires_firmware(self.MIGRATION_FIRMWARE) + screens = self._capture_confirm_text() + + result = self.client.zcash_sign_pczt( + **migration_kwargs(MIGRATION_PREP, fee=80000)) + + self.assertIsInstance(result, zcash_proto.ZcashSignedPCZT) + self.assertEqual(len(result.signatures), 16) + outputs = [body for code, _, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput] + self.assertEqual(len(outputs), 30) # fifteen notes, two screens each + if __name__ == '__main__': unittest.main() From bae8cedcdb4b9e9bacc97688307b56fc16fd3af5 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 07:38:33 -0500 Subject: [PATCH 19/37] test(zcash): change is one total and a memo-only send is shown Outputs proven to pay the account's internal address are shown as one "Change back to your wallet" total. The fabricated, padding and migration transactions now show that total instead of per-output screens, and the 16-action preparation transaction takes three confirmations instead of 32. A new real NU6.2 bundle (orchard 0.16.0) checks that a self-send to the account's external address and a payment to another account's change address are still shown, and that a zero-valued memo-only send with the address the user entered is shown at 0 ZEC. --- tests/test_msg_zcash_sign_pczt_device.py | 259 +++++++++++++++++++++-- 1 file changed, 245 insertions(+), 14 deletions(-) diff --git a/tests/test_msg_zcash_sign_pczt_device.py b/tests/test_msg_zcash_sign_pczt_device.py index 36c62e61..a874c37e 100644 --- a/tests/test_msg_zcash_sign_pczt_device.py +++ b/tests/test_msg_zcash_sign_pczt_device.py @@ -274,6 +274,170 @@ ] +# An NU6.2 bundle (orchard_v2, Flags::ENABLED) built the same way: one +# 100000-zat spend, fee 10000, and four outputs in action order -- 20000 zat +# to this account's own external address 0 (a self-send), 30000 zat to +# account 1's internal address 0 (another account's change), 40000 zat change +# to this account's internal address 0, and a zero-valued send to account 1's +# external address 0 whose memo reads "thanks for lunch!". Only action 0 +# spends a real note; the other spends are dummies. +MEMO_BUNDLE = [ + { + 'nullifier': bytes.fromhex('0b396499eafbba166a936684287c6a9a93c35684059661a365e715c6bdb80903'), + 'rk': bytes.fromhex('0a1d22eb81961326e4d6c5dc1208f1e166cde2d52d7a4ab65b26c74d6281cd0f'), + 'alpha': bytes.fromhex('3b977876ce497d64ec92c71e7e8c0ba25c6e831636d6f0942b7cb944cec18f35'), + 'cv_net': bytes.fromhex('bd26a17d837f604b0834a4f6c4e0b04128d72b98d0d783ff6ad7087feb2e4d10'), + 'cmx': bytes.fromhex('9e926d6a1e046c426c00e41c618e42040dbcb8a4122aeaec30a1558b56e3cb3d'), + 'epk': bytes.fromhex('5d8fe257f17e600c74f9ba9bcc221c85c7074e1ef6adaf23ad884bdb52eea72a'), + 'enc': bytes.fromhex( + '8b0075c256cacbebdf9dcf8db1c780c3149e9130d03fd8b509201130604d5848' + 'ac64ec54a133fb72f09083efedd825a3b08f5f7bf5f54a85a2390ab2e37eaad0' + '28a3a245ce7279f4afe13e5ade9e6eda947119b68ebdc9f86fff852cbe1f0217' + 'd4119815506d9bf3e4530c80c357e0a4910392d59b4d531ff6c318de528fb6b0' + '5fae9aee00fcfe88661ca4b181a96b4255a0a81ad38f21f629e47bbefe5efd1d' + '829d93542f4d6f6fc8e9cc9a5ae91adeeb657c0b3427a86654ee502c944ba189' + '95d9bb4d40de6fbf581d4a89e407e27a968cc0c168ae7f3dcf5afe3272f1dd0d' + 'd805f01b367d93ffd8557e69d39957540e1e70e8cacb7d7a844ff234bc49c403' + '1f39598fcd696606eacbb267172c90f5b438dd4652e6f2ac83913bce4ca7d2ef' + 'd1d04a4fa7eb66f8342bc2b84ff03e64c21139de5c6dc257518c0d20f48c705e' + '512a474d25185e54187af3d650b56ad12539b7797550bb117d12ac6d6ee9c691' + '684fd3c86bbfa004484d6f08574f0bb4031aa27e0599e15f8ce2b00711281f85' + 'cb92e63fb66d0794e4dee0ce15b7e0c3851266ba8c49c9cf2abd22a488e95578' + '693a9c0d5e27ca808de4fc81cafeadbbbff7f83dbc4debba908510279941613c' + 'f9ee909eb5b922b2333f48422cd0bceb61625d21c6b3107cb676e60ad681c4a1' + '51c1b24734ed1f3ec096785c45641fdad75697abdf66e9553ec51af468f90ec8' + 'aeec7cc04e1f6c03656da3389a4fdb287bde0e01bfde5f125803f604a5f38d12' + '482155475a651f76c56eff1777403c5a48499cb196fe7bb0c98e59af720c6090' + '48939155'), + 'out': bytes.fromhex( + '93d14b5a4bc9dd72c1559c0073dd621c184af991a87c75215fecc816f20b83bf' + '3673bab907166ef00ac8b8f67b2be681ebec1b8f49ea0918a0796bbe4ae02925' + '8a6fec6129eb71fc611a7944b9625b7f'), + 'recipient': bytes.fromhex( + 'da973031634a8938ad1c480f978780693ec7709ba5caf58d8a7eb945586cbed6' + '45520f17387437bcfdc216'), + 'rseed': bytes.fromhex('19d1dbfb752c1bbc9ec9a5c44fd2d8a9d79c404e299127d3ad5f34a2b52f4a94'), + 'value': 20000, + }, + { + 'nullifier': bytes.fromhex('f8ba50efc52be70daca605a52065c1f1f58feeff3fdb41ce608a18e529326905'), + 'rk': bytes.fromhex('7282993aed2a09e3d374bc473dd16ec6a760fd8a667b3cb32521f50b80fc3928'), + 'alpha': bytes.fromhex('1cb2601e62e5bf33186b33ea9acbe76d31509cc5e5826ab7b549189b55cc903f'), + 'cv_net': bytes.fromhex('34fd5c6923a81270493d8d8e517044305f7595217d8e3bea2f473b35b0a78017'), + 'cmx': bytes.fromhex('9b020d8c509de25cb2c04955725669284951e081ca602d27b6986f4c0349e93e'), + 'epk': bytes.fromhex('85f4e893d37fbbee4ea43e7cb447c38d1f31669930f8d800e39d3fcac48ae3a9'), + 'enc': bytes.fromhex( + '54d68d62720be92d6c4697751bde5ab39c3de61aaafab88161765cf4aa27a151' + 'b0f5be29d7fcc7f30c6a9301cbfbc4dc348a30a8821fe56c73c8e9c799cf9f50' + '64f8c5778a218123bcacd4b43d971d2a96452a69de0c48b3d9b6cb7a11967851' + '04c7077ae08d0be113696c6f34c41f339c5cee68611856f627553f41e511ff43' + '90a80c74fc31dcc4d1a10b8bc02706ecd187d02aef6fc39f166815c7a12afd39' + 'b6fbde42959672fb508df5432848e5061acf62263046784e3f252a57604f4d3c' + 'c8a295e5bacbaab6f4b0f5aa7b3790abf576260b7d40da6266818f22b4e75a53' + '1b6e565a87ceb0f2a01d70c764de4545c4d5707394af31752b2c30f0fcfd64c3' + '5102558160449a78e3887053d63f015650dcc7f1f56114739bd6f1e730eaf0d8' + 'e2a5755c3424ba6b205145ded3620702c853226945fd8dfddffb359016c24784' + 'c8ce7edac0f02345f6240b4a055b712777f344e4f176082ec27e07ada09473cc' + '7868836a98f7f1de99cade6bb9c1daa9e54c591202f83e90eee76054e76b45e9' + '89caac1005cc961af5111a46c11a41311f6e94540e67f2005d618e731d4792f0' + '5b31ef94ed6d73216991c1ffbd0645bfeafb64554ad082fad7286af5e88ca721' + 'ebb6e89a8aaa097c33c3d3c00b645a5b6e9d892417624d9b58e5157c4f9406e7' + 'ec81cd89028d8969f54feffb4825017676044d5e3ae6603f058f772953c714b1' + '1ed1523d2aa37d36f51bec1a36268bb0953f6836534df86ab196929d5b916781' + '071680fb04d6d27f38399a952bcdb7a459f4fb74d5dfd73c6a0a51c58ea00a53' + 'bc4fb482'), + 'out': bytes.fromhex( + 'b95c75498fd39fe8ffb67803469bb183e6746b7126550c0d56f248d5a3bb2485' + '19ffbac31035d28a29b5295e929bc026f8f321bab87245f47ad4c4604bdd6efe' + '4f77fcbe2fdaa78a5ebd58263a7f6d0e'), + 'recipient': bytes.fromhex( + 'e96953bda73d114cb7bc026036b695c23fb2b80b0c34f217ed3b4e6fd3766efc' + '090a17663191a15eba81ba'), + 'rseed': bytes.fromhex('b7a8c118ceb43470d6a78bcb0eabecdf96b3977bb99506442df3c1db52a0b58c'), + 'value': 30000, + }, + { + 'nullifier': bytes.fromhex('a75bf6e26992c5a04856020a69caa517ba5b0f6dc026e8e8713ae606f2be330e'), + 'rk': bytes.fromhex('b0aba46130d9dfa7e5a1260d134958d5caefed021551c1832fe07cea52ade4ba'), + 'alpha': bytes.fromhex('a16c4408414428dc4e614b56f9bcb7bff36ed395ff25f556327ed877556fef3d'), + 'cv_net': bytes.fromhex('7cc907eea8e6bb1eec198132f122fb9ff7f0797df56fecf63b7d2f2e4fd283b3'), + 'cmx': bytes.fromhex('15c4fafb44b3aa5ef021ec25208136a785139f61a2dc69c1bfa2280e7dc46f08'), + 'epk': bytes.fromhex('34737234bf9190c4a909a96b58f66843fc1ae968851d9dcfe8b9d8a0b1b8042f'), + 'enc': bytes.fromhex( + 'd2c52957269904d95aad1058335d2b431d46e3702b28496752c5d07fb5fdac1f' + 'bbb6e798057939f1b732416f0717f72a037402e6cb6c520c785f87c5eea9655a' + '654936d982329d9f637998e6909b947ede5f3c0844c72d291607bc2dfc0d37e0' + '1e32cb8637244aa02c33ffb95e9b9b99e2936614cc8c95bed046b44fe18c2aad' + 'bb2dfa950de507fd49774343f24ae5329904e7e4b526769a7c3dcebb473d2224' + '76c480d96335b9f3be0e8ae62137d43dc508c7dbe44cd402514aabc2fa8fa81f' + 'cec38e74aefe8ad0defbcdccb091b50b27c0f2abd5622f3cf9d6a0272b7700fe' + 'df22cdcd208c0b6eb94f7ecacbf6af61be4d581efae66b30b7b39d3806ec247c' + '4e16282839725d0eb0836f26500552c7bd98795325888f8626f0214eaf429c6a' + '55cfd7fc564e468103fc577bcf69a5d9f64894262d80731629977972ccf9c962' + '871720149b9db9e4088f061ccae62619827e31b7ae7a7d878bc51b9d63e73613' + 'dd0352e4dffc62fbcf6435b72ff1c2460bf9c5609db734a59ab3a274e0de8dd5' + '2002f787135b9120c02947e62c0fdc4d633248f0098cafecfc8b5ff165392292' + '205c68d13f6e8bc261b6863770c1fdf9715ab0553f8cacf07b20146f815af9a3' + '6664e90df7bacb0ad412e4cd363cbb2a0936d89afd5a228687f9b192b4043a54' + '1341ab18a82405a57e24986d227d8e5c285c0708843adbf2a29293d30e4e9b61' + 'ceddff354e24427e6d2a34cbc1bc6057ec6748387305ecb9ca7b70926e672104' + '900779a167ff00024e3cc6e1cf05636eb7cfb34a2c289bab49f411a9cee2701f' + 'd44d2d13'), + 'out': bytes.fromhex( + '8e6250a506ea4b52188f10ac0ee0e0210ced8f7f8ccda7069f70ed8ed675a953' + 'cae9a37666f191921050a3da74636c907e3c7c36a75ec459255c9ed828718a8c' + 'b31e6ffeb3946f1fbcee60568ccceffe'), + 'recipient': bytes.fromhex( + 'ddae703de8aee25d4e439b8a646b2a5a86f57f802890996e2b3c1f41ff2348a0' + 'f0bb27c032b2e7718c962e'), + 'rseed': bytes.fromhex('2a6913ff3dcb6d496a92b8d5cfccd29afba7b48fc1e21940b55739c5ff950d6d'), + 'value': 40000, + }, + { + 'nullifier': bytes.fromhex('9c8426bcbc3202267606069136500c06aa2e40c378ba22a898aeff0626c75036'), + 'rk': bytes.fromhex('9e74449a938707718574712d19923a0a48820aa17f53491007f873fc3f902902'), + 'alpha': bytes.fromhex('a6e1c02c22a34f14a0a82c221489f26136081ed39b3ef34ac2df17c08aa2e235'), + 'cv_net': bytes.fromhex('15fb882089e16d2f696b2964c8d13fcfbbe66a1e645bf08afdfef2a15352a3a4'), + 'cmx': bytes.fromhex('5d491d6e064e67343e179ee680182a04eca8a9c73b504e17439f695b989d8d35'), + 'epk': bytes.fromhex('266dcf5ffab1cadd4fbc6f030b702f535c49b0971c79f6d581491b5e727eb797'), + 'enc': bytes.fromhex( + '8c7b608859f1c6b4a1b3e304f9ac0319680d53950d98ad1e36216abf62870bc8' + '69195aae61e811cc559a3a06df605489027763cc10731e262dc477c40589c9ac' + '6350ead8f31d6f37d8eb698c7afdb8f835b108d554e97753a8838fb47f0fc1cd' + '989ed1190517a4f455267458b8c9329f846bed3993db930520ab71ade3e5256d' + 'ac93ebdb7abff16cf88030df72fa90db30d3745480c914d501579a6ab93b8472' + 'a52137f13917657037fc458546a1e41af7f12d8b5b492e69c9d595d4e886627c' + '6656b5a3ee37ebc3689290bc88707c91e026d95859d88c5e2187339e4b3e6bff' + '924a0934468e64b86dec35b26c6d2cb750d13f21101e1992cf64eae8547ab462' + '9ad4c7e88264c4a2f04adcbd2cd050038239498b306572494920bdfc7a1bbee9' + 'b190ba82f88e670d7c690b069bfd073c9c4222da760bd347c4e4d321fcddc057' + '88114a34b9bf3b4054381c8595c7ec80140968741d55f1939a9121b3369afaef' + '4c5ce10e662e13b2c0ecd8deb4469a92b66ee8ec2ddc0c600700f1e39cfca631' + 'fef6920a7af021715fe51c178b1665c5d0a261dbcc5ad171613402ff29d883f5' + '12f8a488d23bd4401781997e4dc0d7b62b8bb66f64ace822c08177544dab0a46' + '930ab73eb589460e9e19a77eed8f0a494b92f41dc1dc70572548e755a2023444' + '9ec6d30af18d58d1f0a2752bb8d8a4d201232d3bb3ec3d6fe3fff3cbbcc7e350' + 'e65808cb45bd1d367e447b728ac6a1445a6c7bae6eece7e2b3ecaab583a9ca58' + 'e41cca24d08366be4516fea9dbce82e4de6845b268708b96a572d619e808bb11' + '06ae8a55'), + 'out': bytes.fromhex( + 'eacafd6869e89e8312a0a7caff1e8baf61be383e17db1e61017eaa5842091a05' + 'ec776f4b7fdf85b1f90aa0197118d355ed1f73ac5795f8296afc5f4b87916b2a' + '7cad4d62b2f8c75acafec1ed5bc5248d'), + 'recipient': bytes.fromhex( + '8ee82cdff5120a00cb089ff8acdaa517958971d69d1852dca01fd25e88609ff9' + 'ca7b154dcb2c01a8bc18aa'), + 'rseed': bytes.fromhex('337ce21a2c63debee0bfed8ad8c373d7d5f3fbfd44bed30f2addd92130ef5605'), + 'value': 0, + }, +] +# The Orchard-only mainnet Unified Address of the memo send's receiver +# (zcash_address 0.13.0): the address the user entered. +MEMO_USER_ADDRESS = ( + 'u1u2e4d2ctdr0sq2rtglp3myfzpewgtslzjss0p4jtldylakd8dm5n9rtflehnykkq6c0g5rf' + 'zrswl5tx02uzulwa4xqdpmm9pnuuslzs8') + + def fabricated_action(v): return { 'alpha': v['alpha'], @@ -1497,8 +1661,9 @@ def test_fabricated_zero_value_output_signs(self): From NU6.3 every Orchard spend is paired with a fabricated zero-valued output whose ciphertext is random bytes (ZIP 326), so it cannot decrypt to its note. Its cmx still binds value 0, so it pays no one: the device - accepts it without a screen, shows only the change output and the fee, - and signs both wallet spends. + accepts it without a screen. The change, proven to pay this account's + internal address, is shown as the change total; then the fee, and + both wallet spends are signed. """ self.requires_firmware(self.FABRICATED_OUTPUT_FIRMWARE) actions = [fabricated_action(v) for v in FABRICATED_BUNDLE] @@ -1514,8 +1679,7 @@ def test_fabricated_zero_value_output_signs(self): self.assertEqual(len(result.signatures), 2) outputs = [body for code, _, body in screens if code == proto_types.ButtonRequest_ConfirmOutput] - self.assertEqual(len(outputs), 2) # the change's amount and address - self.assertIn('0.00090000 ZEC', outputs[0]) + self.assertEqual(outputs, ['Change back to your wallet:\n0.00090000 ZEC']) for _, _, body in screens: self.assertNotIn('0.00000000 ZEC', body) @@ -1524,8 +1688,8 @@ def test_zero_value_padding_output_is_not_shown(self): Wallets pad an Orchard bundle with a dummy zero-valued output to a random address (protocol spec 4.8.3). Its cmx binds value 0, so like - Keystone and Ledger the device shows only the change and the fee, and - signs only the wallet spend. + Keystone and Ledger the device does not show it: only the change total + and the fee, and it signs only the wallet spend. """ self.requires_firmware(self.FABRICATED_OUTPUT_FIRMWARE) actions = [fabricated_action(v) for v in PADDING_BUNDLE] @@ -1542,8 +1706,7 @@ def test_zero_value_padding_output_is_not_shown(self): self.assertEqual(len(result.signatures), 1) outputs = [body for code, _, body in screens if code == proto_types.ButtonRequest_ConfirmOutput] - self.assertEqual(len(outputs), 2) # the change's amount and address - self.assertIn('0.00090000 ZEC', outputs[0]) + self.assertEqual(outputs, ['Change back to your wallet:\n0.00090000 ZEC']) for _, _, body in screens: self.assertNotIn('0.00000000 ZEC', body) @@ -1554,8 +1717,9 @@ def test_migration_transfer_signs_both_pools(self): From NU6.3 Orchard funds can only leave the Orchard pool. The transfer spends an Orchard note and creates an Ironwood note in one v6 - transaction; the device verifies both bundles, shows the Ironwood - output and the fee, and signs the one wallet spend. + transaction; the device verifies both bundles. The Ironwood note pays + the account's own internal address (ZIP 318), so it is shown as change + back to the wallet, then the fee, and the one wallet spend is signed. """ self.requires_firmware(self.MIGRATION_FIRMWARE) screens = self._capture_confirm_text() @@ -1567,8 +1731,7 @@ def test_migration_transfer_signs_both_pools(self): self.assertEqual(len(result.signatures), 1) outputs = [body for code, _, body in screens if code == proto_types.ButtonRequest_ConfirmOutput] - self.assertEqual(len(outputs), 2) # the Ironwood amount and address - self.assertIn('0.01000000 ZEC', outputs[0]) + self.assertEqual(outputs, ['Change back to your wallet:\n0.01000000 ZEC']) for _, _, body in screens: self.assertNotIn('0.00000000 ZEC', body) @@ -1576,7 +1739,8 @@ def test_migration_preparation_signs_sixteen_actions(self): """A ZIP 318 note-preparation transaction of 16 actions signs. Every action carries a wallet spend (one real, fifteen fabricated), so - the device returns sixteen signatures. + the device returns sixteen signatures. All fifteen notes pay the + account's internal address and are shown as one change total. """ self.requires_firmware(self.MIGRATION_FIRMWARE) screens = self._capture_confirm_text() @@ -1588,7 +1752,74 @@ def test_migration_preparation_signs_sixteen_actions(self): self.assertEqual(len(result.signatures), 16) outputs = [body for code, _, body in screens if code == proto_types.ButtonRequest_ConfirmOutput] - self.assertEqual(len(outputs), 30) # fifteen notes, two screens each + self.assertEqual(outputs, ['Change back to your wallet:\n0.15225000 ZEC']) + + def test_migration_preparation_needs_three_confirmations(self): + """The 16-action preparation transaction is three confirmations. + + The summary, the change total and the fee: its fifteen notes all return + to the wallet's internal address, so none is reviewed on its own (it + was 32 confirmations when each note took two screens). + """ + self.requires_firmware(self.MIGRATION_FIRMWARE) + screens = self._capture_confirm_text() + + self.client.zcash_sign_pczt( + **migration_kwargs(MIGRATION_PREP, fee=80000)) + + self.assertEqual([title for _, title, _ in screens], + ['Zcash Shielded', 'Zcash Change', 'Zcash Fee']) + self.assertIn('0.00080000 ZEC', screens[2][2]) + + def test_only_proven_change_is_folded_and_memo_send_is_shown(self): + """Only an output proven to pay this account's change address is hidden. + + The device recomputes the account's internal-scope viewing key and + folds an output into the change total only if its recipient is that + key's address. A self-send to the account's external address, and a + payment to another account's change address, are shown like any other + output. A zero-valued memo-only send that carries the address the user + entered is shown with it, at 0 ZEC; the same output without it is + taken for padding and not shown. + """ + self.requires_firmware(self.FABRICATED_OUTPUT_FIRMWARE) + for with_user_address in (True, False): + actions = [fabricated_action(v) for v in MEMO_BUNDLE] + for action in actions[1:]: + action['is_spend'] = False # dummy spends carry dummy_sk + if with_user_address: + actions[3]['user_address'] = MEMO_USER_ADDRESS + kwargs = sign_kwargs(actions, fee=10000, orchard_value_balance=10000, + total_amount=90000) + kwargs['orchard_digest'] = bundle_digest(actions, False, 5, + value_balance=10000) + screens = self._capture_confirm_text() + + result = self.client.zcash_sign_pczt(**kwargs) + + self.assertIsInstance(result, zcash_proto.ZcashSignedPCZT) + self.assertEqual(len(result.signatures), 1) + outputs = [(title, body) for code, title, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput] + expected = [ + ('Zcash Output', 'Send shielded ZEC?\nAmount: 0.00020000 ZEC'), + ('Orchard address', None), + ('Zcash Output', 'Send shielded ZEC?\nAmount: 0.00030000 ZEC'), + ('Orchard address', None), + ] + if with_user_address: + expected += [ + ('Zcash Output', 'Send shielded ZEC?\nAmount: 0.00000000 ZEC'), + ('Shielded recipient', MEMO_USER_ADDRESS), + ] + expected.append(('Zcash Change', + 'Change back to your wallet:\n0.00040000 ZEC')) + self.assertEqual([t for t, _ in outputs], [t for t, _ in expected]) + for (_, body), (_, want) in zip(outputs, expected): + if want is not None: + self.assertEqual(body, want) + # The two rebuilt addresses differ: neither output was folded. + self.assertNotEqual(outputs[1][1], outputs[3][1]) if __name__ == '__main__': From 1d3f92cc1805ed22f4ebf620fdef316b09bfb0b8 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 08:42:13 -0500 Subject: [PATCH 20/37] test(thorchain): sign deposits on the BSC, Base and Arbitrum routers Native and token deposits to THORChain's BSC and Base routers and Maya's Arbitrum router clear-sign on their own chain, and each signature recovers to the device's address. The same router address on another chain is refused. --- tests/test_msg_ethereum_thorchain_deposit.py | 74 ++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tests/test_msg_ethereum_thorchain_deposit.py b/tests/test_msg_ethereum_thorchain_deposit.py index aa0f023d..027af7f5 100644 --- a/tests/test_msg_ethereum_thorchain_deposit.py +++ b/tests/test_msg_ethereum_thorchain_deposit.py @@ -213,5 +213,79 @@ def test_deposit_unpinned_chain_blind_sign_blocked(self): ) + # (router, chain_id) pins added with the BSC, Base and Arbitrum routers. + NEW_ROUTER_PINS = [ + ("b30ec53f98ff5947ede720d32ac2da7e52a5f56b", 56), # THORChain BSC + ("00dc6100103bc402d490aee3f9a5560cbd91f1d4", 8453), # THORChain Base + ("700e97ef07219440487840dc472e7120a7ff11f4", 42161), # Maya Arbitrum + ] + + def test_deposit_new_routers_native_and_token(self): + """Native and token deposits clear-sign on the BSC and Base THORChain + routers and Maya's Arbitrum router, each on its own chain. Each + signature is recovered against the EIP-155 pre-image.""" + self.requires_fullFeature() + self.requires_firmware("7.15.0") + self.requires_release_capability('thor-deposit-review') + self.setup_mnemonic_allallall() + + from keepkeylib.signed_metadata import eth_sighash_legacy, keccak256 + from ecdsa import VerifyingKey, SECP256k1, util + + memo = "=:ETH.ETH:0xabcdef1234567890abcdef1234567890abcdef12:0:t:0" + native = _build_deposit_with_expiry_calldata(memo) + token = bytearray(native) + token[4 + 32 + 12:4 + 64] = b"\x11" * 20 # asset = an ERC-20 + token = bytes(token) + + n = parse_path("m/44'/60'/0'/0/0") + me = self.client.ethereum_get_address(n) + nonce, gas_price, gas_limit = 6, 50000000000, 300000 + for router, chain_id in self.NEW_ROUTER_PINS: + to = binascii.unhexlify(router) + for data, value in ((native, 500000000000000000), (token, 0)): + sig_v, sig_r, sig_s = self.client.ethereum_sign_tx( + n=n, nonce=nonce, gas_price=gas_price, + gas_limit=gas_limit, to=to, value=value, + chain_id=chain_id, data=data, + ) + self.assertIn(sig_v, [2 * chain_id + 35, 2 * chain_id + 36]) + digest = eth_sighash_legacy(nonce, gas_price, gas_limit, to, + value, data, chain_id) + keys = VerifyingKey.from_public_key_recovery_with_digest( + sig_r + sig_s, digest, SECP256k1, hashfunc=None, + sigdecode=util.sigdecode_string, + ) + rec = sig_v - (35 + 2 * chain_id) + self.assertEqual(keccak256(keys[rec].to_string())[-20:], me) + + def test_deposit_new_routers_wrong_chain_blocked(self): + """Each new router is pinned to its own chain only: the same address + on another chain falls to the blind-sign gate.""" + self.requires_fullFeature() + self.requires_firmware("7.15.0") + self.requires_release_capability('thor-deposit-review') + self.setup_mnemonic_allallall() + + from keepkeylib.client import CallException + + memo = "=:ETH.ETH:0xabcdef1234567890abcdef1234567890abcdef12:0:t:0" + data = _build_deposit_with_expiry_calldata(memo) + bsc, base, arb = (r for r, _ in self.NEW_ROUTER_PINS) + for router, chain_id in ((bsc, 1), (bsc, 8453), (base, 1), (base, 56), + (base, 42161), (arb, 1), (arb, 8453)): + with self.assertRaises(CallException, + msg="router %s chain %d" % (router, chain_id)): + self.client.ethereum_sign_tx( + n=parse_path("m/44'/60'/0'/0/0"), + nonce=7, + gas_price=50000000000, + gas_limit=300000, + to=binascii.unhexlify(router), + value=500000000000000000, + chain_id=chain_id, + data=data, + ) + if __name__ == "__main__": unittest.main() From bc922554741da6ee642f3ac5a345c3400756401c Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 09:02:07 -0500 Subject: [PATCH 21/37] test(zcash): an all-uppercase user_address is accepted and shown in lowercase --- tests/test_msg_zcash_sign_pczt_device.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_msg_zcash_sign_pczt_device.py b/tests/test_msg_zcash_sign_pczt_device.py index a874c37e..07999cb5 100644 --- a/tests/test_msg_zcash_sign_pczt_device.py +++ b/tests/test_msg_zcash_sign_pczt_device.py @@ -1620,6 +1620,27 @@ def test_user_address_is_checked_then_shown(self): self.assertEqual(shown, [('Shielded recipient', self.MULTI_RECEIVER_UA)]) + def test_uppercase_user_address_is_shown_in_lowercase(self): + """An all-uppercase address, as a QR code carries it, is accepted. + + BIP 350 allows all-uppercase bech32m and forbids only mixed case. The + device checks it like the lowercase form and shows the lowercase form + wallets display. + """ + self.requires_firmware(self.USER_ADDRESS_FIRMWARE) + action = note_action(CMX_ORCHARD) + action['user_address'] = self.MULTI_RECEIVER_UA.upper() + screens = self._capture_confirm_text() + + result = self.client.zcash_sign_pczt(**sign_kwargs([action])) + + self.assertIsInstance(result, zcash_proto.ZcashSignedPCZT) + shown = [(title, body) for code, title, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput + and title == 'Shielded recipient'] + self.assertEqual(shown, + [('Shielded recipient', self.MULTI_RECEIVER_UA)]) + def test_mismatched_user_address_is_refused(self): """A valid address that does not hold the recipient refuses signing. From 05938bfd8773ac1238fd7f5cee73566a686c562f Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 15:17:43 -0500 Subject: [PATCH 22/37] test(eip712): sign three real UniswapX V3 Dutch orders from Arbitrum A V3DutchOrder witness references seven struct types and nests six frames deep (witness.baseOutputs[i].curve.relativeAmounts). Add three filled Arbitrum orders: one output, one with a fee output, and one with a negative curve amount. Each document was rebuilt from the order the UniswapX API returned, and the swapper's own signature recovers from its digest. --- tests/eip712_corpus.json | 688 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 688 insertions(+) diff --git a/tests/eip712_corpus.json b/tests/eip712_corpus.json index 39980742..8807d01c 100644 --- a/tests/eip712_corpus.json +++ b/tests/eip712_corpus.json @@ -5501,5 +5501,693 @@ "message_hash": "c52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e", "digest": "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2" } + }, + { + "id": "uniswapx-V3DutchOrder-arbitrum", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/V3DutchOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/DutchOrderLib.sol", + "https://arbiscan.io/tx/0x1f2f270bf65d2b7f02b2a141f71e23bfcc3a84b37c9a0921577516c58031678d" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitWitnessTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "V3DutchOrder" + } + ], + "NonlinearDutchDecay": [ + { + "name": "relativeBlocks", + "type": "uint256" + }, + { + "name": "relativeAmounts", + "type": "int256[]" + } + ], + "OrderInfo": [ + { + "name": "reactor", + "type": "address" + }, + { + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "additionalValidationContract", + "type": "address" + }, + { + "name": "additionalValidationData", + "type": "bytes" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "V3DutchInput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "curve", + "type": "NonlinearDutchDecay" + }, + { + "name": "maxAmount", + "type": "uint256" + }, + { + "name": "adjustmentPerGweiBaseFee", + "type": "uint256" + } + ], + "V3DutchOrder": [ + { + "name": "info", + "type": "OrderInfo" + }, + { + "name": "cosigner", + "type": "address" + }, + { + "name": "startingBaseFee", + "type": "uint256" + }, + { + "name": "baseInput", + "type": "V3DutchInput" + }, + { + "name": "baseOutputs", + "type": "V3DutchOutput[]" + } + ], + "V3DutchOutput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "curve", + "type": "NonlinearDutchDecay" + }, + { + "name": "recipient", + "type": "address" + }, + { + "name": "minAmount", + "type": "uint256" + }, + { + "name": "adjustmentPerGweiBaseFee", + "type": "uint256" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 42161, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", + "amount": "12514477" + }, + "spender": "0xB274d5F4b833b61B340b654d600A864fB604a87c", + "nonce": "1993355410060173619975858542007397613024848003638926934372819132074995782145", + "deadline": "1791402675", + "witness": { + "info": { + "reactor": "0xB274d5F4b833b61B340b654d600A864fB604a87c", + "swapper": "0x7f05C94cE299Eea9FCdAE798Be34E226a0cb1e77", + "nonce": "1993355410060173619975858542007397613024848003638926934372819132074995782145", + "deadline": "1791402675", + "additionalValidationContract": "0x0000000000000000000000000000000000000000", + "additionalValidationData": "0x" + }, + "cosigner": "0x4449Cd34d1eb1FEDCF02A1Be3834FfDe8E6A6180", + "startingBaseFee": "0", + "baseInput": { + "token": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", + "startAmount": "12514477", + "curve": { + "relativeBlocks": "32", + "relativeAmounts": [ + "0" + ] + }, + "maxAmount": "12514477", + "adjustmentPerGweiBaseFee": "0" + }, + "baseOutputs": [ + { + "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + "startAmount": "10407262726", + "curve": { + "relativeBlocks": "32", + "relativeAmounts": [ + "260181569" + ] + }, + "recipient": "0x7f05C94cE299Eea9FCdAE798Be34E226a0cb1e77", + "minAmount": "10147081157", + "adjustmentPerGweiBaseFee": "0" + } + ] + } + } + }, + "expected": { + "domain_separator": "8a6e6e19bdfb3db3409910416b47c2f8fc28b49488d6555c7fceaa4479135bc3", + "message_hash": "d9f38d5095c80dc8118342f144211cb537cc6b9b98027c20cefed85ffaf1cf5c", + "digest": "2a1fba9e8c26066eadd659350dc6e6cc17a9a63c8c4fbdbd771049adb754c33a" + } + }, + { + "id": "uniswapx-V3DutchOrder-arbitrum-fee-output", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/V3DutchOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/DutchOrderLib.sol", + "https://arbiscan.io/tx/0x3c67c0eb31f12747d675fe1c65734b2ce7b3ce608dcc02be348285b7435e7855" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitWitnessTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "V3DutchOrder" + } + ], + "NonlinearDutchDecay": [ + { + "name": "relativeBlocks", + "type": "uint256" + }, + { + "name": "relativeAmounts", + "type": "int256[]" + } + ], + "OrderInfo": [ + { + "name": "reactor", + "type": "address" + }, + { + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "additionalValidationContract", + "type": "address" + }, + { + "name": "additionalValidationData", + "type": "bytes" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "V3DutchInput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "curve", + "type": "NonlinearDutchDecay" + }, + { + "name": "maxAmount", + "type": "uint256" + }, + { + "name": "adjustmentPerGweiBaseFee", + "type": "uint256" + } + ], + "V3DutchOrder": [ + { + "name": "info", + "type": "OrderInfo" + }, + { + "name": "cosigner", + "type": "address" + }, + { + "name": "startingBaseFee", + "type": "uint256" + }, + { + "name": "baseInput", + "type": "V3DutchInput" + }, + { + "name": "baseOutputs", + "type": "V3DutchOutput[]" + } + ], + "V3DutchOutput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "curve", + "type": "NonlinearDutchDecay" + }, + { + "name": "recipient", + "type": "address" + }, + { + "name": "minAmount", + "type": "uint256" + }, + { + "name": "adjustmentPerGweiBaseFee", + "type": "uint256" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 42161, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", + "amount": "1226547455" + }, + "spender": "0xB274d5F4b833b61B340b654d600A864fB604a87c", + "nonce": "1993355115482027011983709557485874525075304680291794092761105090895583133443", + "deadline": "1791393631", + "witness": { + "info": { + "reactor": "0xB274d5F4b833b61B340b654d600A864fB604a87c", + "swapper": "0xD606742f4f6a05305B59174C9DA0B3BB30987556", + "nonce": "1993355115482027011983709557485874525075304680291794092761105090895583133443", + "deadline": "1791393631", + "additionalValidationContract": "0x0000000000000000000000000000000000000000", + "additionalValidationData": "0x" + }, + "cosigner": "0x4449Cd34d1eb1FEDCF02A1Be3834FfDe8E6A6180", + "startingBaseFee": "0", + "baseInput": { + "token": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", + "startAmount": "1226547455", + "curve": { + "relativeBlocks": "32", + "relativeAmounts": [ + "0" + ] + }, + "maxAmount": "1226547455", + "adjustmentPerGweiBaseFee": "0" + }, + "baseOutputs": [ + { + "token": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", + "startAmount": "1446161", + "curve": { + "relativeBlocks": "32", + "relativeAmounts": [ + "7232" + ] + }, + "recipient": "0xD606742f4f6a05305B59174C9DA0B3BB30987556", + "minAmount": "1438929", + "adjustmentPerGweiBaseFee": "0" + }, + { + "token": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", + "startAmount": "23514", + "curve": { + "relativeBlocks": "32", + "relativeAmounts": [ + "117" + ] + }, + "recipient": "0x147CF09E7373B8FDA6f12021F1b0f98D6da1A566", + "minAmount": "23397", + "adjustmentPerGweiBaseFee": "0" + } + ] + } + } + }, + "expected": { + "domain_separator": "8a6e6e19bdfb3db3409910416b47c2f8fc28b49488d6555c7fceaa4479135bc3", + "message_hash": "d4e97cc058c321e71216e131c51438117e2c106360aa5d893bc194c54b51bfe1", + "digest": "4ef893b03e07c41dad0a7811640b7fcd7eafd518099eea36a6191f7c56061595" + } + }, + { + "id": "uniswapx-V3DutchOrder-arbitrum-negative-curve", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/V3DutchOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/DutchOrderLib.sol", + "https://arbiscan.io/tx/0xfa14890926a78bb4095b7453c97150b3dea12fe8d53d21d97fd7366dfb595b7a" + ], + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitWitnessTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "V3DutchOrder" + } + ], + "NonlinearDutchDecay": [ + { + "name": "relativeBlocks", + "type": "uint256" + }, + { + "name": "relativeAmounts", + "type": "int256[]" + } + ], + "OrderInfo": [ + { + "name": "reactor", + "type": "address" + }, + { + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "additionalValidationContract", + "type": "address" + }, + { + "name": "additionalValidationData", + "type": "bytes" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "V3DutchInput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "curve", + "type": "NonlinearDutchDecay" + }, + { + "name": "maxAmount", + "type": "uint256" + }, + { + "name": "adjustmentPerGweiBaseFee", + "type": "uint256" + } + ], + "V3DutchOrder": [ + { + "name": "info", + "type": "OrderInfo" + }, + { + "name": "cosigner", + "type": "address" + }, + { + "name": "startingBaseFee", + "type": "uint256" + }, + { + "name": "baseInput", + "type": "V3DutchInput" + }, + { + "name": "baseOutputs", + "type": "V3DutchOutput[]" + } + ], + "V3DutchOutput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "curve", + "type": "NonlinearDutchDecay" + }, + { + "name": "recipient", + "type": "address" + }, + { + "name": "minAmount", + "type": "uint256" + }, + { + "name": "adjustmentPerGweiBaseFee", + "type": "uint256" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 42161, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + "amount": "18453595142" + }, + "spender": "0xB274d5F4b833b61B340b654d600A864fB604a87c", + "nonce": "1993353428323324451238365100392469529508888071612295616155122962755034285057", + "deadline": "1791397885", + "witness": { + "info": { + "reactor": "0xB274d5F4b833b61B340b654d600A864fB604a87c", + "swapper": "0xd603a49886c9B500f96C0d798aed10068D73bF7C", + "nonce": "1993353428323324451238365100392469529508888071612295616155122962755034285057", + "deadline": "1791397885", + "additionalValidationContract": "0x0000000000000000000000000000000000000000", + "additionalValidationData": "0x" + }, + "cosigner": "0x4449Cd34d1eb1FEDCF02A1Be3834FfDe8E6A6180", + "startingBaseFee": "0", + "baseInput": { + "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + "startAmount": "18361786211", + "curve": { + "relativeBlocks": "32", + "relativeAmounts": [ + "-91808931" + ] + }, + "maxAmount": "18453595142", + "adjustmentPerGweiBaseFee": "0" + }, + "baseOutputs": [ + { + "token": "0x0000000000000000000000000000000000000000", + "startAmount": "7165000000000000000", + "curve": { + "relativeBlocks": "32", + "relativeAmounts": [ + "0" + ] + }, + "recipient": "0xd603a49886c9B500f96C0d798aed10068D73bF7C", + "minAmount": "7165000000000000000", + "adjustmentPerGweiBaseFee": "0" + } + ] + } + } + }, + "expected": { + "domain_separator": "8a6e6e19bdfb3db3409910416b47c2f8fc28b49488d6555c7fceaa4479135bc3", + "message_hash": "b8c2d5a7b402683b9422031588d6231566d3813eb284905088fdab5bbbfb3e27", + "digest": "6e576a58ecf5e234ea6fc3eae0b97fe2ac8fa90c5a8e822da53fad929b5977b2" + } } ] From b82ab6a0c4d87f9f45c6b8370d7f4dcba34d5d30 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 14:57:39 -0500 Subject: [PATCH 23/37] test(zcash): sign ZIP 320 TEX payments A wallet pays a TEX address from shielded funds in two transactions: one to its own one-time address m/44'/133'/account'/2/index, then a transparent-only one from there to the TEX recipient. Both are built by librustzcash for the "all" seed. Step 1 shows the output as a transfer to your one-time address when its path is sent, and as a plain t1 send without it. Step 2 has no shielded action; it shows the recipient as tex1... and its signature verifies under librustzcash's sighash, in v6 and v5. Shielded bundle claims, a non-empty Sapling digest, another account's path and change 3 are refused. zcash_sign_pczt accepts no actions when there is a transparent input. Repins device-protocol for is_tex and the one-time output path. --- device-protocol | 2 +- keepkeylib/client.py | 10 +- keepkeylib/messages_zcash_pb2.py | 42 +- tests/test_msg_zcash_tex.py | 409 ++++++++++++++++++ tests/test_msg_zcash_transparent_shielding.py | 2 +- 5 files changed, 446 insertions(+), 19 deletions(-) create mode 100644 tests/test_msg_zcash_tex.py diff --git a/device-protocol b/device-protocol index e8947b36..47f1fdc4 160000 --- a/device-protocol +++ b/device-protocol @@ -1 +1 @@ -Subproject commit e8947b360557df2cf51bd58732b214eddaa18ff3 +Subproject commit 47f1fdc4030f726fae3d694329a78be319d80e48 diff --git a/keepkeylib/client.py b/keepkeylib/client.py index 72b6c09c..f1ac6194 100644 --- a/keepkeylib/client.py +++ b/keepkeylib/client.py @@ -2101,7 +2101,9 @@ def zcash_sign_pczt(self, address_n, actions, account=None, version_group_id: transaction version group ID lock_time: transaction lock time expiry_height: transaction expiry height - transparent_outputs: output dicts matching ZcashTransparentOutput + transparent_outputs: output dicts matching ZcashTransparentOutput; + is_tex shows a P2PKH output as a ZIP 320 tex1 address, and + address_n marks the account's own one-time address transparent_inputs: input dicts matching ZcashTransparentInput; host-provided per-input sighashes are rejected by RC18 return_transparent_signatures: when true, return a tuple of @@ -2111,8 +2113,10 @@ def zcash_sign_pczt(self, address_n, actions, account=None, ZcashSignedPCZT with compact Orchard signatures and optional txid, or a tuple including transparent signatures when requested. """ - if len(actions) == 0: - raise ValueError("Must have at least one action") + # No action is a transparent-only transaction (ZIP 320 TEX step 2), + # which needs a transparent input to sign. + if len(actions) == 0 and not transparent_inputs: + raise ValueError("Must have at least one action or transparent input") # Orchard actions first, then any Ironwood ones; indices count on. stream = list(actions) + list(ironwood_actions or []) n_actions = len(stream) diff --git a/keepkeylib/messages_zcash_pb2.py b/keepkeylib/messages_zcash_pb2.py index a31e647e..026e3128 100644 --- a/keepkeylib/messages_zcash_pb2.py +++ b/keepkeylib/messages_zcash_pb2.py @@ -20,7 +20,7 @@ name='messages-zcash.proto', package='', syntax='proto2', - serialized_pb=_b('\n\x14messages-zcash.proto\"\xad\x05\n\rZcashSignPCZT\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x11\n\tpczt_data\x18\x03 \x01(\x0c\x12\x11\n\tn_actions\x18\x04 \x01(\r\x12\x14\n\x0ctotal_amount\x18\x05 \x01(\x04\x12\x0b\n\x03\x66\x65\x65\x18\x06 \x01(\x04\x12\x11\n\tbranch_id\x18\x07 \x01(\r\x12\x15\n\rheader_digest\x18\x08 \x01(\x0c\x12\x1a\n\x12transparent_digest\x18\t \x01(\x0c\x12\x16\n\x0esapling_digest\x18\n \x01(\x0c\x12\x16\n\x0eorchard_digest\x18\x0b \x01(\x0c\x12\x15\n\rorchard_flags\x18\x0c \x01(\r\x12\x1d\n\x15orchard_value_balance\x18\r \x01(\x03\x12\x16\n\x0eorchard_anchor\x18\x0e \x01(\x0c\x12\x12\n\ntx_version\x18\x0f \x01(\r\x12\x18\n\x10version_group_id\x18\x10 \x01(\r\x12\x11\n\tlock_time\x18\x11 \x01(\r\x12\x15\n\rexpiry_height\x18\x12 \x01(\r\x12\x46\n\rshielded_pool\x18\x13 \x01(\x0e\x32\x12.ZcashShieldedPool:\x1bZCASH_SHIELDED_POOL_ORCHARD\x12\x17\n\x0fironwood_digest\x18\x14 \x01(\x0c\x12\x1a\n\x12n_ironwood_actions\x18\x15 \x01(\r\x12\x16\n\x0eironwood_flags\x18\x16 \x01(\r\x12\x1e\n\x16ironwood_value_balance\x18\x17 \x01(\x03\x12\x1d\n\x15n_transparent_outputs\x18\x1d \x01(\r\x12\x1c\n\x14n_transparent_inputs\x18\x1e \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x1f \x01(\x0c\"\xb9\x02\n\x0fZcashPCZTAction\x12\r\n\x05index\x18\x01 \x01(\r\x12\r\n\x05\x61lpha\x18\x02 \x01(\x0c\x12\x0f\n\x07sighash\x18\x03 \x01(\x0c\x12\x0e\n\x06\x63v_net\x18\x04 \x01(\x0c\x12\r\n\x05value\x18\x05 \x01(\x04\x12\x10\n\x08is_spend\x18\x06 \x01(\x08\x12\x11\n\tnullifier\x18\x07 \x01(\x0c\x12\x0b\n\x03\x63mx\x18\x08 \x01(\x0c\x12\x0b\n\x03\x65pk\x18\t \x01(\x0c\x12\x13\n\x0b\x65nc_compact\x18\n \x01(\x0c\x12\x10\n\x08\x65nc_memo\x18\x0b \x01(\x0c\x12\x16\n\x0e\x65nc_noncompact\x18\x0c \x01(\x0c\x12\n\n\x02rk\x18\r \x01(\x0c\x12\x16\n\x0eout_ciphertext\x18\x0e \x01(\x0c\x12\x11\n\trecipient\x18\x0f \x01(\x0c\x12\r\n\x05rseed\x18\x10 \x01(\x0c\x12\x14\n\x0cuser_address\x18\x11 \x01(\t\"(\n\x12ZcashPCZTActionAck\x12\x12\n\nnext_index\x18\x01 \x01(\r\"3\n\x0fZcashSignedPCZT\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"N\n\x12ZcashGetOrchardFVK\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\"Q\n\x0fZcashOrchardFVK\x12\n\n\x02\x61k\x18\x01 \x01(\x0c\x12\n\n\x02nk\x18\x02 \x01(\x0c\x12\x0c\n\x04rivk\x18\x03 \x01(\x0c\x12\x18\n\x10seed_fingerprint\x18\x04 \x01(\x0c\"N\n\x16ZcashTransparentOutput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x15\n\rscript_pubkey\x18\x03 \x01(\x0c\"\xb0\x01\n\x15ZcashTransparentInput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0f\n\x07sighash\x18\x02 \x01(\x0c\x12\x11\n\taddress_n\x18\x03 \x03(\r\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x04\x12\x14\n\x0cprevout_txid\x18\x05 \x01(\x0c\x12\x15\n\rprevout_index\x18\x06 \x01(\r\x12\x10\n\x08sequence\x18\x07 \x01(\r\x12\x15\n\rscript_pubkey\x18\x08 \x01(\x0c\"J\n\x13ZcashTransparentAck\x12\x19\n\x11next_output_index\x18\x01 \x01(\r\x12\x18\n\x10next_input_index\x18\x02 \x01(\r\",\n\x16ZcashTransparentSigned\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\"\x8b\x01\n\x13ZcashDisplayAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x07 \x01(\x0cJ\x04\x08\x03\x10\x04J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07R\x07\x61\x64\x64ressR\x02\x61kR\x02nkR\x04rivk\"9\n\x0cZcashAddress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x18\n\x10seed_fingerprint\x18\x02 \x01(\x0c*V\n\x11ZcashShieldedPool\x12\x1f\n\x1bZCASH_SHIELDED_POOL_ORCHARD\x10\x00\x12 \n\x1cZCASH_SHIELDED_POOL_IRONWOOD\x10\x01\x42\x31\n\x1a\x63om.keepkey.deviceprotocolB\x13KeepKeyMessageZcash') + serialized_pb=_b('\n\x14messages-zcash.proto\"\xad\x05\n\rZcashSignPCZT\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x11\n\tpczt_data\x18\x03 \x01(\x0c\x12\x11\n\tn_actions\x18\x04 \x01(\r\x12\x14\n\x0ctotal_amount\x18\x05 \x01(\x04\x12\x0b\n\x03\x66\x65\x65\x18\x06 \x01(\x04\x12\x11\n\tbranch_id\x18\x07 \x01(\r\x12\x15\n\rheader_digest\x18\x08 \x01(\x0c\x12\x1a\n\x12transparent_digest\x18\t \x01(\x0c\x12\x16\n\x0esapling_digest\x18\n \x01(\x0c\x12\x16\n\x0eorchard_digest\x18\x0b \x01(\x0c\x12\x15\n\rorchard_flags\x18\x0c \x01(\r\x12\x1d\n\x15orchard_value_balance\x18\r \x01(\x03\x12\x16\n\x0eorchard_anchor\x18\x0e \x01(\x0c\x12\x12\n\ntx_version\x18\x0f \x01(\r\x12\x18\n\x10version_group_id\x18\x10 \x01(\r\x12\x11\n\tlock_time\x18\x11 \x01(\r\x12\x15\n\rexpiry_height\x18\x12 \x01(\r\x12\x46\n\rshielded_pool\x18\x13 \x01(\x0e\x32\x12.ZcashShieldedPool:\x1bZCASH_SHIELDED_POOL_ORCHARD\x12\x17\n\x0fironwood_digest\x18\x14 \x01(\x0c\x12\x1a\n\x12n_ironwood_actions\x18\x15 \x01(\r\x12\x16\n\x0eironwood_flags\x18\x16 \x01(\r\x12\x1e\n\x16ironwood_value_balance\x18\x17 \x01(\x03\x12\x1d\n\x15n_transparent_outputs\x18\x1d \x01(\r\x12\x1c\n\x14n_transparent_inputs\x18\x1e \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x1f \x01(\x0c\"\xb9\x02\n\x0fZcashPCZTAction\x12\r\n\x05index\x18\x01 \x01(\r\x12\r\n\x05\x61lpha\x18\x02 \x01(\x0c\x12\x0f\n\x07sighash\x18\x03 \x01(\x0c\x12\x0e\n\x06\x63v_net\x18\x04 \x01(\x0c\x12\r\n\x05value\x18\x05 \x01(\x04\x12\x10\n\x08is_spend\x18\x06 \x01(\x08\x12\x11\n\tnullifier\x18\x07 \x01(\x0c\x12\x0b\n\x03\x63mx\x18\x08 \x01(\x0c\x12\x0b\n\x03\x65pk\x18\t \x01(\x0c\x12\x13\n\x0b\x65nc_compact\x18\n \x01(\x0c\x12\x10\n\x08\x65nc_memo\x18\x0b \x01(\x0c\x12\x16\n\x0e\x65nc_noncompact\x18\x0c \x01(\x0c\x12\n\n\x02rk\x18\r \x01(\x0c\x12\x16\n\x0eout_ciphertext\x18\x0e \x01(\x0c\x12\x11\n\trecipient\x18\x0f \x01(\x0c\x12\r\n\x05rseed\x18\x10 \x01(\x0c\x12\x14\n\x0cuser_address\x18\x11 \x01(\t\"(\n\x12ZcashPCZTActionAck\x12\x12\n\nnext_index\x18\x01 \x01(\r\"3\n\x0fZcashSignedPCZT\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"N\n\x12ZcashGetOrchardFVK\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\"Q\n\x0fZcashOrchardFVK\x12\n\n\x02\x61k\x18\x01 \x01(\x0c\x12\n\n\x02nk\x18\x02 \x01(\x0c\x12\x0c\n\x04rivk\x18\x03 \x01(\x0c\x12\x18\n\x10seed_fingerprint\x18\x04 \x01(\x0c\"q\n\x16ZcashTransparentOutput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0e\n\x06\x61mount\x18\x02 \x01(\x04\x12\x15\n\rscript_pubkey\x18\x03 \x01(\x0c\x12\x0e\n\x06is_tex\x18\x04 \x01(\x08\x12\x11\n\taddress_n\x18\x05 \x03(\r\"\xb0\x01\n\x15ZcashTransparentInput\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0f\n\x07sighash\x18\x02 \x01(\x0c\x12\x11\n\taddress_n\x18\x03 \x03(\r\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x04\x12\x14\n\x0cprevout_txid\x18\x05 \x01(\x0c\x12\x15\n\rprevout_index\x18\x06 \x01(\r\x12\x10\n\x08sequence\x18\x07 \x01(\r\x12\x15\n\rscript_pubkey\x18\x08 \x01(\x0c\"J\n\x13ZcashTransparentAck\x12\x19\n\x11next_output_index\x18\x01 \x01(\r\x12\x18\n\x10next_input_index\x18\x02 \x01(\r\",\n\x16ZcashTransparentSigned\x12\x12\n\nsignatures\x18\x01 \x03(\x0c\"\x8b\x01\n\x13ZcashDisplayAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\r\x12!\n\x19\x65xpected_seed_fingerprint\x18\x07 \x01(\x0cJ\x04\x08\x03\x10\x04J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07R\x07\x61\x64\x64ressR\x02\x61kR\x02nkR\x04rivk\"9\n\x0cZcashAddress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x18\n\x10seed_fingerprint\x18\x02 \x01(\x0c*V\n\x11ZcashShieldedPool\x12\x1f\n\x1bZCASH_SHIELDED_POOL_ORCHARD\x10\x00\x12 \n\x1cZCASH_SHIELDED_POOL_IRONWOOD\x10\x01\x42\x31\n\x1a\x63om.keepkey.deviceprotocolB\x13KeepKeyMessageZcash') ) _ZCASHSHIELDEDPOOL = _descriptor.EnumDescriptor( @@ -40,8 +40,8 @@ ], containing_type=None, options=None, - serialized_start=1868, - serialized_end=1954, + serialized_start=1903, + serialized_end=1989, ) _sym_db.RegisterEnumDescriptor(_ZCASHSHIELDEDPOOL) @@ -594,6 +594,20 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='is_tex', full_name='ZcashTransparentOutput.is_tex', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='address_n', full_name='ZcashTransparentOutput.address_n', index=4, + number=5, type=13, cpp_type=3, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), ], extensions=[ ], @@ -607,7 +621,7 @@ oneofs=[ ], serialized_start=1286, - serialized_end=1364, + serialized_end=1399, ) @@ -686,8 +700,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1367, - serialized_end=1543, + serialized_start=1402, + serialized_end=1578, ) @@ -724,8 +738,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1545, - serialized_end=1619, + serialized_start=1580, + serialized_end=1654, ) @@ -755,8 +769,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1621, - serialized_end=1665, + serialized_start=1656, + serialized_end=1700, ) @@ -800,8 +814,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1668, - serialized_end=1807, + serialized_start=1703, + serialized_end=1842, ) @@ -838,8 +852,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1809, - serialized_end=1866, + serialized_start=1844, + serialized_end=1901, ) _ZCASHSIGNPCZT.fields_by_name['shielded_pool'].enum_type = _ZCASHSHIELDEDPOOL diff --git a/tests/test_msg_zcash_tex.py b/tests/test_msg_zcash_tex.py new file mode 100644 index 00000000..cbfe23d7 --- /dev/null +++ b/tests/test_msg_zcash_tex.py @@ -0,0 +1,409 @@ +"""ZIP 320 TEX payments. + +A TEX address (tex1..., bech32m) wraps a P2PKH key hash and takes only +transparent funds, so a wallet pays it from shielded funds in two +transactions: one to its own one-time address m/44'/133'/account'/2/index, +then a transparent-only one from that address to the TEX recipient. + +Both transactions are built by librustzcash (zcash_primitives +Builder::build_for_pczt, pczt Creator + IoFinalizer; orchard 0.16.0) for the +"all" seed, account 0, on regtest, and the sighashes are librustzcash's pczt +Signer sighashes: + + step 1 (v6, NU6.3): one 1,025,000-zat Orchard note to the one-time address, + 1,010,000 zat, fee 15,000, a 2-action Orchard bundle; + step 2 (v6 NU6.3 and v5 NU6.2): that output to the ZIP 320 example address + tex1s2rt77ggv6q989lr49rkgzmh5slsksa9khdgte, 1,000,000 zat, fee 10,000, + with no shielded bundle at all. +""" + +import hashlib + +import ecdsa +from ecdsa import SECP256k1, VerifyingKey +from ecdsa.util import sigdecode_der + +import common +from keepkeylib import messages_pb2 as proto +from keepkeylib import types_pb2 as proto_types +from keepkeylib import messages_zcash_pb2 as zcash_proto +from keepkeylib.client import CallException + +from test_msg_zcash_sign_pczt_device import ( + ADDRESS_N, + ANCHOR, + IRONWOOD_TX, + ORCHARD_TX, + bundle_digest, + header_digest, + migration_action, +) +from test_msg_zcash_transparent_shielding import ( + _b2b, + _outputs_digest, + _prevouts_digest, + _sequences_digest, + _transparent_sig_digest, +) + +H = 0x80000000 +ONE_TIME_PATH = [H + 44, H + 133, H, 2, 0] +TEX_FIRMWARE = "7.15.0" + +ONE_TIME_SCRIPT = bytes.fromhex( + '76a9142875b160968fae11ca7fdd0174825c812f24f05688ac') +ONE_TIME_T1 = 't1MZY2TFhahiyGL9fscjCAjhppXGn48oHMM' +TEX_SCRIPT = bytes.fromhex('76a9148286bf790866805397e3a947640b77a43f0b43a588ac') +TEX_ADDRESS = 'tex1s2rt77ggv6q989lr49rkgzmh5slsksa9khdgte' +TEX_T1 = 't1VmmGiyjVNeCjxDZzg7vZmd99WyzVby9yC' # the same key hash (ZIP 320) +EMPTY_SAPLING = hashlib.blake2b(b'', digest_size=32, + person=b'ZTxIdSaplingHash').digest() + +STEP1 = { + 'sighash': bytes.fromhex('981da2d23dcdd6c04e5d36aa00cca4eb6d21cd4aa1af6547bacc5ab8d9764e7d'), + 'actions': [ + { + 'is_spend': False, + 'nullifier': bytes.fromhex('a593b9cd40c49a4f447d87ed1f01b5896b1135ee7739d7fdb04ad1a6eeda232f'), + 'rk': bytes.fromhex('6742b6a24175e6157151140a7ed464e4236a78785183c58a0eadc9a00e22dea4'), + 'alpha': bytes.fromhex('0b9ad8d7306dba1cb592962db3ffec30ca18fe3cdcef51c199eaad4615b57d3e'), + 'cv_net': bytes.fromhex('a41f06ec4122d79c0442c06a04c8d9a623c87d94a1e994787ec2acfb13288587'), + 'cmx': bytes.fromhex('ff61e8171bc34a00c43a2824b016c089b467313e51e02d776dcbc13c4dc7d10f'), + 'epk': bytes.fromhex('676ed987d25b96b7ff4ef38bf414f74e884e79012ba1db7d6090e37d7e9cb126'), + 'enc': bytes.fromhex( + 'ffd7c80499607ed7ff8ccf36ea23890a40dbfbe7e1e2f4c4ecc9c719ec38bfd3' + '7022afc69a91121396c96183645bc6f2cf4a8b2c1455fb81ce815ed964e2788e' + '7db82875c8d9a6ac4235b5d1ac954b906c7181b97ed1aae6641b2d2ed4a41d20' + '5b61d2bf0faabfa6512ffdaff000525efb96acf524120155847b45e3938e30c8' + '310202deb697351458742efe591c95865f2348c220d17318dcacf183e4ae6e15' + '302a4bcea524810ae1c70d50ee0aa9dd90546e7a6345922297220454c6a97b44' + '4a926be368679a31535c95932daeb1aed9fcda3000cf0963c4105fd9bc6fd24b' + '84062be96d06debd41f20332dc4d4291ec2b2f9364ba0f6cbc6f90da6729d0ee' + 'c6417a722c38c893f7e9113698f4ecde3ec560c37d88a8bd6bd04f2b1d86a06e' + '84695c69b0333913ae81492981b4bff367998995ea689a705113346cb81ccdad' + '6cbc5fc0733b97cefb92ce0b335bab1f492ac38da4c2384c005d7e2a352467d2' + 'e8544f9664df46950a3207edcfefd983e8495f53c214f19a9024c0c54a15fa75' + 'b80166313f550dbe42fa71704620bb78ed7273d5e5af8760b330278306da8098' + 'da3cbf3a640dc60aea3642e2b59a7902e61f89d3396c25d4692881f7de250e51' + 'e72300866c789036f550c3008b3865c89101b8925fee8abcddbd08b72ae9933f' + '812f52f5d7705a105f66d0b4dfd16bde4211a08c6740c3887b1356a1744ddd81' + '6f7c6e9dd2d682df1d14603ad69aa684587ea09c2fd25f746798c604fa3d7b07' + 'cb1ab7170a18b380cdd2fd3d539039d21a1e264d4cc9803170c3931408d2fd2c' + '16310cf7'), + 'out': bytes.fromhex( + '8e54a7b6418db19c27eb768c3c754113eaa575de83023862634fb40ccb87c17c' + '043be6f0d883754e2fd337dae19d79fe27c4904ab28ffc360681ffb6f8e871ed' + 'fd6684d6b5e23c4e0ec28d3a951f5d3f'), + 'recipient': bytes.fromhex( + '66faa653531d4a8acdf5096f756577b19962df6615641e5fe2db3a324970a7ff' + 'e546a1f97b073e94f7b238'), + 'rseed': bytes.fromhex('716070a992fff0e13dd67cc3ddaef4ada426aa5f4ec0f13668a6f4f537ad545e'), + 'value': 0, + }, + { + 'is_spend': True, + 'nullifier': bytes.fromhex('0440da65e51ce699977111262432473c28e06b12c3e5e38fc742f35b0fd02429'), + 'rk': bytes.fromhex('fec66ad4c564961f7af701ffbc1f1e2eb3abf8b4bbf1404d60aa9efc0881e585'), + 'alpha': bytes.fromhex('83969789048b1dc71a91d523c61624cc84894ddd64c4b8dc4380e08a327f4820'), + 'cv_net': bytes.fromhex('c7343ce3e707ccdbfac1e3cae1acbef5701e793e107efe264d027af10ab73e31'), + 'cmx': bytes.fromhex('a34f6a687d5ad6aae53f0225144324a070a7de1066a9c592af9d51ea0da78b1b'), + 'epk': bytes.fromhex('0e4237147546d913c2273bb3a70f6e3cbbd8b9f839c89cc1f8a26ecaa2aba986'), + 'enc': bytes.fromhex( + 'd878f9142e82cfdcc5abb5f3d6b4b32978a6b8f3163cf48cd167abfb8512e352' + '04e7edd316c39e5c9a01be0efab1707355bb93d9b968c50820f27b6bb20415a6' + '0213dbf69f95554b56bac05cceae2f2f227b7c03925e87374fb1f9c3c0d723e8' + '9fc80fa61c863fc04aa061c91cc81c484d086d2ab66125532d89a530ee6f07e6' + '7941805f62784c8a8dec22a230506f9cb74e4c7dcaff00c4224543b052aa9f6b' + '6db1837090f7fd87536d326866a8694dbe41b3b388cca38e6e546af80af68e9f' + '8b24abb381fe57b6731dd686c179bea116a8e3be9fe041488d46634bda302abd' + '027249fb2508bfc5260b286f0ab66a9c32c07fe1796c767b266d30d752e77840' + '320ba9d7669c0ed9fe5b3c488856e84ab3674e4deed92225c4be2f31f56d4963' + 'b4bfc5f0fe686fc01950a9c5b6a95024086c566bfa7f1ce88fd6785660e60cbc' + '0b2e881a4dacfdea6b9158717dd5d0aba72b30dd067f7c32b1e08f9a714a5965' + '6567ea0fa3dbc31c12cdacbcefb9c42d832ae8cfea003fea36a32327f44ce8ac' + '0d9aff4bccf03539cf037d5c2dfb114c2b027328660b0abbab07554015b9ab8d' + '284db47c04b117e2a4365cd14f10c0416082d8615e36fa1bc5243c9d76902b99' + 'c87fb5d635b94157915fd979747247b5294006d2a74dce6c7c666e3d9c1bea5f' + 'b0124b846b22e217c6732817a13b72111395f496220af07bc8732e7d4a0f353c' + 'ff5b4c453915b5bee84ec00f817f51ff05d09138e7460727637641b178982a53' + '3ca4618a251b2f92d7b73e5d31f3d56693964a014418d767dc23c6b522d2a695' + '2a3d3200'), + 'out': bytes.fromhex( + '93148f0d468fa8c829f1499a4fa7acb16a99a2cd42b958397dffb46b2e34370e' + 'a2168063cfe245932eb4d828a461ad6a1f9951f2752b2ff9333893aa9045008a' + '1b0bf265077c00d84d6617524e341439'), + 'recipient': bytes.fromhex( + 'da973031634a8938ad1c480f978780693ec7709ba5caf58d8a7eb945586cbed6' + '45520f17387437bcfdc216'), + 'rseed': bytes.fromhex('ab6fed1774ced71e695168fc591b9cfc196b7297ac0486db880fb455b1a0f3ee'), + 'value': 0, + }, + ], +} + +STEP2 = { + 6: dict(IRONWOOD_TX, + prevout_txid=bytes.fromhex( + '981da2d23dcdd6c04e5d36aa00cca4eb6d21cd4aa1af6547bacc5ab8d9764e7d'), + sighash=bytes.fromhex( + '03e328bb6a70c20bd803375f2da51bcd56d1e46fba7f6408752de160fc7234a5')), + 5: dict(ORCHARD_TX, + prevout_txid=bytes.fromhex( + '7d2c9e83172282b3d4521c9c16632bae0ef19dbaaa8188cb46761f510574b621'), + sighash=bytes.fromhex( + '89c5074214b150ff73a3d80fa893e1b66c41e96be1f67b504740a27b16a4f6e6')), +} + + +def transparent_digest_no_inputs(outputs): + """ZIP-244 T.2: with no transparent input the shielded sighash commits to + the transparent txid digest, not the S.2 signature digest.""" + return _b2b(b'ZTxIdTranspaHash', _prevouts_digest([]) + + _sequences_digest([]) + _outputs_digest(outputs)) + + +def step1_kwargs(output): + actions = [migration_action(v) for v in STEP1['actions']] + tx = IRONWOOD_TX + kwargs = dict(tx) + kwargs.update({ + 'address_n': ADDRESS_N, + 'actions': actions, + 'account': 0, + 'fee': 15000, + 'lock_time': 0, + 'expiry_height': 69120, + 'orchard_flags': 0x03, + 'orchard_value_balance': 1025000, + 'orchard_anchor': ANCHOR, + 'header_digest': header_digest(tx['tx_version'], tx['version_group_id'], + tx['branch_id'], 0, 69120), + 'orchard_digest': bundle_digest(actions, False, 6, flags=0x03, + value_balance=1025000), + 'transparent_outputs': [output], + 'transparent_digest': transparent_digest_no_inputs([output]), + }) + return kwargs + + +def step2_parts(version): + tx = STEP2[version] + output = {'amount': 1000000, 'script_pubkey': TEX_SCRIPT, 'is_tex': True} + inp = { + 'address_n': ONE_TIME_PATH, + 'amount': 1010000, + 'prevout_txid': tx['prevout_txid'], + 'prevout_index': 0, + 'sequence': 0xffffffff, + 'script_pubkey': ONE_TIME_SCRIPT, + } + return tx, output, inp + + +def step2_kwargs(version, output, inp): + tx = STEP2[version] + kwargs = {k: tx[k] for k in ('tx_version', 'version_group_id', 'branch_id')} + kwargs.update({ + 'address_n': ADDRESS_N, + 'actions': [], + 'account': 0, + 'fee': 10000, + 'lock_time': 0, + 'expiry_height': 69121, + 'orchard_flags': 0, + 'orchard_value_balance': 0, + 'orchard_anchor': ANCHOR, + 'header_digest': header_digest(tx['tx_version'], tx['version_group_id'], + tx['branch_id'], 0, 69121), + # The empty bundle digest: ZIP 229 for v6, ZIP 244 for v5. + 'orchard_digest': hashlib.blake2b( + b'', digest_size=32, + person=b'ZTxIdOrchardH_v6' if version == 6 + else b'ZTxIdOrchardHash').digest(), + 'sapling_digest': EMPTY_SAPLING, + 'transparent_outputs': [output], + 'transparent_inputs': [inp], + 'transparent_digest': _transparent_sig_digest([inp], [output]), + 'return_transparent_signatures': True, + }) + return kwargs + + +class TestZcashTex(common.KeepKeyTest): + + def setUp(self): + super(TestZcashTex, self).setUp() + self.requires_firmware(TEX_FIRMWARE) + self.requires_fullFeature() + self.requires_message("ZcashSignPCZT") + self.setup_mnemonic_allallall() + + def _capture_confirm_text(self): + screens = [] + original = self.client.callback_ButtonRequest + + def capture(msg): + # A paged confirmation asks once per page; keep each one once. + title, body = self.client.debug.read_confirm_text() + if not screens or screens[-1][1:] != (title, body): + screens.append((msg.code, title, body)) + return original(msg) + + self.client.callback_ButtonRequest = capture + return screens + + def _one_time_pubkey(self): + node = self.client.get_public_node(ONE_TIME_PATH, coin_name='Zcash').node + return bytes(node.public_key) + + def _refused(self, kwargs, message): + with self.assertRaises(CallException) as ctx: + self.client.zcash_sign_pczt(**kwargs) + self.assertEqual(ctx.exception.args[1], message) + + def test_one_time_address_matches_librustzcash(self): + """The fixture's one-time address is the device's own key at + m/44'/133'/0'/2/0 (TransparentKeyScope::EPHEMERAL).""" + pubkey = self._one_time_pubkey() + self.assertEqual( + ONE_TIME_SCRIPT, + b'\x76\xa9\x14' + hashlib.new( + 'ripemd160', hashlib.sha256(pubkey).digest()).digest() + + b'\x88\xac') + + def test_step1_pays_the_own_one_time_address(self): + """With its path, the output is proven to pay the account's one-time + address and is shown as a transfer to yourself; the one wallet spend + is signed.""" + self.requires_firmware(TEX_FIRMWARE) + screens = self._capture_confirm_text() + output = {'amount': 1010000, 'script_pubkey': ONE_TIME_SCRIPT, + 'address_n': ONE_TIME_PATH} + + result = self.client.zcash_sign_pczt(**step1_kwargs(output)) + + self.assertIsInstance(result, zcash_proto.ZcashSignedPCZT) + self.assertEqual(len(result.signatures), 1) + self.assertEqual( + [(title, body) for code, title, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput], + [('Zcash Output', 'To your one-time address:\n%s\n' + 'Amount: 0.01010000 ZEC' % ONE_TIME_T1)]) + self.assertEqual(screens[-1][1:], + ('Zcash Fee', + 'Confirm transaction fee?\n0.00015000 ZEC')) + + def test_step1_without_a_path_is_a_plain_send(self): + """Without a path the device cannot know the address is the user's own, + so it is shown as a payment to that t1 address.""" + self.requires_firmware(TEX_FIRMWARE) + screens = self._capture_confirm_text() + output = {'amount': 1010000, 'script_pubkey': ONE_TIME_SCRIPT} + + result = self.client.zcash_sign_pczt(**step1_kwargs(output)) + + self.assertEqual(len(result.signatures), 1) + self.assertEqual( + [body for code, _, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput], + ['Send transparent ZEC?\n%s\nAmount: 0.01010000 ZEC' % ONE_TIME_T1]) + + def test_step1_refuses_a_path_that_does_not_pay_the_script(self): + self.requires_firmware(TEX_FIRMWARE) + for path, message in ( + (ONE_TIME_PATH[:4] + [1], + 'Transparent output script does not match path'), + (ONE_TIME_PATH[:3] + [0, 0], + 'Output path must be a one-time address'), + ([H + 44, H + 133, H + 1, 2, 0], + 'Account does not match approved session')): + output = {'amount': 1010000, 'script_pubkey': ONE_TIME_SCRIPT, + 'address_n': path} + self._refused(step1_kwargs(output), message) + + def test_step2_signs_a_transparent_only_transaction(self): + """No shielded action: the TEX recipient is shown as tex1..., then the + fee and the input, and the one ECDSA signature verifies under + librustzcash's sighash, in v6 and v5.""" + self.requires_firmware(TEX_FIRMWARE) + pubkey = self._one_time_pubkey() + key = VerifyingKey.from_string(pubkey, curve=SECP256k1) + for version in (6, 5): + tx, output, inp = step2_parts(version) + screens = self._capture_confirm_text() + + result, signatures = self.client.zcash_sign_pczt( + **step2_kwargs(version, output, inp)) + + self.assertIsInstance(result, zcash_proto.ZcashSignedPCZT) + self.assertEqual(len(result.signatures), 0) + self.assertEqual(len(signatures), 1) + self.assertTrue(key.verify_digest(signatures[0], tx['sighash'], + sigdecode=sigdecode_der)) + self.assertEqual( + [(title, body) for _, title, body in screens], + [('Zcash Transparent', 'Send transparent ZEC?\n' + 'Fee: 0.00010000 ZEC\nInputs: 1\nOutputs: 1'), + ('Zcash Output', 'Send to TEX address?\n%s\n' + 'Amount: 0.01000000 ZEC' % TEX_ADDRESS), + ('Zcash Fee', 'Confirm transaction fee?\n0.00010000 ZEC'), + ('Sign Input', + 'Sign transparent input?\nInput 1: 0.01010000 ZEC')]) + + def test_step2_without_the_hint_shows_the_t1_address(self): + """is_tex is only a display hint: without it the same key hash is shown + in its t1 form, and the transaction signs the same.""" + self.requires_firmware(TEX_FIRMWARE) + tx, output, inp = step2_parts(6) + del output['is_tex'] + screens = self._capture_confirm_text() + + _, signatures = self.client.zcash_sign_pczt( + **step2_kwargs(6, output, inp)) + + self.assertEqual(len(signatures), 1) + self.assertEqual( + [body for code, _, body in screens + if code == proto_types.ButtonRequest_ConfirmOutput], + ['Send transparent ZEC?\n%s\nAmount: 0.01000000 ZEC' % TEX_T1]) + + def test_transparent_only_refusals(self): + """A transparent-only request is refused with no signature when it + claims a shielded bundle, a non-empty Sapling digest, or an input path + outside the session account or past the one-time scope.""" + self.requires_firmware(TEX_FIRMWARE) + tx, output, inp = step2_parts(6) + other_account = dict(inp, address_n=[H + 44, H + 133, H + 1, 2, 0]) + change_3 = dict(inp, address_n=ONE_TIME_PATH[:3] + [3, 0]) + cases = ( + ({'orchard_digest': b'\x01' * 32}, + 'Transparent transaction must have empty shielded bundles'), + ({'orchard_value_balance': 5000}, + 'Transparent transaction must have empty shielded bundles'), + ({'sapling_digest': b'\x11' * 32}, 'Sapling not supported'), + ({'transparent_inputs': [other_account], + 'transparent_digest': _transparent_sig_digest( + [other_account], [output])}, + 'Account does not match approved session'), + ({'transparent_inputs': [change_3], + 'transparent_digest': _transparent_sig_digest( + [change_3], [output])}, + 'Change must be 0, 1 or 2'), + ) + for overrides, message in cases: + kwargs = step2_kwargs(6, output, inp) + kwargs.update(overrides) + self._refused(kwargs, message) + + def test_no_action_needs_a_transparent_input(self): + """n_actions 0 with nothing to sign is refused before any screen.""" + self.requires_firmware(TEX_FIRMWARE) + tx, output, inp = step2_parts(6) + kwargs = step2_kwargs(6, output, inp) + for key in ('actions', 'transparent_outputs', 'transparent_inputs', + 'return_transparent_signatures'): + del kwargs[key] + kwargs.update(n_actions=0, n_transparent_outputs=1) + response = self.client.call_raw(zcash_proto.ZcashSignPCZT(**kwargs)) + self.assertIsInstance(response, proto.Failure) + self.assertEqual(response.message, 'No actions specified') diff --git a/tests/test_msg_zcash_transparent_shielding.py b/tests/test_msg_zcash_transparent_shielding.py index cd65e193..730c9026 100644 --- a/tests/test_msg_zcash_transparent_shielding.py +++ b/tests/test_msg_zcash_transparent_shielding.py @@ -326,7 +326,7 @@ def test_rejects_short_path(self): def test_rejects_bad_change(self): self._assert_path_rejected( - [H + 44, H + 133, H, 7, 0], "change must be 0 or 1") + [H + 44, H + 133, H, 7, 0], "change must be 0") def test_rejects_hardened_index(self): self._assert_path_rejected( From 4bc241ee1413887dea9e1e6889313eb2f5690056 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 17:18:08 -0500 Subject: [PATCH 24/37] fix(eos): send a derived authority key as K1 EOSIO key types are K1=0, R1=1, WA=2. parse_authorization sent type 1 for every address_n key, so an updateauth or newaccount built here put the user's K1 key bytes on chain as an R1 key nobody holds, which can freeze the account. Send type 0; a raw PUB_R1_ key still sends 1. Every firmware accepts type 0 for a derived key, and firmware that checks the type refuses anything else. The updateauth, newaccount and JSON newaccount digests are re-derived with an independent EOSIO serializer; the old digests come out of the same serializer with type 1. --- keepkeylib/eos.py | 2 +- tests/test_msg_eos_signtx.py | 26 ++++++++++++------------ tests/unit/test_eos_updateauth_vector.py | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/keepkeylib/eos.py b/keepkeylib/eos.py index b7c68880..8e8aa416 100644 --- a/keepkeylib/eos.py +++ b/keepkeylib/eos.py @@ -257,7 +257,7 @@ def parse_authorization(data): keys.append( proto.EosAuthorizationKey( - type=1, + type=0, # K1: the key derived from address_n address_n=address_n, weight=int(key['weight']) ) diff --git a/tests/test_msg_eos_signtx.py b/tests/test_msg_eos_signtx.py index a59bcf5a..a939c7d6 100644 --- a/tests/test_msg_eos_signtx.py +++ b/tests/test_msg_eos_signtx.py @@ -247,7 +247,7 @@ def authorization(self, is_slip48): threshold=1, keys=[ proto.EosAuthorizationKey( - type=1, + type=0, address_n=parse_path("m/48'/4'/1'/0'/0'"), weight=1) ], @@ -267,7 +267,7 @@ def authorization(self, is_slip48): threshold=2, keys=[ proto.EosAuthorizationKey( - type=1, + type=0, address_n=parse_path("m/44'/194'/0'/0/0"), weight=2) ], @@ -331,7 +331,7 @@ def action_newaccount(self): threshold=1, keys=[ proto.EosAuthorizationKey( - type=1, + type=0, address_n=parse_path("m/44'/194'/0'/0/0"), weight=1) ], @@ -558,17 +558,16 @@ def test_updateauth(self): num_actions=1), [self.action_updateauth(False)]) - self.assertEqual(binascii.hexlify(res.hash), "0282c00575f451a47e99902ab2a51243499ea004df309148d1f2e23c007520b7") + self.assertEqual(binascii.hexlify(res.hash), "bf6f207d8f79500180aa5025d1f4a44d36bdd1136f093979d6975c20b2319a07") # Fork #568/#563: eos_hashAuthorization() used to emit accounts_count # wait entries instead of waits_count. This SLIP-48 vector has one # delegated account and zero waits, so older firmware hashes a phantom # 6-byte zero wait that was neither present nor confirmed on-device - # (the known-bad digest fb936ef1...). The fix is on the 7.15 and 7.16 + # (the known-bad digest d46ec843...). The fix is on the 7.15 and 7.16 # release heads; 7.14.3 is a bitcoin-only release and its full image - # keeps the 7.14.2 EOS serializer. 5938294e... is checked against two - # independent EOSIO ABI serializers and against mainnet updateauth - # transactions of the same shape. + # keeps the 7.14.2 EOS serializer. c94f1271... is checked against an + # independent EOSIO ABI serializer; the derived key is K1 (type 0). if not self.firmware_at_least("7.15.0"): self.skipTest("Firmware before 7.15.0 hashes a phantom updateauth " "wait (fork #568/#563)") @@ -581,7 +580,7 @@ def test_updateauth(self): num_actions=1), [self.action_updateauth(True)]) - self.assertEqual(binascii.hexlify(res.hash), "5938294e65cf9e8b5dd5f2b204503b4825f277e6f4a2d5ab7a55a31065a23af1") + self.assertEqual(binascii.hexlify(res.hash), "c94f1271f26492a4ca308a6cfae610ce41a3beb7354beb1747e2a2261c8c1973") def test_deleteauth(self): self.requires_fullFeature() @@ -637,7 +636,7 @@ def test_newaccount(self): num_actions=1), [self.action_newaccount()]) - self.assertEqual(binascii.hexlify(res.hash), "8e0accde9fb6529b5d72b4d9a9859e1dae0c6ae9a159bb1ea8c8f579f942c291") + self.assertEqual(binascii.hexlify(res.hash), "0f2c85cfc957eda7e37d6d006f98a56948fe4ab34ecea64d1a1462f4c6fbb387") def test_unknown_advanced(self): self.requires_fullFeature() @@ -1418,9 +1417,10 @@ def test_eos_signtx_newaccount(self): json.loads(data)) assert isinstance(actionResp, proto.EosSignedTx) - self.assertEqual(binascii.hexlify(actionResp.signature_r), "61317ec7dc5c6e62ef7e45b4961ad89fe748c5a9076f23e0c6afc90ad1f93f47") - self.assertEqual(binascii.hexlify(actionResp.signature_s), "056996515e6c6e28be136821859cf2092f719a8da25171ce6534c2bf9a013d3d") - self.assertEqual(actionResp.signature_v, 32) + self.assertEqual(binascii.hexlify(actionResp.hash), "584a5c3ded379ce4f3d3f6ea3364726e0e608214170eb9f7b25baa3010255cb1") + self.assertEqual(binascii.hexlify(actionResp.signature_r), "2db69cbe5f1600ca5d54c77b6f79426cde215531ee84d90eada8de310d07292f") + self.assertEqual(binascii.hexlify(actionResp.signature_s), "6d113bbdb017b95f4d9fb7e8fc3493b763576193409ed19d6e0d9b42c0cf754a") + self.assertEqual(actionResp.signature_v, 31) def test_eos_signtx_setcontract(self): self.requires_fullFeature() diff --git a/tests/unit/test_eos_updateauth_vector.py b/tests/unit/test_eos_updateauth_vector.py index 6762f8c8..690a6220 100644 --- a/tests/unit/test_eos_updateauth_vector.py +++ b/tests/unit/test_eos_updateauth_vector.py @@ -33,7 +33,7 @@ def var(x): auth = ( struct.pack(' Date: Wed, 7 Oct 2026 17:03:50 -0500 Subject: [PATCH 25/37] feat(eip712): send typed-data values over 1 KB in chunks A Safe MultiSend's data or a Snapshot proposal's body is often several KB, past the 1024-byte EthereumTypedDataValueAck. eip712_stream.value_ack() answers a ValueRequest whole up to 1 KB, and past that in 1 KB chunks: the first names the total length, each later one echoes the offset the device asked for. It chunks only for firmware that reports CAPABILITY_EIP712_CHUNKED_VALUES; older firmware would hash the first chunk alone, so without it a long value is refused on the host. Pin device-protocol at feat/eip712-chunked-values and regenerate the bindings. Add a real Safe MultiSend (4,900 B of data, digest equals its safeTxHash) and a real Snapshot proposal (3,929 B body, digest equals its proposal id) to the corpus, and device tests for boundary lengths, UTF-8 split across chunks, misframed chunks, a part declined mid-value and an unlimited approve heading chunked SafeTx data. --- device-protocol | 2 +- keepkeylib/eip712_stream.py | 69 ++++++- keepkeylib/messages_ethereum_pb2.py | 49 +++-- keepkeylib/messages_pb2.py | 286 ++++++++++++++-------------- tests/eip712_corpus.json | 203 ++++++++++++++++++++ tests/test_msg_eip712_corpus.py | 1 + tests/test_msg_eip712_streaming.py | 247 +++++++++++++++++++++++- 7 files changed, 682 insertions(+), 175 deletions(-) diff --git a/device-protocol b/device-protocol index 47f1fdc4..80987a78 160000 --- a/device-protocol +++ b/device-protocol @@ -1 +1 @@ -Subproject commit 47f1fdc4030f726fae3d694329a78be319d80e48 +Subproject commit 80987a7893be8a5898e17d0d7cbd04e7c80adcf6 diff --git a/keepkeylib/eip712_stream.py b/keepkeylib/eip712_stream.py index ab98b462..48310b6f 100644 --- a/keepkeylib/eip712_stream.py +++ b/keepkeylib/eip712_stream.py @@ -28,8 +28,14 @@ STRUCT = DataType.STRUCT # EthereumTypedDataValueAck.value max_size in messages-ethereum.options, and -# EIP712_MAX_LEAF on the device. +# EIP712_MAX_LEAF on the device: one ValueAck, and one chunk of a longer value. MAX_LEAF_BYTES = 1024 +# EIP712_MAX_VALUE: the longest `bytes` or `string` value the device takes in +# chunks. +MAX_VALUE_BYTES = 1024 * 1024 +# The Features capability (as common.capability_names() spells it) of +# firmware that takes chunked values. +CHUNKED_VALUES = 'eip712-chunked-values' MAX_IDENTIFIER_BYTES = 31 # Struct type names (EIP712_MAX_STRUCT_NAME - 1 on the device) may also use # ':' after the first character, as Hyperliquid's @@ -151,13 +157,14 @@ def _hex_bytes(value, what): return bytes(bytearray.fromhex(h)) -def encode_value(field, value): +def encode_value(field, value, max_bytes=MAX_LEAF_BYTES): """One leaf, as the exact bytes the device will hash and display. Raw big-endian at the declared width, never a decimal string: the device does no number parsing at all, which is what removes the old path's 2**63-1 ceiling and any chance of the two sides disagreeing about what a - decimal meant. + decimal meant. A dynamic value may be up to `max_bytes` long; past + MAX_LEAF_BYTES only value_ack() can put it on the wire. """ dt = field['data_type'] @@ -202,18 +209,18 @@ def encode_value(field, value): if len(b) != size: raise Eip712Error('bytes%d must be %d bytes, got %d' % (size, size, len(b))) return b - if len(b) > MAX_LEAF_BYTES: - raise Eip712Error('bytes value is %d bytes, over the %d-byte wire limit' - % (len(b), MAX_LEAF_BYTES)) + if len(b) > max_bytes: + raise Eip712Error('bytes value is %d bytes, over the %d-byte limit' + % (len(b), max_bytes)) return b if dt == STRING: if not isinstance(value, str): raise Eip712Error('string field must be a string') b = value.encode('utf-8') - if len(b) > MAX_LEAF_BYTES: - raise Eip712Error('string value is %d bytes, over the %d-byte wire limit' - % (len(b), MAX_LEAF_BYTES)) + if len(b) > max_bytes: + raise Eip712Error('string value is %d bytes, over the %d-byte limit' + % (len(b), max_bytes)) return b raise Eip712Error('Cannot encode data type %r as a leaf' % (dt,)) @@ -325,3 +332,47 @@ def build_struct_ack(members): for lvl in m['type']['array_levels']: entry.type.array_levels.append(lvl) return ack + + +def value_ack(typed_data, request, chunked=False): + """The EthereumTypedDataValueAck that answers a device ValueRequest. + + A value of up to MAX_LEAF_BYTES goes whole. A longer `bytes` or `string` + goes in MAX_LEAF_BYTES chunks: the first carries value_total_length, and + each later request names a value_offset that its answer echoes. The + device may read a string twice from offset 0 (it counts and checks it + before showing it), so every chunk is served by its offset alone. + + `chunked` must be True only when the firmware reports CHUNKED_VALUES. + Older firmware skips the chunk fields and would hash the first chunk + alone, so without the capability a long value is refused here. + """ + resolved = resolve_member_path(typed_data, list(request.member_path)) + ack = eth_proto.EthereumTypedDataValueAck() + if resolved[0] == 'length': + if request.HasField('value_offset'): + raise Eip712Error('Device asked for an array length in chunks') + ack.value = encode_array_length(resolved[1]) + return ack + value = encode_value(resolved[1], resolved[2], MAX_VALUE_BYTES) + if len(value) > MAX_LEAF_BYTES and request.member_path[0] == 0: + raise Eip712Error('A domain value of %d bytes cannot be signed; the ' + 'device takes at most %d' % (len(value), MAX_LEAF_BYTES)) + if request.HasField('value_offset'): + offset = request.value_offset + if len(value) <= MAX_LEAF_BYTES or offset >= len(value): + raise Eip712Error('Device asked for offset %d of a %d-byte value' + % (offset, len(value))) + ack.value = value[offset:offset + MAX_LEAF_BYTES] + ack.value_offset = offset + return ack + if len(value) <= MAX_LEAF_BYTES: + ack.value = value + return ack + if not chunked: + raise Eip712Error('A %d-byte value needs firmware that takes chunked ' + 'values (%s); this one takes at most %d bytes' + % (len(value), CHUNKED_VALUES, MAX_LEAF_BYTES)) + ack.value = value[:MAX_LEAF_BYTES] + ack.value_total_length = len(value) + return ack diff --git a/keepkeylib/messages_ethereum_pb2.py b/keepkeylib/messages_ethereum_pb2.py index 06c6e087..3bebe702 100644 --- a/keepkeylib/messages_ethereum_pb2.py +++ b/keepkeylib/messages_ethereum_pb2.py @@ -21,7 +21,7 @@ name='messages-ethereum.proto', package='', syntax='proto2', - serialized_pb=_b('\n\x17messages-ethereum.proto\x1a\x0btypes.proto\"=\n\x12\x45thereumGetAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x14\n\x0cshow_display\x18\x02 \x01(\x08\"7\n\x0f\x45thereumAddress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x02(\x0c\x12\x13\n\x0b\x61\x64\x64ress_str\x18\x02 \x01(\t\"\x95\x03\n\x0e\x45thereumSignTx\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\r\n\x05nonce\x18\x02 \x01(\x0c\x12\x11\n\tgas_price\x18\x03 \x01(\x0c\x12\x11\n\tgas_limit\x18\x04 \x01(\x0c\x12\n\n\x02to\x18\x05 \x01(\x0c\x12\r\n\x05value\x18\x06 \x01(\x0c\x12\x1a\n\x12\x64\x61ta_initial_chunk\x18\x07 \x01(\x0c\x12\x13\n\x0b\x64\x61ta_length\x18\x08 \x01(\r\x12\x14\n\x0cto_address_n\x18\t \x03(\r\x12(\n\x0c\x61\x64\x64ress_type\x18\n \x01(\x0e\x32\x12.OutputAddressType\x12\x10\n\x08\x63hain_id\x18\x0c \x01(\r\x12\x17\n\x0fmax_fee_per_gas\x18\r \x01(\x0c\x12 \n\x18max_priority_fee_per_gas\x18\x0e \x01(\x0c\x12\x13\n\x0btoken_value\x18\x64 \x01(\x0c\x12\x10\n\x08token_to\x18\x65 \x01(\x0c\x12\x16\n\x0etoken_shortcut\x18\x66 \x01(\t\x12\x0f\n\x07tx_type\x18g \x01(\r\x12\x0c\n\x04type\x18h \x01(\rJ\x04\x08\x0b\x10\x0c\"\x8c\x01\n\x11\x45thereumTxRequest\x12\x13\n\x0b\x64\x61ta_length\x18\x01 \x01(\r\x12\x13\n\x0bsignature_v\x18\x02 \x01(\r\x12\x13\n\x0bsignature_r\x18\x03 \x01(\x0c\x12\x13\n\x0bsignature_s\x18\x04 \x01(\x0c\x12\x0c\n\x04hash\x18\x05 \x01(\x0c\x12\x15\n\rsignature_der\x18\x06 \x01(\x0c\"#\n\rEthereumTxAck\x12\x12\n\ndata_chunk\x18\x01 \x01(\x0c\"V\n\x12\x45thereumTxMetadata\x12\x16\n\x0esigned_payload\x18\x01 \x01(\x0c\x12\x18\n\x10metadata_version\x18\x02 \x01(\r\x12\x0e\n\x06key_id\x18\x03 \x01(\r\"F\n\x13\x45thereumMetadataAck\x12\x16\n\x0e\x63lassification\x18\x01 \x02(\r\x12\x17\n\x0f\x64isplay_summary\x18\x02 \x01(\t\"\x8c\x01\n\x13LoadClearsignSigner\x12\x0e\n\x06key_id\x18\x01 \x01(\r\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\x12\r\n\x05\x61lias\x18\x03 \x01(\t\x12\x0c\n\x04icon\x18\x04 \x01(\x0c\x12\x12\n\nicon_width\x18\x05 \x01(\r\x12\x13\n\x0bicon_height\x18\x06 \x01(\r\x12\x0f\n\x07persist\x18\x07 \x01(\x08\"9\n\x13\x45thereumSignMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07message\x18\x02 \x02(\x0c\"L\n\x15\x45thereumVerifyMessage\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\">\n\x18\x45thereumMessageSignature\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"_\n\x15\x45thereumSignTypedHash\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x1d\n\x15\x64omain_separator_hash\x18\x02 \x02(\x0c\x12\x14\n\x0cmessage_hash\x18\x03 \x01(\x0c\"\x8b\x01\n\x1a\x45thereumTypedDataSignature\x12\x11\n\tsignature\x18\x01 \x02(\x0c\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x02(\t\x12\x1d\n\x15\x64omain_separator_hash\x18\x03 \x01(\x0c\x12\x14\n\x0chas_msg_hash\x18\x04 \x02(\x08\x12\x14\n\x0cmessage_hash\x18\x05 \x01(\x0c\"\x85\x01\n\x16\x45thereum712TypesValues\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x13\n\x0b\x65ip712types\x18\x02 \x02(\t\x12\x17\n\x0f\x65ip712primetype\x18\x03 \x02(\t\x12\x12\n\neip712data\x18\x04 \x02(\t\x12\x16\n\x0e\x65ip712typevals\x18\x05 \x02(\r\"b\n\x15\x45thereumSignTypedData\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x14\n\x0cprimary_type\x18\x02 \x02(\t\x12 \n\x12metamask_v4_compat\x18\x03 \x01(\x08:\x04true\".\n\x1e\x45thereumTypedDataStructRequest\x12\x0c\n\x04name\x18\x01 \x02(\t\"\xbe\x03\n\x1a\x45thereumTypedDataStructAck\x12\x41\n\x07members\x18\x01 \x03(\x0b\x32\x30.EthereumTypedDataStructAck.EthereumStructMember\x1a\x61\n\x14\x45thereumStructMember\x12;\n\x04type\x18\x01 \x02(\x0b\x32-.EthereumTypedDataStructAck.EthereumFieldType\x12\x0c\n\x04name\x18\x02 \x02(\t\x1a\x8d\x01\n\x11\x45thereumFieldType\x12?\n\tdata_type\x18\x01 \x02(\x0e\x32,.EthereumTypedDataStructAck.EthereumDataType\x12\x0c\n\x04size\x18\x02 \x01(\r\x12\x13\n\x0bstruct_name\x18\x03 \x01(\t\x12\x14\n\x0c\x61rray_levels\x18\x04 \x03(\r\"j\n\x10\x45thereumDataType\x12\x08\n\x04UINT\x10\x01\x12\x07\n\x03INT\x10\x02\x12\t\n\x05\x42YTES\x10\x03\x12\n\n\x06STRING\x10\x04\x12\x08\n\x04\x42OOL\x10\x05\x12\x0b\n\x07\x41\x44\x44RESS\x10\x06\x12\t\n\x05\x41RRAY\x10\x07\x12\n\n\x06STRUCT\x10\x08\"4\n\x1d\x45thereumTypedDataValueRequest\x12\x13\n\x0bmember_path\x18\x01 \x03(\r\"*\n\x19\x45thereumTypedDataValueAck\x12\r\n\x05value\x18\x01 \x02(\x0c\"h\n\x1b\x45thereumClearSignDefinition\x12\x15\n\rdefinition_id\x18\x01 \x02(\x0c\x12\x0e\n\x06offset\x18\x02 \x02(\r\x12\x14\n\x0ctotal_length\x18\x03 \x02(\r\x12\x0c\n\x04\x64\x61ta\x18\x04 \x02(\x0c\"^\n\x1e\x45thereumClearSignDefinitionAck\x12\x15\n\rdefinition_id\x18\x01 \x02(\x0c\x12\x13\n\x0bnext_offset\x18\x02 \x02(\r\x12\x10\n\x08\x63omplete\x18\x03 \x02(\x08\"\xef\x01\n\"EthereumClearSignDefinitionRequest\x12.\n\x04kind\x18\x01 \x02(\x0e\x32 .EthereumClearSignDefinitionKind\x12\x10\n\x08\x63hain_id\x18\x02 \x02(\x04\x12\x18\n\x10\x63ontract_address\x18\x03 \x01(\x0c\x12\x1d\n\x15selector_or_type_hash\x18\x04 \x01(\x0c\x12\x15\n\rdefinition_id\x18\x05 \x01(\x0c\x12\x0e\n\x06offset\x18\x06 \x02(\r\x12\x0e\n\x06length\x18\x07 \x02(\r\x12\x17\n\x0frecursion_depth\x18\x08 \x01(\r\"m\n EthereumClearSignDefinitionChunk\x12\x15\n\rdefinition_id\x18\x01 \x02(\x0c\x12\x0e\n\x06offset\x18\x02 \x02(\r\x12\x14\n\x0ctotal_length\x18\x03 \x02(\r\x12\x0c\n\x04\x64\x61ta\x18\x04 \x02(\x0c*s\n\x1f\x45thereumClearSignDefinitionKind\x12\x14\n\x10\x45RC7730_CALLDATA\x10\x01\x12\x12\n\x0e\x45RC7730_EIP712\x10\x02\x12\x11\n\rERC7730_TOKEN\x10\x03\x12\x13\n\x0f\x45RC7730_NETWORK\x10\x04\x42\x34\n\x1a\x63om.keepkey.deviceprotocolB\x16KeepKeyMessageEthereum') + serialized_pb=_b('\n\x17messages-ethereum.proto\x1a\x0btypes.proto\"=\n\x12\x45thereumGetAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x14\n\x0cshow_display\x18\x02 \x01(\x08\"7\n\x0f\x45thereumAddress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x02(\x0c\x12\x13\n\x0b\x61\x64\x64ress_str\x18\x02 \x01(\t\"\x95\x03\n\x0e\x45thereumSignTx\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\r\n\x05nonce\x18\x02 \x01(\x0c\x12\x11\n\tgas_price\x18\x03 \x01(\x0c\x12\x11\n\tgas_limit\x18\x04 \x01(\x0c\x12\n\n\x02to\x18\x05 \x01(\x0c\x12\r\n\x05value\x18\x06 \x01(\x0c\x12\x1a\n\x12\x64\x61ta_initial_chunk\x18\x07 \x01(\x0c\x12\x13\n\x0b\x64\x61ta_length\x18\x08 \x01(\r\x12\x14\n\x0cto_address_n\x18\t \x03(\r\x12(\n\x0c\x61\x64\x64ress_type\x18\n \x01(\x0e\x32\x12.OutputAddressType\x12\x10\n\x08\x63hain_id\x18\x0c \x01(\r\x12\x17\n\x0fmax_fee_per_gas\x18\r \x01(\x0c\x12 \n\x18max_priority_fee_per_gas\x18\x0e \x01(\x0c\x12\x13\n\x0btoken_value\x18\x64 \x01(\x0c\x12\x10\n\x08token_to\x18\x65 \x01(\x0c\x12\x16\n\x0etoken_shortcut\x18\x66 \x01(\t\x12\x0f\n\x07tx_type\x18g \x01(\r\x12\x0c\n\x04type\x18h \x01(\rJ\x04\x08\x0b\x10\x0c\"\x8c\x01\n\x11\x45thereumTxRequest\x12\x13\n\x0b\x64\x61ta_length\x18\x01 \x01(\r\x12\x13\n\x0bsignature_v\x18\x02 \x01(\r\x12\x13\n\x0bsignature_r\x18\x03 \x01(\x0c\x12\x13\n\x0bsignature_s\x18\x04 \x01(\x0c\x12\x0c\n\x04hash\x18\x05 \x01(\x0c\x12\x15\n\rsignature_der\x18\x06 \x01(\x0c\"#\n\rEthereumTxAck\x12\x12\n\ndata_chunk\x18\x01 \x01(\x0c\"V\n\x12\x45thereumTxMetadata\x12\x16\n\x0esigned_payload\x18\x01 \x01(\x0c\x12\x18\n\x10metadata_version\x18\x02 \x01(\r\x12\x0e\n\x06key_id\x18\x03 \x01(\r\"F\n\x13\x45thereumMetadataAck\x12\x16\n\x0e\x63lassification\x18\x01 \x02(\r\x12\x17\n\x0f\x64isplay_summary\x18\x02 \x01(\t\"\x8c\x01\n\x13LoadClearsignSigner\x12\x0e\n\x06key_id\x18\x01 \x01(\r\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\x12\r\n\x05\x61lias\x18\x03 \x01(\t\x12\x0c\n\x04icon\x18\x04 \x01(\x0c\x12\x12\n\nicon_width\x18\x05 \x01(\r\x12\x13\n\x0bicon_height\x18\x06 \x01(\r\x12\x0f\n\x07persist\x18\x07 \x01(\x08\"9\n\x13\x45thereumSignMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07message\x18\x02 \x02(\x0c\"L\n\x15\x45thereumVerifyMessage\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\">\n\x18\x45thereumMessageSignature\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"_\n\x15\x45thereumSignTypedHash\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x1d\n\x15\x64omain_separator_hash\x18\x02 \x02(\x0c\x12\x14\n\x0cmessage_hash\x18\x03 \x01(\x0c\"\x8b\x01\n\x1a\x45thereumTypedDataSignature\x12\x11\n\tsignature\x18\x01 \x02(\x0c\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x02(\t\x12\x1d\n\x15\x64omain_separator_hash\x18\x03 \x01(\x0c\x12\x14\n\x0chas_msg_hash\x18\x04 \x02(\x08\x12\x14\n\x0cmessage_hash\x18\x05 \x01(\x0c\"\x85\x01\n\x16\x45thereum712TypesValues\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x13\n\x0b\x65ip712types\x18\x02 \x02(\t\x12\x17\n\x0f\x65ip712primetype\x18\x03 \x02(\t\x12\x12\n\neip712data\x18\x04 \x02(\t\x12\x16\n\x0e\x65ip712typevals\x18\x05 \x02(\r\"b\n\x15\x45thereumSignTypedData\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x14\n\x0cprimary_type\x18\x02 \x02(\t\x12 \n\x12metamask_v4_compat\x18\x03 \x01(\x08:\x04true\".\n\x1e\x45thereumTypedDataStructRequest\x12\x0c\n\x04name\x18\x01 \x02(\t\"\xbe\x03\n\x1a\x45thereumTypedDataStructAck\x12\x41\n\x07members\x18\x01 \x03(\x0b\x32\x30.EthereumTypedDataStructAck.EthereumStructMember\x1a\x61\n\x14\x45thereumStructMember\x12;\n\x04type\x18\x01 \x02(\x0b\x32-.EthereumTypedDataStructAck.EthereumFieldType\x12\x0c\n\x04name\x18\x02 \x02(\t\x1a\x8d\x01\n\x11\x45thereumFieldType\x12?\n\tdata_type\x18\x01 \x02(\x0e\x32,.EthereumTypedDataStructAck.EthereumDataType\x12\x0c\n\x04size\x18\x02 \x01(\r\x12\x13\n\x0bstruct_name\x18\x03 \x01(\t\x12\x14\n\x0c\x61rray_levels\x18\x04 \x03(\r\"j\n\x10\x45thereumDataType\x12\x08\n\x04UINT\x10\x01\x12\x07\n\x03INT\x10\x02\x12\t\n\x05\x42YTES\x10\x03\x12\n\n\x06STRING\x10\x04\x12\x08\n\x04\x42OOL\x10\x05\x12\x0b\n\x07\x41\x44\x44RESS\x10\x06\x12\t\n\x05\x41RRAY\x10\x07\x12\n\n\x06STRUCT\x10\x08\"J\n\x1d\x45thereumTypedDataValueRequest\x12\x13\n\x0bmember_path\x18\x01 \x03(\r\x12\x14\n\x0cvalue_offset\x18\x02 \x01(\r\"\\\n\x19\x45thereumTypedDataValueAck\x12\r\n\x05value\x18\x01 \x02(\x0c\x12\x1a\n\x12value_total_length\x18\x02 \x01(\r\x12\x14\n\x0cvalue_offset\x18\x03 \x01(\r\"h\n\x1b\x45thereumClearSignDefinition\x12\x15\n\rdefinition_id\x18\x01 \x02(\x0c\x12\x0e\n\x06offset\x18\x02 \x02(\r\x12\x14\n\x0ctotal_length\x18\x03 \x02(\r\x12\x0c\n\x04\x64\x61ta\x18\x04 \x02(\x0c\"^\n\x1e\x45thereumClearSignDefinitionAck\x12\x15\n\rdefinition_id\x18\x01 \x02(\x0c\x12\x13\n\x0bnext_offset\x18\x02 \x02(\r\x12\x10\n\x08\x63omplete\x18\x03 \x02(\x08\"\xef\x01\n\"EthereumClearSignDefinitionRequest\x12.\n\x04kind\x18\x01 \x02(\x0e\x32 .EthereumClearSignDefinitionKind\x12\x10\n\x08\x63hain_id\x18\x02 \x02(\x04\x12\x18\n\x10\x63ontract_address\x18\x03 \x01(\x0c\x12\x1d\n\x15selector_or_type_hash\x18\x04 \x01(\x0c\x12\x15\n\rdefinition_id\x18\x05 \x01(\x0c\x12\x0e\n\x06offset\x18\x06 \x02(\r\x12\x0e\n\x06length\x18\x07 \x02(\r\x12\x17\n\x0frecursion_depth\x18\x08 \x01(\r\"m\n EthereumClearSignDefinitionChunk\x12\x15\n\rdefinition_id\x18\x01 \x02(\x0c\x12\x0e\n\x06offset\x18\x02 \x02(\r\x12\x14\n\x0ctotal_length\x18\x03 \x02(\r\x12\x0c\n\x04\x64\x61ta\x18\x04 \x02(\x0c*s\n\x1f\x45thereumClearSignDefinitionKind\x12\x14\n\x10\x45RC7730_CALLDATA\x10\x01\x12\x12\n\x0e\x45RC7730_EIP712\x10\x02\x12\x11\n\rERC7730_TOKEN\x10\x03\x12\x13\n\x0f\x45RC7730_NETWORK\x10\x04\x42\x34\n\x1a\x63om.keepkey.deviceprotocolB\x16KeepKeyMessageEthereum') , dependencies=[types__pb2.DESCRIPTOR,]) @@ -50,8 +50,8 @@ ], containing_type=None, options=None, - serialized_start=2877, - serialized_end=2992, + serialized_start=2949, + serialized_end=3064, ) _sym_db.RegisterEnumDescriptor(_ETHEREUMCLEARSIGNDEFINITIONKIND) @@ -1082,6 +1082,13 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value_offset', full_name='EthereumTypedDataValueRequest.value_offset', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), ], extensions=[ ], @@ -1095,7 +1102,7 @@ oneofs=[ ], serialized_start=2224, - serialized_end=2276, + serialized_end=2298, ) @@ -1113,6 +1120,20 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value_total_length', full_name='EthereumTypedDataValueAck.value_total_length', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value_offset', full_name='EthereumTypedDataValueAck.value_offset', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), ], extensions=[ ], @@ -1125,8 +1146,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2278, - serialized_end=2320, + serialized_start=2300, + serialized_end=2392, ) @@ -1177,8 +1198,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2322, - serialized_end=2426, + serialized_start=2394, + serialized_end=2498, ) @@ -1222,8 +1243,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2428, - serialized_end=2522, + serialized_start=2500, + serialized_end=2594, ) @@ -1302,8 +1323,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2525, - serialized_end=2764, + serialized_start=2597, + serialized_end=2836, ) @@ -1354,8 +1375,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2766, - serialized_end=2875, + serialized_start=2838, + serialized_end=2947, ) _ETHEREUMSIGNTX.fields_by_name['address_type'].enum_type = types__pb2._OUTPUTADDRESSTYPE diff --git a/keepkeylib/messages_pb2.py b/keepkeylib/messages_pb2.py index f9365e9c..d07232a2 100644 --- a/keepkeylib/messages_pb2.py +++ b/keepkeylib/messages_pb2.py @@ -21,7 +21,7 @@ name='messages.proto', package='', syntax='proto2', - serialized_pb=_b('\n\x0emessages.proto\x1a\x0btypes.proto\"\x0c\n\nInitialize\"\r\n\x0bGetFeatures\"\x93\r\n\x08\x46\x65\x61tures\x12\x0e\n\x06vendor\x18\x01 \x01(\t\x12\x15\n\rmajor_version\x18\x02 \x01(\r\x12\x15\n\rminor_version\x18\x03 \x01(\r\x12\x15\n\rpatch_version\x18\x04 \x01(\r\x12\x17\n\x0f\x62ootloader_mode\x18\x05 \x01(\x08\x12\x11\n\tdevice_id\x18\x06 \x01(\t\x12\x16\n\x0epin_protection\x18\x07 \x01(\x08\x12\x1d\n\x15passphrase_protection\x18\x08 \x01(\x08\x12\x10\n\x08language\x18\t \x01(\t\x12\r\n\x05label\x18\n \x01(\t\x12\x18\n\x05\x63oins\x18\x0b \x03(\x0b\x32\t.CoinType\x12\x13\n\x0binitialized\x18\x0c \x01(\x08\x12\x10\n\x08revision\x18\r \x01(\x0c\x12\x17\n\x0f\x62ootloader_hash\x18\x0e \x01(\x0c\x12\x10\n\x08imported\x18\x0f \x01(\x08\x12\x12\n\npin_cached\x18\x10 \x01(\x08\x12\x19\n\x11passphrase_cached\x18\x11 \x01(\x08\x12\x1d\n\x08policies\x18\x12 \x03(\x0b\x32\x0b.PolicyType\x12\r\n\x05model\x18\x15 \x01(\t\x12\x18\n\x10\x66irmware_variant\x18\x16 \x01(\t\x12\x15\n\rfirmware_hash\x18\x17 \x01(\x0c\x12\x11\n\tno_backup\x18\x18 \x01(\x08\x12\x1c\n\x14wipe_code_protection\x18\x19 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x1a \x01(\r\x12\x18\n\x10supports_taproot\x18\x1b \x01(\x08\x12\x1b\n\x13supports_dice_modes\x18\x1c \x01(\x08\x12\'\n\x1fsupports_solana_lut_attestation\x18\x1d \x01(\x08\x12.\n\x0c\x63\x61pabilities\x18\x1e \x03(\x0e\x32\x14.Features.CapabilityB\x02\x10\x01\"\xd6\x07\n\nCapability\x12\x1a\n\x16\x43\x41PABILITY_UNSPECIFIED\x10\x00\x12#\n\x1f\x43\x41PABILITY_ENTROPY_AUDIT_BUDGET\x10\x01\x12-\n)CAPABILITY_ERC20_UNLIMITED_APPROVE_REVIEW\x10\x02\x12,\n(CAPABILITY_ERC20_UNLIMITED_PERMIT_REVIEW\x10\x03\x12%\n!CAPABILITY_ERC7730_RUNTIME_REVIEW\x10\x04\x12#\n\x1f\x43\x41PABILITY_EVM_CERTIFIED_INTENT\x10\x05\x12$\n CAPABILITY_EVM_MAX_AMOUNT_REVIEW\x10\x06\x12\x1e\n\x1a\x43\x41PABILITY_EVM_TX_METADATA\x10\x07\x12\'\n#CAPABILITY_EVM_UNKNOWN_TOKEN_REVIEW\x10\x08\x12\"\n\x1e\x43\x41PABILITY_HIVE_RELEASE_REVIEW\x10\t\x12(\n$CAPABILITY_LEGACY_EVM_ROUTER_SIGNING\x10\n\x12\"\n\x1e\x43\x41PABILITY_MAYA_SINGLE_MESSAGE\x10\x0b\x12\"\n\x1e\x43\x41PABILITY_OSMOSIS_WIRE_GUARDS\x10\x0c\x12\x1d\n\x19\x43\x41PABILITY_PERMIT2_REVIEW\x10\r\x12%\n!CAPABILITY_PROMPT_WORKFLOW_UNWIND\x10\x0e\x12&\n\"CAPABILITY_PROTECTED_PING_PRESENCE\x10\x0f\x12!\n\x1d\x43\x41PABILITY_RIPPLE_MEMO_POLICY\x10\x10\x12%\n!CAPABILITY_SESSION_TRUST_LIFETIME\x10\x11\x12&\n\"CAPABILITY_SOLANA_CERTIFIED_REVIEW\x10\x12\x12%\n!CAPABILITY_SOLANA_LUT_ATTESTATION\x10\x13\x12$\n CAPABILITY_SOLANA_RUNTIME_REVIEW\x10\x14\x12\x1e\n\x1a\x43\x41PABILITY_STORAGE_V19_KDF\x10\x15\x12\"\n\x1e\x43\x41PABILITY_TENDERMINT_PROGRESS\x10\x16\x12\"\n\x1e\x43\x41PABILITY_SAFE_RESET_CEREMONY\x10\x17\x12\"\n\x1e\x43\x41PABILITY_THOR_DEPOSIT_REVIEW\x10\x18\x12 \n\x1c\x43\x41PABILITY_TRON_TRC20_REVIEW\x10\x19\"*\n\x0cGetCoinTable\x12\r\n\x05start\x18\x01 \x01(\r\x12\x0b\n\x03\x65nd\x18\x02 \x01(\r\"L\n\tCoinTable\x12\x18\n\x05table\x18\x01 \x03(\x0b\x32\t.CoinType\x12\x11\n\tnum_coins\x18\x02 \x01(\r\x12\x12\n\nchunk_size\x18\x03 \x01(\r\"\x0e\n\x0c\x43learSession\"y\n\rApplySettings\x12\x10\n\x08language\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x16\n\x0euse_passphrase\x18\x03 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x04 \x01(\r\x12\x13\n\x0bu2f_counter\x18\x05 \x01(\r\"\x1b\n\tChangePin\x12\x0e\n\x06remove\x18\x01 \x01(\x08\"\x87\x01\n\x04Ping\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x19\n\x11\x62utton_protection\x18\x02 \x01(\x08\x12\x16\n\x0epin_protection\x18\x03 \x01(\x08\x12\x1d\n\x15passphrase_protection\x18\x04 \x01(\x08\x12\x1c\n\x14wipe_code_protection\x18\x05 \x01(\x08\"\x1a\n\x07Success\x12\x0f\n\x07message\x18\x01 \x01(\t\"6\n\x07\x46\x61ilure\x12\x1a\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x0c.FailureType\x12\x0f\n\x07message\x18\x02 \x01(\t\"?\n\rButtonRequest\x12 \n\x04\x63ode\x18\x01 \x01(\x0e\x32\x12.ButtonRequestType\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x0b\n\tButtonAck\"7\n\x10PinMatrixRequest\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.PinMatrixRequestType\"\x1b\n\x0cPinMatrixAck\x12\x0b\n\x03pin\x18\x01 \x02(\t\"\x08\n\x06\x43\x61ncel\"\x13\n\x11PassphraseRequest\"#\n\rPassphraseAck\x12\x12\n\npassphrase\x18\x01 \x02(\t\"\x1a\n\nGetEntropy\x12\x0c\n\x04size\x18\x01 \x02(\r\"\x1a\n\x07\x45ntropy\x12\x0f\n\x07\x65ntropy\x18\x01 \x02(\x0c\"\xa2\x01\n\x0cGetPublicKey\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x18\n\x10\x65\x63\x64sa_curve_name\x18\x02 \x01(\t\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\x12\x1a\n\tcoin_name\x18\x04 \x01(\t:\x07\x42itcoin\x12\x33\n\x0bscript_type\x18\x05 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"4\n\tPublicKey\x12\x19\n\x04node\x18\x01 \x02(\x0b\x32\x0b.HDNodeType\x12\x0c\n\x04xpub\x18\x02 \x01(\t\"\xb3\x01\n\nGetAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x1a\n\tcoin_name\x18\x02 \x01(\t:\x07\x42itcoin\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\x12+\n\x08multisig\x18\x04 \x01(\x0b\x32\x19.MultisigRedeemScriptType\x12\x33\n\x0bscript_type\x18\x05 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"\x1a\n\x07\x41\x64\x64ress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x02(\t\"\x0c\n\nWipeDevice\"\xbb\x01\n\nLoadDevice\x12\x10\n\x08mnemonic\x18\x01 \x01(\t\x12\x19\n\x04node\x18\x02 \x01(\x0b\x32\x0b.HDNodeType\x12\x0b\n\x03pin\x18\x03 \x01(\t\x12\x1d\n\x15passphrase_protection\x18\x04 \x01(\x08\x12\x19\n\x08language\x18\x05 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x06 \x01(\t\x12\x15\n\rskip_checksum\x18\x07 \x01(\x08\x12\x13\n\x0bu2f_counter\x18\x08 \x01(\r\"\x8a\x02\n\x0bResetDevice\x12\x16\n\x0e\x64isplay_random\x18\x01 \x01(\x08\x12\x15\n\x08strength\x18\x02 \x01(\r:\x03\x32\x35\x36\x12\x1d\n\x15passphrase_protection\x18\x03 \x01(\x08\x12\x16\n\x0epin_protection\x18\x04 \x01(\x08\x12\x19\n\x08language\x18\x05 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x06 \x01(\t\x12\x11\n\tno_backup\x18\x07 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x08 \x01(\r\x12\x13\n\x0bu2f_counter\x18\t \x01(\r\x12\x14\n\x0c\x64ice_entropy\x18\n \x01(\x08\x12\x11\n\tdice_only\x18\x0b \x01(\x08\"\x10\n\x0e\x45ntropyRequest\"\x1d\n\nEntropyAck\x12\x0f\n\x07\x65ntropy\x18\x01 \x01(\x0c\"\xff\x01\n\x0eRecoveryDevice\x12\x12\n\nword_count\x18\x01 \x01(\r\x12\x1d\n\x15passphrase_protection\x18\x02 \x01(\x08\x12\x16\n\x0epin_protection\x18\x03 \x01(\x08\x12\x19\n\x08language\x18\x04 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x05 \x01(\t\x12\x18\n\x10\x65nforce_wordlist\x18\x06 \x01(\x08\x12\x1c\n\x14use_character_cipher\x18\x07 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x08 \x01(\r\x12\x13\n\x0bu2f_counter\x18\t \x01(\r\x12\x0f\n\x07\x64ry_run\x18\n \x01(\x08\"\r\n\x0bWordRequest\"\x17\n\x07WordAck\x12\x0c\n\x04word\x18\x01 \x02(\t\";\n\x10\x43haracterRequest\x12\x10\n\x08word_pos\x18\x01 \x02(\r\x12\x15\n\rcharacter_pos\x18\x02 \x02(\r\"?\n\x0c\x43haracterAck\x12\x11\n\tcharacter\x18\x01 \x01(\t\x12\x0e\n\x06\x64\x65lete\x18\x02 \x01(\x08\x12\x0c\n\x04\x64one\x18\x03 \x01(\x08\"\x82\x01\n\x0bSignMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07message\x18\x02 \x02(\x0c\x12\x1a\n\tcoin_name\x18\x03 \x01(\t:\x07\x42itcoin\x12\x33\n\x0bscript_type\x18\x04 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"`\n\rVerifyMessage\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\x12\x1a\n\tcoin_name\x18\x04 \x01(\t:\x07\x42itcoin\"6\n\x10MessageSignature\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"v\n\x0e\x45ncryptMessage\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\x12\x14\n\x0c\x64isplay_only\x18\x03 \x01(\x08\x12\x11\n\taddress_n\x18\x04 \x03(\r\x12\x1a\n\tcoin_name\x18\x05 \x01(\t:\x07\x42itcoin\"@\n\x10\x45ncryptedMessage\x12\r\n\x05nonce\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\x12\x0c\n\x04hmac\x18\x03 \x01(\x0c\"Q\n\x0e\x44\x65\x63ryptMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\r\n\x05nonce\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\x12\x0c\n\x04hmac\x18\x04 \x01(\x0c\"4\n\x10\x44\x65\x63ryptedMessage\x12\x0f\n\x07message\x18\x01 \x01(\x0c\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"\x8c\x01\n\x0e\x43ipherKeyValue\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\x0c\x12\x0f\n\x07\x65ncrypt\x18\x04 \x01(\x08\x12\x16\n\x0e\x61sk_on_encrypt\x18\x05 \x01(\x08\x12\x16\n\x0e\x61sk_on_decrypt\x18\x06 \x01(\x08\x12\n\n\x02iv\x18\x07 \x01(\x0c\"!\n\x10\x43ipheredKeyValue\x12\r\n\x05value\x18\x01 \x01(\x0c\"5\n\x10GetBip85Mnemonic\x12\x12\n\nword_count\x18\x01 \x02(\r\x12\r\n\x05index\x18\x02 \x02(\r\"!\n\rBip85Mnemonic\x12\x10\n\x08mnemonic\x18\x01 \x02(\t\"\xce\x01\n\x06SignTx\x12\x15\n\routputs_count\x18\x01 \x02(\r\x12\x14\n\x0cinputs_count\x18\x02 \x02(\r\x12\x1a\n\tcoin_name\x18\x03 \x01(\t:\x07\x42itcoin\x12\x12\n\x07version\x18\x04 \x01(\r:\x01\x31\x12\x14\n\tlock_time\x18\x05 \x01(\r:\x01\x30\x12\x0e\n\x06\x65xpiry\x18\x06 \x01(\r\x12\x14\n\x0coverwintered\x18\x07 \x01(\x08\x12\x18\n\x10version_group_id\x18\x08 \x01(\r\x12\x11\n\tbranch_id\x18\n \x01(\r\"\x85\x01\n\tTxRequest\x12\"\n\x0crequest_type\x18\x01 \x01(\x0e\x32\x0c.RequestType\x12&\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32\x15.TxRequestDetailsType\x12,\n\nserialized\x18\x03 \x01(\x0b\x32\x18.TxRequestSerializedType\"%\n\x05TxAck\x12\x1c\n\x02tx\x18\x01 \x01(\x0b\x32\x10.TransactionType\"+\n\x08RawTxAck\x12\x1f\n\x02tx\x18\x01 \x01(\x0b\x32\x13.RawTransactionType\"}\n\x0cSignIdentity\x12\x1f\n\x08identity\x18\x01 \x01(\x0b\x32\r.IdentityType\x12\x18\n\x10\x63hallenge_hidden\x18\x02 \x01(\x0c\x12\x18\n\x10\x63hallenge_visual\x18\x03 \x01(\t\x12\x18\n\x10\x65\x63\x64sa_curve_name\x18\x04 \x01(\t\"H\n\x0eSignedIdentity\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x12\n\npublic_key\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\",\n\rApplyPolicies\x12\x1b\n\x06policy\x18\x01 \x03(\x0b\x32\x0b.PolicyType\"?\n\tFlashHash\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0e\n\x06length\x18\x02 \x01(\r\x12\x11\n\tchallenge\x18\x03 \x01(\x0c\":\n\nFlashWrite\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\r\n\x05\x65rase\x18\x03 \x01(\x08\"!\n\x11\x46lashHashResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"5\n\x12\x44\x65\x62ugLinkFlashDump\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0e\n\x06length\x18\x02 \x01(\r\"*\n\x1a\x44\x65\x62ugLinkFlashDumpResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x0b\n\tSoftReset\"\x0f\n\rFirmwareErase\"7\n\x0e\x46irmwareUpload\x12\x14\n\x0cpayload_hash\x18\x01 \x02(\x0c\x12\x0f\n\x07payload\x18\x02 \x02(\x0c\"2\n\x11\x44\x65\x62ugLinkDecision\x12\x0e\n\x06yes_no\x18\x01 \x02(\x08\x12\r\n\x05input\x18\x02 \x01(\t\"\x13\n\x11\x44\x65\x62ugLinkGetState\"\x99\x03\n\x0e\x44\x65\x62ugLinkState\x12\x0e\n\x06layout\x18\x01 \x01(\x0c\x12\x0b\n\x03pin\x18\x02 \x01(\t\x12\x0e\n\x06matrix\x18\x03 \x01(\t\x12\x10\n\x08mnemonic\x18\x04 \x01(\t\x12\x19\n\x04node\x18\x05 \x01(\x0b\x32\x0b.HDNodeType\x12\x1d\n\x15passphrase_protection\x18\x06 \x01(\x08\x12\x12\n\nreset_word\x18\x07 \x01(\t\x12\x15\n\rreset_entropy\x18\x08 \x01(\x0c\x12\x1a\n\x12recovery_fake_word\x18\t \x01(\t\x12\x19\n\x11recovery_word_pos\x18\n \x01(\r\x12\x17\n\x0frecovery_cipher\x18\x0b \x01(\t\x12$\n\x1crecovery_auto_completed_word\x18\x0c \x01(\t\x12\x15\n\rfirmware_hash\x18\r \x01(\x0c\x12\x14\n\x0cstorage_hash\x18\x0e \x01(\x0c\x12\x13\n\x0b\x64ice_digest\x18\x0f \x01(\x0c\x12\x15\n\rconfirm_title\x18\x10 \x01(\t\x12\x14\n\x0c\x63onfirm_body\x18\x11 \x01(\t\"\x0f\n\rDebugLinkStop\";\n\x0c\x44\x65\x62ugLinkLog\x12\r\n\x05level\x18\x01 \x01(\r\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12\x0c\n\x04text\x18\x03 \x01(\t\"\x15\n\x13\x44\x65\x62ugLinkFillConfig\" \n\x0e\x43hangeWipeCode\x12\x0e\n\x06remove\x18\x01 \x01(\x08\"\x1f\n\x1d\x43learsignAttestorGetPublicKey\"0\n\x1a\x43learsignAttestorPublicKey\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\"(\n\x15\x43learsignAttestorSign\x12\x0f\n\x07payload\x18\x01 \x01(\x0c\"C\n\x1a\x43learsignAttestorSignature\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\x12\n\npublic_key\x18\x02 \x01(\x0c*\xc3\x46\n\x0bMessageType\x12 \n\x16MessageType_Initialize\x10\x00\x1a\x04\x90\xb5\x18\x01\x12\x1a\n\x10MessageType_Ping\x10\x01\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Success\x10\x02\x1a\x04\x98\xb5\x18\x01\x12\x1d\n\x13MessageType_Failure\x10\x03\x1a\x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_ChangePin\x10\x04\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_WipeDevice\x10\x05\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_FirmwareErase\x10\x06\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_FirmwareUpload\x10\x07\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_GetEntropy\x10\t\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Entropy\x10\n\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_GetPublicKey\x10\x0b\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_PublicKey\x10\x0c\x1a\x04\x98\xb5\x18\x01\x12 \n\x16MessageType_LoadDevice\x10\r\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_ResetDevice\x10\x0e\x1a\x04\x90\xb5\x18\x01\x12\x1c\n\x12MessageType_SignTx\x10\x0f\x1a\x04\x90\xb5\x18\x01\x12\x1e\n\x14MessageType_Features\x10\x11\x1a\x04\x98\xb5\x18\x01\x12&\n\x1cMessageType_PinMatrixRequest\x10\x12\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_PinMatrixAck\x10\x13\x1a\x04\x90\xb5\x18\x01\x12\x1c\n\x12MessageType_Cancel\x10\x14\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_TxRequest\x10\x15\x1a\x04\x98\xb5\x18\x01\x12\x1b\n\x11MessageType_TxAck\x10\x16\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_CipherKeyValue\x10\x17\x1a\x04\x90\xb5\x18\x01\x12\"\n\x18MessageType_ClearSession\x10\x18\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ApplySettings\x10\x19\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ButtonRequest\x10\x1a\x1a\x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_ButtonAck\x10\x1b\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_GetAddress\x10\x1d\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Address\x10\x1e\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EntropyRequest\x10#\x1a\x04\x98\xb5\x18\x01\x12 \n\x16MessageType_EntropyAck\x10$\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_SignMessage\x10&\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_VerifyMessage\x10\'\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_MessageSignature\x10(\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1dMessageType_PassphraseRequest\x10)\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_PassphraseAck\x10*\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_RecoveryDevice\x10-\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_WordRequest\x10.\x1a\x04\x98\xb5\x18\x01\x12\x1d\n\x13MessageType_WordAck\x10/\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_CipheredKeyValue\x10\x30\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EncryptMessage\x10\x31\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_EncryptedMessage\x10\x32\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_DecryptMessage\x10\x33\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_DecryptedMessage\x10\x34\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_SignIdentity\x10\x35\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_SignedIdentity\x10\x36\x1a\x04\x98\xb5\x18\x01\x12!\n\x17MessageType_GetFeatures\x10\x37\x1a\x04\x90\xb5\x18\x01\x12(\n\x1eMessageType_EthereumGetAddress\x10\x38\x1a\x04\x90\xb5\x18\x01\x12%\n\x1bMessageType_EthereumAddress\x10\x39\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EthereumSignTx\x10:\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1dMessageType_EthereumTxRequest\x10;\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_EthereumTxAck\x10<\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_CharacterRequest\x10P\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_CharacterAck\x10Q\x1a\x04\x90\xb5\x18\x01\x12\x1e\n\x14MessageType_RawTxAck\x10R\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ApplyPolicies\x10S\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_FlashHash\x10T\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_FlashWrite\x10U\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1dMessageType_FlashHashResponse\x10V\x1a\x04\x98\xb5\x18\x01\x12(\n\x1eMessageType_DebugLinkFlashDump\x10W\x1a\x04\xa0\xb5\x18\x01\x12\x30\n&MessageType_DebugLinkFlashDumpResponse\x10X\x1a\x04\xa8\xb5\x18\x01\x12\x1f\n\x15MessageType_SoftReset\x10Y\x1a\x04\xa0\xb5\x18\x01\x12\'\n\x1dMessageType_DebugLinkDecision\x10\x64\x1a\x04\xa0\xb5\x18\x01\x12\'\n\x1dMessageType_DebugLinkGetState\x10\x65\x1a\x04\xa0\xb5\x18\x01\x12$\n\x1aMessageType_DebugLinkState\x10\x66\x1a\x04\xa8\xb5\x18\x01\x12#\n\x19MessageType_DebugLinkStop\x10g\x1a\x04\xa0\xb5\x18\x01\x12\"\n\x18MessageType_DebugLinkLog\x10h\x1a\x04\xa8\xb5\x18\x01\x12)\n\x1fMessageType_DebugLinkFillConfig\x10i\x1a\x04\xa8\xb5\x18\x01\x12\"\n\x18MessageType_GetCoinTable\x10j\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_CoinTable\x10k\x1a\x04\x98\xb5\x18\x01\x12)\n\x1fMessageType_EthereumSignMessage\x10l\x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumVerifyMessage\x10m\x1a\x04\x90\xb5\x18\x01\x12.\n$MessageType_EthereumMessageSignature\x10n\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_ChangeWipeCode\x10o\x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumSignTypedHash\x10p\x1a\x04\x90\xb5\x18\x01\x12\x30\n&MessageType_EthereumTypedDataSignature\x10q\x1a\x04\x98\xb5\x18\x01\x12,\n\"MessageType_Ethereum712TypesValues\x10r\x1a\x04\x90\xb5\x18\x01\x12(\n\x1eMessageType_EthereumTxMetadata\x10s\x1a\x04\x90\xb5\x18\x01\x12)\n\x1fMessageType_EthereumMetadataAck\x10t\x1a\x04\x98\xb5\x18\x01\x12)\n\x1fMessageType_LoadClearsignSigner\x10u\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_EthereumSignTypedData\x10\xa8\r\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_EthereumTypedDataStructRequest\x10\xa9\r\x1a\x04\x98\xb5\x18\x01\x12\x31\n&MessageType_EthereumTypedDataStructAck\x10\xaa\r\x1a\x04\x90\xb5\x18\x01\x12\x34\n)MessageType_EthereumTypedDataValueRequest\x10\xab\r\x1a\x04\x98\xb5\x18\x01\x12\x30\n%MessageType_EthereumTypedDataValueAck\x10\xac\r\x1a\x04\x90\xb5\x18\x01\x12\x32\n\'MessageType_EthereumClearSignDefinition\x10\xad\r\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_EthereumClearSignDefinitionAck\x10\xae\r\x1a\x04\x98\xb5\x18\x01\x12\x39\n.MessageType_EthereumClearSignDefinitionRequest\x10\xaf\r\x1a\x04\x98\xb5\x18\x01\x12\x37\n,MessageType_EthereumClearSignDefinitionChunk\x10\xb0\r\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_GetBip85Mnemonic\x10x\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_Bip85Mnemonic\x10y\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_RippleGetAddress\x10\x90\x03\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_RippleAddress\x10\x91\x03\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_RippleSignTx\x10\x92\x03\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_RippleSignedTx\x10\x93\x03\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ThorchainGetAddress\x10\xf4\x03\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_ThorchainAddress\x10\xf5\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ThorchainSignTx\x10\xf6\x03\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ThorchainMsgRequest\x10\xf7\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ThorchainMsgAck\x10\xf8\x03\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_ThorchainSignedTx\x10\xf9\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_EosGetPublicKey\x10\xd8\x04\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_EosPublicKey\x10\xd9\x04\x1a\x04\x98\xb5\x18\x01\x12 \n\x15MessageType_EosSignTx\x10\xda\x04\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_EosTxActionRequest\x10\xdb\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_EosTxActionAck\x10\xdc\x04\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_EosSignedTx\x10\xdd\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_NanoGetAddress\x10\xbc\x05\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_NanoAddress\x10\xbd\x05\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_NanoSignTx\x10\xbe\x05\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_NanoSignedTx\x10\xbf\x05\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_SolanaGetAddress\x10\xee\x05\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_SolanaAddress\x10\xef\x05\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_SolanaSignTx\x10\xf0\x05\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_SolanaSignedTx\x10\xf1\x05\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_SolanaSignMessage\x10\xf2\x05\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_SolanaMessageSignature\x10\xf3\x05\x1a\x04\x98\xb5\x18\x01\x12\x30\n%MessageType_SolanaSignOffchainMessage\x10\xf4\x05\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_SolanaOffchainMessageSignature\x10\xf5\x05\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_BinanceGetAddress\x10\xa0\x06\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_BinanceAddress\x10\xa1\x06\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_BinanceGetPublicKey\x10\xa2\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinancePublicKey\x10\xa3\x06\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_BinanceSignTx\x10\xa4\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinanceTxRequest\x10\xa5\x06\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_BinanceTransferMsg\x10\xa6\x06\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_BinanceOrderMsg\x10\xa7\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinanceCancelMsg\x10\xa8\x06\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_BinanceSignedTx\x10\xa9\x06\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosGetAddress\x10\x84\x07\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_CosmosAddress\x10\x85\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_CosmosSignTx\x10\x86\x07\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosMsgRequest\x10\x87\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_CosmosMsgAck\x10\x88\x07\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_CosmosSignedTx\x10\x89\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_CosmosMsgDelegate\x10\x8a\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CosmosMsgUndelegate\x10\x8b\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CosmosMsgRedelegate\x10\x8c\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosMsgRewards\x10\x8d\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_CosmosMsgIBCTransfer\x10\x8e\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_TendermintGetAddress\x10\xe8\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TendermintAddress\x10\xe9\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_TendermintSignTx\x10\xea\x07\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_TendermintMsgRequest\x10\xeb\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_TendermintMsgAck\x10\xec\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TendermintMsgSend\x10\xed\x07\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_TendermintSignedTx\x10\xee\x07\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_TendermintMsgDelegate\x10\xef\x07\x1a\x04\x98\xb5\x18\x01\x12.\n#MessageType_TendermintMsgUndelegate\x10\xf0\x07\x1a\x04\x98\xb5\x18\x01\x12.\n#MessageType_TendermintMsgRedelegate\x10\xf1\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_TendermintMsgRewards\x10\xf2\x07\x1a\x04\x98\xb5\x18\x01\x12/\n$MessageType_TendermintMsgIBCTransfer\x10\xf3\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisGetAddress\x10\xcc\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisAddress\x10\xcd\x08\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_OsmosisSignTx\x10\xce\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgRequest\x10\xcf\x08\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_OsmosisMsgAck\x10\xd0\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisMsgSend\x10\xd1\x08\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_OsmosisMsgDelegate\x10\xd2\x08\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_OsmosisMsgUndelegate\x10\xd3\x08\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_OsmosisMsgRedelegate\x10\xd4\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgRewards\x10\xd5\x08\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_OsmosisMsgLPAdd\x10\xd6\x08\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_OsmosisMsgLPRemove\x10\xd7\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgLPStake\x10\xd8\x08\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_OsmosisMsgLPUnstake\x10\xd9\x08\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_OsmosisMsgIBCTransfer\x10\xda\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisMsgSwap\x10\xdb\x08\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_OsmosisSignedTx\x10\xdc\x08\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_MayachainGetAddress\x10\xb0\t\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_MayachainAddress\x10\xb1\t\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_MayachainSignTx\x10\xb2\t\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_MayachainMsgRequest\x10\xb3\t\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_MayachainMsgAck\x10\xb4\t\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_MayachainSignedTx\x10\xb5\t\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_ZcashSignPCZT\x10\x94\n\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_ZcashPCZTAction\x10\x95\n\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_ZcashPCZTActionAck\x10\x96\n\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ZcashSignedPCZT\x10\x97\n\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_ZcashGetOrchardFVK\x10\x98\n\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_ZcashOrchardFVK\x10\x99\n\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_ZcashTransparentInput\x10\x9a\n\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_ZcashTransparentSigned\x10\x9b\n\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_ZcashDisplayAddress\x10\x9c\n\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_ZcashAddress\x10\x9d\n\x1a\x04\x98\xb5\x18\x01\x12-\n\"MessageType_ZcashTransparentOutput\x10\x9e\n\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ZcashTransparentAck\x10\x9f\n\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_TronGetAddress\x10\xf8\n\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TronAddress\x10\xf9\n\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_TronSignTx\x10\xfa\n\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_TronSignedTx\x10\xfb\n\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_TronSignMessage\x10\xfc\n\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_TronMessageSignature\x10\xfd\n\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_TronVerifyMessage\x10\xfe\n\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TronSignTypedHash\x10\xff\n\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_TronTypedDataSignature\x10\x80\x0b\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_TonGetAddress\x10\xdc\x0b\x1a\x04\x90\xb5\x18\x01\x12!\n\x16MessageType_TonAddress\x10\xdd\x0b\x1a\x04\x98\xb5\x18\x01\x12 \n\x15MessageType_TonSignTx\x10\xde\x0b\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TonSignedTx\x10\xdf\x0b\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_TonSignMessage\x10\xe0\x0b\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_TonMessageSignature\x10\xe1\x0b\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_HiveGetPublicKey\x10\xc0\x0c\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_HivePublicKey\x10\xc1\x0c\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_HiveSignTx\x10\xc2\x0c\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_HiveSignedTx\x10\xc3\x0c\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_HiveGetPublicKeys\x10\xc4\x0c\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_HivePublicKeys\x10\xc5\x0c\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_HiveSignAccountCreate\x10\xc6\x0c\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_HiveSignedAccountCreate\x10\xc7\x0c\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_HiveSignAccountUpdate\x10\xc8\x0c\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_HiveSignedAccountUpdate\x10\xc9\x0c\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_NearGetAddress\x10\xca\x0c\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_NearAddress\x10\xcb\x0c\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_NearSignTx\x10\xcc\x0c\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_NearSignedTx\x10\xcd\x0c\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_HiveSignMessage\x10\xce\x0c\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_HiveSignedMessage\x10\xcf\x0c\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_HiveSignOperations\x10\xd0\x0c\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_HiveSignedOperations\x10\xd1\x0c\x1a\x04\x98\xb5\x18\x01\x12\x34\n)MessageType_ClearsignAttestorGetPublicKey\x10\xa4\r\x1a\x04\x90\xb5\x18\x01\x12\x31\n&MessageType_ClearsignAttestorPublicKey\x10\xa5\r\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_ClearsignAttestorSign\x10\xa6\r\x1a\x04\x90\xb5\x18\x01\x12\x31\n&MessageType_ClearsignAttestorSignature\x10\xa7\r\x1a\x04\x98\xb5\x18\x01\x42,\n\x1a\x63om.keepkey.deviceprotocolB\x0eKeepKeyMessage') + serialized_pb=_b('\n\x0emessages.proto\x1a\x0btypes.proto\"\x0c\n\nInitialize\"\r\n\x0bGetFeatures\"\xb9\r\n\x08\x46\x65\x61tures\x12\x0e\n\x06vendor\x18\x01 \x01(\t\x12\x15\n\rmajor_version\x18\x02 \x01(\r\x12\x15\n\rminor_version\x18\x03 \x01(\r\x12\x15\n\rpatch_version\x18\x04 \x01(\r\x12\x17\n\x0f\x62ootloader_mode\x18\x05 \x01(\x08\x12\x11\n\tdevice_id\x18\x06 \x01(\t\x12\x16\n\x0epin_protection\x18\x07 \x01(\x08\x12\x1d\n\x15passphrase_protection\x18\x08 \x01(\x08\x12\x10\n\x08language\x18\t \x01(\t\x12\r\n\x05label\x18\n \x01(\t\x12\x18\n\x05\x63oins\x18\x0b \x03(\x0b\x32\t.CoinType\x12\x13\n\x0binitialized\x18\x0c \x01(\x08\x12\x10\n\x08revision\x18\r \x01(\x0c\x12\x17\n\x0f\x62ootloader_hash\x18\x0e \x01(\x0c\x12\x10\n\x08imported\x18\x0f \x01(\x08\x12\x12\n\npin_cached\x18\x10 \x01(\x08\x12\x19\n\x11passphrase_cached\x18\x11 \x01(\x08\x12\x1d\n\x08policies\x18\x12 \x03(\x0b\x32\x0b.PolicyType\x12\r\n\x05model\x18\x15 \x01(\t\x12\x18\n\x10\x66irmware_variant\x18\x16 \x01(\t\x12\x15\n\rfirmware_hash\x18\x17 \x01(\x0c\x12\x11\n\tno_backup\x18\x18 \x01(\x08\x12\x1c\n\x14wipe_code_protection\x18\x19 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x1a \x01(\r\x12\x18\n\x10supports_taproot\x18\x1b \x01(\x08\x12\x1b\n\x13supports_dice_modes\x18\x1c \x01(\x08\x12\'\n\x1fsupports_solana_lut_attestation\x18\x1d \x01(\x08\x12.\n\x0c\x63\x61pabilities\x18\x1e \x03(\x0e\x32\x14.Features.CapabilityB\x02\x10\x01\"\xfc\x07\n\nCapability\x12\x1a\n\x16\x43\x41PABILITY_UNSPECIFIED\x10\x00\x12#\n\x1f\x43\x41PABILITY_ENTROPY_AUDIT_BUDGET\x10\x01\x12-\n)CAPABILITY_ERC20_UNLIMITED_APPROVE_REVIEW\x10\x02\x12,\n(CAPABILITY_ERC20_UNLIMITED_PERMIT_REVIEW\x10\x03\x12%\n!CAPABILITY_ERC7730_RUNTIME_REVIEW\x10\x04\x12#\n\x1f\x43\x41PABILITY_EVM_CERTIFIED_INTENT\x10\x05\x12$\n CAPABILITY_EVM_MAX_AMOUNT_REVIEW\x10\x06\x12\x1e\n\x1a\x43\x41PABILITY_EVM_TX_METADATA\x10\x07\x12\'\n#CAPABILITY_EVM_UNKNOWN_TOKEN_REVIEW\x10\x08\x12\"\n\x1e\x43\x41PABILITY_HIVE_RELEASE_REVIEW\x10\t\x12(\n$CAPABILITY_LEGACY_EVM_ROUTER_SIGNING\x10\n\x12\"\n\x1e\x43\x41PABILITY_MAYA_SINGLE_MESSAGE\x10\x0b\x12\"\n\x1e\x43\x41PABILITY_OSMOSIS_WIRE_GUARDS\x10\x0c\x12\x1d\n\x19\x43\x41PABILITY_PERMIT2_REVIEW\x10\r\x12%\n!CAPABILITY_PROMPT_WORKFLOW_UNWIND\x10\x0e\x12&\n\"CAPABILITY_PROTECTED_PING_PRESENCE\x10\x0f\x12!\n\x1d\x43\x41PABILITY_RIPPLE_MEMO_POLICY\x10\x10\x12%\n!CAPABILITY_SESSION_TRUST_LIFETIME\x10\x11\x12&\n\"CAPABILITY_SOLANA_CERTIFIED_REVIEW\x10\x12\x12%\n!CAPABILITY_SOLANA_LUT_ATTESTATION\x10\x13\x12$\n CAPABILITY_SOLANA_RUNTIME_REVIEW\x10\x14\x12\x1e\n\x1a\x43\x41PABILITY_STORAGE_V19_KDF\x10\x15\x12\"\n\x1e\x43\x41PABILITY_TENDERMINT_PROGRESS\x10\x16\x12\"\n\x1e\x43\x41PABILITY_SAFE_RESET_CEREMONY\x10\x17\x12\"\n\x1e\x43\x41PABILITY_THOR_DEPOSIT_REVIEW\x10\x18\x12 \n\x1c\x43\x41PABILITY_TRON_TRC20_REVIEW\x10\x19\x12$\n CAPABILITY_EIP712_CHUNKED_VALUES\x10\x1a\"*\n\x0cGetCoinTable\x12\r\n\x05start\x18\x01 \x01(\r\x12\x0b\n\x03\x65nd\x18\x02 \x01(\r\"L\n\tCoinTable\x12\x18\n\x05table\x18\x01 \x03(\x0b\x32\t.CoinType\x12\x11\n\tnum_coins\x18\x02 \x01(\r\x12\x12\n\nchunk_size\x18\x03 \x01(\r\"\x0e\n\x0c\x43learSession\"y\n\rApplySettings\x12\x10\n\x08language\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x16\n\x0euse_passphrase\x18\x03 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x04 \x01(\r\x12\x13\n\x0bu2f_counter\x18\x05 \x01(\r\"\x1b\n\tChangePin\x12\x0e\n\x06remove\x18\x01 \x01(\x08\"\x87\x01\n\x04Ping\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x19\n\x11\x62utton_protection\x18\x02 \x01(\x08\x12\x16\n\x0epin_protection\x18\x03 \x01(\x08\x12\x1d\n\x15passphrase_protection\x18\x04 \x01(\x08\x12\x1c\n\x14wipe_code_protection\x18\x05 \x01(\x08\"\x1a\n\x07Success\x12\x0f\n\x07message\x18\x01 \x01(\t\"6\n\x07\x46\x61ilure\x12\x1a\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x0c.FailureType\x12\x0f\n\x07message\x18\x02 \x01(\t\"?\n\rButtonRequest\x12 \n\x04\x63ode\x18\x01 \x01(\x0e\x32\x12.ButtonRequestType\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x0b\n\tButtonAck\"7\n\x10PinMatrixRequest\x12#\n\x04type\x18\x01 \x01(\x0e\x32\x15.PinMatrixRequestType\"\x1b\n\x0cPinMatrixAck\x12\x0b\n\x03pin\x18\x01 \x02(\t\"\x08\n\x06\x43\x61ncel\"\x13\n\x11PassphraseRequest\"#\n\rPassphraseAck\x12\x12\n\npassphrase\x18\x01 \x02(\t\"\x1a\n\nGetEntropy\x12\x0c\n\x04size\x18\x01 \x02(\r\"\x1a\n\x07\x45ntropy\x12\x0f\n\x07\x65ntropy\x18\x01 \x02(\x0c\"\xa2\x01\n\x0cGetPublicKey\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x18\n\x10\x65\x63\x64sa_curve_name\x18\x02 \x01(\t\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\x12\x1a\n\tcoin_name\x18\x04 \x01(\t:\x07\x42itcoin\x12\x33\n\x0bscript_type\x18\x05 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"4\n\tPublicKey\x12\x19\n\x04node\x18\x01 \x02(\x0b\x32\x0b.HDNodeType\x12\x0c\n\x04xpub\x18\x02 \x01(\t\"\xb3\x01\n\nGetAddress\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x1a\n\tcoin_name\x18\x02 \x01(\t:\x07\x42itcoin\x12\x14\n\x0cshow_display\x18\x03 \x01(\x08\x12+\n\x08multisig\x18\x04 \x01(\x0b\x32\x19.MultisigRedeemScriptType\x12\x33\n\x0bscript_type\x18\x05 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"\x1a\n\x07\x41\x64\x64ress\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x02(\t\"\x0c\n\nWipeDevice\"\xbb\x01\n\nLoadDevice\x12\x10\n\x08mnemonic\x18\x01 \x01(\t\x12\x19\n\x04node\x18\x02 \x01(\x0b\x32\x0b.HDNodeType\x12\x0b\n\x03pin\x18\x03 \x01(\t\x12\x1d\n\x15passphrase_protection\x18\x04 \x01(\x08\x12\x19\n\x08language\x18\x05 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x06 \x01(\t\x12\x15\n\rskip_checksum\x18\x07 \x01(\x08\x12\x13\n\x0bu2f_counter\x18\x08 \x01(\r\"\x8a\x02\n\x0bResetDevice\x12\x16\n\x0e\x64isplay_random\x18\x01 \x01(\x08\x12\x15\n\x08strength\x18\x02 \x01(\r:\x03\x32\x35\x36\x12\x1d\n\x15passphrase_protection\x18\x03 \x01(\x08\x12\x16\n\x0epin_protection\x18\x04 \x01(\x08\x12\x19\n\x08language\x18\x05 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x06 \x01(\t\x12\x11\n\tno_backup\x18\x07 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x08 \x01(\r\x12\x13\n\x0bu2f_counter\x18\t \x01(\r\x12\x14\n\x0c\x64ice_entropy\x18\n \x01(\x08\x12\x11\n\tdice_only\x18\x0b \x01(\x08\"\x10\n\x0e\x45ntropyRequest\"\x1d\n\nEntropyAck\x12\x0f\n\x07\x65ntropy\x18\x01 \x01(\x0c\"\xff\x01\n\x0eRecoveryDevice\x12\x12\n\nword_count\x18\x01 \x01(\r\x12\x1d\n\x15passphrase_protection\x18\x02 \x01(\x08\x12\x16\n\x0epin_protection\x18\x03 \x01(\x08\x12\x19\n\x08language\x18\x04 \x01(\t:\x07\x65nglish\x12\r\n\x05label\x18\x05 \x01(\t\x12\x18\n\x10\x65nforce_wordlist\x18\x06 \x01(\x08\x12\x1c\n\x14use_character_cipher\x18\x07 \x01(\x08\x12\x1a\n\x12\x61uto_lock_delay_ms\x18\x08 \x01(\r\x12\x13\n\x0bu2f_counter\x18\t \x01(\r\x12\x0f\n\x07\x64ry_run\x18\n \x01(\x08\"\r\n\x0bWordRequest\"\x17\n\x07WordAck\x12\x0c\n\x04word\x18\x01 \x02(\t\";\n\x10\x43haracterRequest\x12\x10\n\x08word_pos\x18\x01 \x02(\r\x12\x15\n\rcharacter_pos\x18\x02 \x02(\r\"?\n\x0c\x43haracterAck\x12\x11\n\tcharacter\x18\x01 \x01(\t\x12\x0e\n\x06\x64\x65lete\x18\x02 \x01(\x08\x12\x0c\n\x04\x64one\x18\x03 \x01(\x08\"\x82\x01\n\x0bSignMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0f\n\x07message\x18\x02 \x02(\x0c\x12\x1a\n\tcoin_name\x18\x03 \x01(\t:\x07\x42itcoin\x12\x33\n\x0bscript_type\x18\x04 \x01(\x0e\x32\x10.InputScriptType:\x0cSPENDADDRESS\"`\n\rVerifyMessage\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\x12\x1a\n\tcoin_name\x18\x04 \x01(\t:\x07\x42itcoin\"6\n\x10MessageSignature\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"v\n\x0e\x45ncryptMessage\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\x12\x14\n\x0c\x64isplay_only\x18\x03 \x01(\x08\x12\x11\n\taddress_n\x18\x04 \x03(\r\x12\x1a\n\tcoin_name\x18\x05 \x01(\t:\x07\x42itcoin\"@\n\x10\x45ncryptedMessage\x12\r\n\x05nonce\x18\x01 \x01(\x0c\x12\x0f\n\x07message\x18\x02 \x01(\x0c\x12\x0c\n\x04hmac\x18\x03 \x01(\x0c\"Q\n\x0e\x44\x65\x63ryptMessage\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\r\n\x05nonce\x18\x02 \x01(\x0c\x12\x0f\n\x07message\x18\x03 \x01(\x0c\x12\x0c\n\x04hmac\x18\x04 \x01(\x0c\"4\n\x10\x44\x65\x63ryptedMessage\x12\x0f\n\x07message\x18\x01 \x01(\x0c\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"\x8c\x01\n\x0e\x43ipherKeyValue\x12\x11\n\taddress_n\x18\x01 \x03(\r\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\x0c\x12\x0f\n\x07\x65ncrypt\x18\x04 \x01(\x08\x12\x16\n\x0e\x61sk_on_encrypt\x18\x05 \x01(\x08\x12\x16\n\x0e\x61sk_on_decrypt\x18\x06 \x01(\x08\x12\n\n\x02iv\x18\x07 \x01(\x0c\"!\n\x10\x43ipheredKeyValue\x12\r\n\x05value\x18\x01 \x01(\x0c\"5\n\x10GetBip85Mnemonic\x12\x12\n\nword_count\x18\x01 \x02(\r\x12\r\n\x05index\x18\x02 \x02(\r\"!\n\rBip85Mnemonic\x12\x10\n\x08mnemonic\x18\x01 \x02(\t\"\xce\x01\n\x06SignTx\x12\x15\n\routputs_count\x18\x01 \x02(\r\x12\x14\n\x0cinputs_count\x18\x02 \x02(\r\x12\x1a\n\tcoin_name\x18\x03 \x01(\t:\x07\x42itcoin\x12\x12\n\x07version\x18\x04 \x01(\r:\x01\x31\x12\x14\n\tlock_time\x18\x05 \x01(\r:\x01\x30\x12\x0e\n\x06\x65xpiry\x18\x06 \x01(\r\x12\x14\n\x0coverwintered\x18\x07 \x01(\x08\x12\x18\n\x10version_group_id\x18\x08 \x01(\r\x12\x11\n\tbranch_id\x18\n \x01(\r\"\x85\x01\n\tTxRequest\x12\"\n\x0crequest_type\x18\x01 \x01(\x0e\x32\x0c.RequestType\x12&\n\x07\x64\x65tails\x18\x02 \x01(\x0b\x32\x15.TxRequestDetailsType\x12,\n\nserialized\x18\x03 \x01(\x0b\x32\x18.TxRequestSerializedType\"%\n\x05TxAck\x12\x1c\n\x02tx\x18\x01 \x01(\x0b\x32\x10.TransactionType\"+\n\x08RawTxAck\x12\x1f\n\x02tx\x18\x01 \x01(\x0b\x32\x13.RawTransactionType\"}\n\x0cSignIdentity\x12\x1f\n\x08identity\x18\x01 \x01(\x0b\x32\r.IdentityType\x12\x18\n\x10\x63hallenge_hidden\x18\x02 \x01(\x0c\x12\x18\n\x10\x63hallenge_visual\x18\x03 \x01(\t\x12\x18\n\x10\x65\x63\x64sa_curve_name\x18\x04 \x01(\t\"H\n\x0eSignedIdentity\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x12\n\npublic_key\x18\x02 \x01(\x0c\x12\x11\n\tsignature\x18\x03 \x01(\x0c\",\n\rApplyPolicies\x12\x1b\n\x06policy\x18\x01 \x03(\x0b\x32\x0b.PolicyType\"?\n\tFlashHash\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0e\n\x06length\x18\x02 \x01(\r\x12\x11\n\tchallenge\x18\x03 \x01(\x0c\":\n\nFlashWrite\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\r\n\x05\x65rase\x18\x03 \x01(\x08\"!\n\x11\x46lashHashResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"5\n\x12\x44\x65\x62ugLinkFlashDump\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\r\x12\x0e\n\x06length\x18\x02 \x01(\r\"*\n\x1a\x44\x65\x62ugLinkFlashDumpResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x0b\n\tSoftReset\"\x0f\n\rFirmwareErase\"7\n\x0e\x46irmwareUpload\x12\x14\n\x0cpayload_hash\x18\x01 \x02(\x0c\x12\x0f\n\x07payload\x18\x02 \x02(\x0c\"2\n\x11\x44\x65\x62ugLinkDecision\x12\x0e\n\x06yes_no\x18\x01 \x02(\x08\x12\r\n\x05input\x18\x02 \x01(\t\"\x13\n\x11\x44\x65\x62ugLinkGetState\"\x99\x03\n\x0e\x44\x65\x62ugLinkState\x12\x0e\n\x06layout\x18\x01 \x01(\x0c\x12\x0b\n\x03pin\x18\x02 \x01(\t\x12\x0e\n\x06matrix\x18\x03 \x01(\t\x12\x10\n\x08mnemonic\x18\x04 \x01(\t\x12\x19\n\x04node\x18\x05 \x01(\x0b\x32\x0b.HDNodeType\x12\x1d\n\x15passphrase_protection\x18\x06 \x01(\x08\x12\x12\n\nreset_word\x18\x07 \x01(\t\x12\x15\n\rreset_entropy\x18\x08 \x01(\x0c\x12\x1a\n\x12recovery_fake_word\x18\t \x01(\t\x12\x19\n\x11recovery_word_pos\x18\n \x01(\r\x12\x17\n\x0frecovery_cipher\x18\x0b \x01(\t\x12$\n\x1crecovery_auto_completed_word\x18\x0c \x01(\t\x12\x15\n\rfirmware_hash\x18\r \x01(\x0c\x12\x14\n\x0cstorage_hash\x18\x0e \x01(\x0c\x12\x13\n\x0b\x64ice_digest\x18\x0f \x01(\x0c\x12\x15\n\rconfirm_title\x18\x10 \x01(\t\x12\x14\n\x0c\x63onfirm_body\x18\x11 \x01(\t\"\x0f\n\rDebugLinkStop\";\n\x0c\x44\x65\x62ugLinkLog\x12\r\n\x05level\x18\x01 \x01(\r\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12\x0c\n\x04text\x18\x03 \x01(\t\"\x15\n\x13\x44\x65\x62ugLinkFillConfig\" \n\x0e\x43hangeWipeCode\x12\x0e\n\x06remove\x18\x01 \x01(\x08\"\x1f\n\x1d\x43learsignAttestorGetPublicKey\"0\n\x1a\x43learsignAttestorPublicKey\x12\x12\n\npublic_key\x18\x01 \x01(\x0c\"(\n\x15\x43learsignAttestorSign\x12\x0f\n\x07payload\x18\x01 \x01(\x0c\"C\n\x1a\x43learsignAttestorSignature\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\x12\n\npublic_key\x18\x02 \x01(\x0c*\xc3\x46\n\x0bMessageType\x12 \n\x16MessageType_Initialize\x10\x00\x1a\x04\x90\xb5\x18\x01\x12\x1a\n\x10MessageType_Ping\x10\x01\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Success\x10\x02\x1a\x04\x98\xb5\x18\x01\x12\x1d\n\x13MessageType_Failure\x10\x03\x1a\x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_ChangePin\x10\x04\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_WipeDevice\x10\x05\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_FirmwareErase\x10\x06\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_FirmwareUpload\x10\x07\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_GetEntropy\x10\t\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Entropy\x10\n\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_GetPublicKey\x10\x0b\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_PublicKey\x10\x0c\x1a\x04\x98\xb5\x18\x01\x12 \n\x16MessageType_LoadDevice\x10\r\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_ResetDevice\x10\x0e\x1a\x04\x90\xb5\x18\x01\x12\x1c\n\x12MessageType_SignTx\x10\x0f\x1a\x04\x90\xb5\x18\x01\x12\x1e\n\x14MessageType_Features\x10\x11\x1a\x04\x98\xb5\x18\x01\x12&\n\x1cMessageType_PinMatrixRequest\x10\x12\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_PinMatrixAck\x10\x13\x1a\x04\x90\xb5\x18\x01\x12\x1c\n\x12MessageType_Cancel\x10\x14\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_TxRequest\x10\x15\x1a\x04\x98\xb5\x18\x01\x12\x1b\n\x11MessageType_TxAck\x10\x16\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_CipherKeyValue\x10\x17\x1a\x04\x90\xb5\x18\x01\x12\"\n\x18MessageType_ClearSession\x10\x18\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ApplySettings\x10\x19\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ButtonRequest\x10\x1a\x1a\x04\x98\xb5\x18\x01\x12\x1f\n\x15MessageType_ButtonAck\x10\x1b\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_GetAddress\x10\x1d\x1a\x04\x90\xb5\x18\x01\x12\x1d\n\x13MessageType_Address\x10\x1e\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EntropyRequest\x10#\x1a\x04\x98\xb5\x18\x01\x12 \n\x16MessageType_EntropyAck\x10$\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_SignMessage\x10&\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_VerifyMessage\x10\'\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_MessageSignature\x10(\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1dMessageType_PassphraseRequest\x10)\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_PassphraseAck\x10*\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_RecoveryDevice\x10-\x1a\x04\x90\xb5\x18\x01\x12!\n\x17MessageType_WordRequest\x10.\x1a\x04\x98\xb5\x18\x01\x12\x1d\n\x13MessageType_WordAck\x10/\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_CipheredKeyValue\x10\x30\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EncryptMessage\x10\x31\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_EncryptedMessage\x10\x32\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_DecryptMessage\x10\x33\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_DecryptedMessage\x10\x34\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_SignIdentity\x10\x35\x1a\x04\x90\xb5\x18\x01\x12$\n\x1aMessageType_SignedIdentity\x10\x36\x1a\x04\x98\xb5\x18\x01\x12!\n\x17MessageType_GetFeatures\x10\x37\x1a\x04\x90\xb5\x18\x01\x12(\n\x1eMessageType_EthereumGetAddress\x10\x38\x1a\x04\x90\xb5\x18\x01\x12%\n\x1bMessageType_EthereumAddress\x10\x39\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_EthereumSignTx\x10:\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1dMessageType_EthereumTxRequest\x10;\x1a\x04\x98\xb5\x18\x01\x12#\n\x19MessageType_EthereumTxAck\x10<\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_CharacterRequest\x10P\x1a\x04\x98\xb5\x18\x01\x12\"\n\x18MessageType_CharacterAck\x10Q\x1a\x04\x90\xb5\x18\x01\x12\x1e\n\x14MessageType_RawTxAck\x10R\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_ApplyPolicies\x10S\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_FlashHash\x10T\x1a\x04\x90\xb5\x18\x01\x12 \n\x16MessageType_FlashWrite\x10U\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1dMessageType_FlashHashResponse\x10V\x1a\x04\x98\xb5\x18\x01\x12(\n\x1eMessageType_DebugLinkFlashDump\x10W\x1a\x04\xa0\xb5\x18\x01\x12\x30\n&MessageType_DebugLinkFlashDumpResponse\x10X\x1a\x04\xa8\xb5\x18\x01\x12\x1f\n\x15MessageType_SoftReset\x10Y\x1a\x04\xa0\xb5\x18\x01\x12\'\n\x1dMessageType_DebugLinkDecision\x10\x64\x1a\x04\xa0\xb5\x18\x01\x12\'\n\x1dMessageType_DebugLinkGetState\x10\x65\x1a\x04\xa0\xb5\x18\x01\x12$\n\x1aMessageType_DebugLinkState\x10\x66\x1a\x04\xa8\xb5\x18\x01\x12#\n\x19MessageType_DebugLinkStop\x10g\x1a\x04\xa0\xb5\x18\x01\x12\"\n\x18MessageType_DebugLinkLog\x10h\x1a\x04\xa8\xb5\x18\x01\x12)\n\x1fMessageType_DebugLinkFillConfig\x10i\x1a\x04\xa8\xb5\x18\x01\x12\"\n\x18MessageType_GetCoinTable\x10j\x1a\x04\x90\xb5\x18\x01\x12\x1f\n\x15MessageType_CoinTable\x10k\x1a\x04\x98\xb5\x18\x01\x12)\n\x1fMessageType_EthereumSignMessage\x10l\x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumVerifyMessage\x10m\x1a\x04\x90\xb5\x18\x01\x12.\n$MessageType_EthereumMessageSignature\x10n\x1a\x04\x98\xb5\x18\x01\x12$\n\x1aMessageType_ChangeWipeCode\x10o\x1a\x04\x90\xb5\x18\x01\x12+\n!MessageType_EthereumSignTypedHash\x10p\x1a\x04\x90\xb5\x18\x01\x12\x30\n&MessageType_EthereumTypedDataSignature\x10q\x1a\x04\x98\xb5\x18\x01\x12,\n\"MessageType_Ethereum712TypesValues\x10r\x1a\x04\x90\xb5\x18\x01\x12(\n\x1eMessageType_EthereumTxMetadata\x10s\x1a\x04\x90\xb5\x18\x01\x12)\n\x1fMessageType_EthereumMetadataAck\x10t\x1a\x04\x98\xb5\x18\x01\x12)\n\x1fMessageType_LoadClearsignSigner\x10u\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_EthereumSignTypedData\x10\xa8\r\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_EthereumTypedDataStructRequest\x10\xa9\r\x1a\x04\x98\xb5\x18\x01\x12\x31\n&MessageType_EthereumTypedDataStructAck\x10\xaa\r\x1a\x04\x90\xb5\x18\x01\x12\x34\n)MessageType_EthereumTypedDataValueRequest\x10\xab\r\x1a\x04\x98\xb5\x18\x01\x12\x30\n%MessageType_EthereumTypedDataValueAck\x10\xac\r\x1a\x04\x90\xb5\x18\x01\x12\x32\n\'MessageType_EthereumClearSignDefinition\x10\xad\r\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_EthereumClearSignDefinitionAck\x10\xae\r\x1a\x04\x98\xb5\x18\x01\x12\x39\n.MessageType_EthereumClearSignDefinitionRequest\x10\xaf\r\x1a\x04\x98\xb5\x18\x01\x12\x37\n,MessageType_EthereumClearSignDefinitionChunk\x10\xb0\r\x1a\x04\x90\xb5\x18\x01\x12&\n\x1cMessageType_GetBip85Mnemonic\x10x\x1a\x04\x90\xb5\x18\x01\x12#\n\x19MessageType_Bip85Mnemonic\x10y\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_RippleGetAddress\x10\x90\x03\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_RippleAddress\x10\x91\x03\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_RippleSignTx\x10\x92\x03\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_RippleSignedTx\x10\x93\x03\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ThorchainGetAddress\x10\xf4\x03\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_ThorchainAddress\x10\xf5\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ThorchainSignTx\x10\xf6\x03\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ThorchainMsgRequest\x10\xf7\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ThorchainMsgAck\x10\xf8\x03\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_ThorchainSignedTx\x10\xf9\x03\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_EosGetPublicKey\x10\xd8\x04\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_EosPublicKey\x10\xd9\x04\x1a\x04\x98\xb5\x18\x01\x12 \n\x15MessageType_EosSignTx\x10\xda\x04\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_EosTxActionRequest\x10\xdb\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_EosTxActionAck\x10\xdc\x04\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_EosSignedTx\x10\xdd\x04\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_NanoGetAddress\x10\xbc\x05\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_NanoAddress\x10\xbd\x05\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_NanoSignTx\x10\xbe\x05\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_NanoSignedTx\x10\xbf\x05\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_SolanaGetAddress\x10\xee\x05\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_SolanaAddress\x10\xef\x05\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_SolanaSignTx\x10\xf0\x05\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_SolanaSignedTx\x10\xf1\x05\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_SolanaSignMessage\x10\xf2\x05\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_SolanaMessageSignature\x10\xf3\x05\x1a\x04\x98\xb5\x18\x01\x12\x30\n%MessageType_SolanaSignOffchainMessage\x10\xf4\x05\x1a\x04\x90\xb5\x18\x01\x12\x35\n*MessageType_SolanaOffchainMessageSignature\x10\xf5\x05\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_BinanceGetAddress\x10\xa0\x06\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_BinanceAddress\x10\xa1\x06\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_BinanceGetPublicKey\x10\xa2\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinancePublicKey\x10\xa3\x06\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_BinanceSignTx\x10\xa4\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinanceTxRequest\x10\xa5\x06\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_BinanceTransferMsg\x10\xa6\x06\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_BinanceOrderMsg\x10\xa7\x06\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_BinanceCancelMsg\x10\xa8\x06\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_BinanceSignedTx\x10\xa9\x06\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosGetAddress\x10\x84\x07\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_CosmosAddress\x10\x85\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_CosmosSignTx\x10\x86\x07\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosMsgRequest\x10\x87\x07\x1a\x04\x98\xb5\x18\x01\x12#\n\x18MessageType_CosmosMsgAck\x10\x88\x07\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_CosmosSignedTx\x10\x89\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_CosmosMsgDelegate\x10\x8a\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CosmosMsgUndelegate\x10\x8b\x07\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_CosmosMsgRedelegate\x10\x8c\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_CosmosMsgRewards\x10\x8d\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_CosmosMsgIBCTransfer\x10\x8e\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_TendermintGetAddress\x10\xe8\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TendermintAddress\x10\xe9\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_TendermintSignTx\x10\xea\x07\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_TendermintMsgRequest\x10\xeb\x07\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_TendermintMsgAck\x10\xec\x07\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TendermintMsgSend\x10\xed\x07\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_TendermintSignedTx\x10\xee\x07\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_TendermintMsgDelegate\x10\xef\x07\x1a\x04\x98\xb5\x18\x01\x12.\n#MessageType_TendermintMsgUndelegate\x10\xf0\x07\x1a\x04\x98\xb5\x18\x01\x12.\n#MessageType_TendermintMsgRedelegate\x10\xf1\x07\x1a\x04\x98\xb5\x18\x01\x12+\n MessageType_TendermintMsgRewards\x10\xf2\x07\x1a\x04\x98\xb5\x18\x01\x12/\n$MessageType_TendermintMsgIBCTransfer\x10\xf3\x07\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisGetAddress\x10\xcc\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisAddress\x10\xcd\x08\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_OsmosisSignTx\x10\xce\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgRequest\x10\xcf\x08\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_OsmosisMsgAck\x10\xd0\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisMsgSend\x10\xd1\x08\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_OsmosisMsgDelegate\x10\xd2\x08\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_OsmosisMsgUndelegate\x10\xd3\x08\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_OsmosisMsgRedelegate\x10\xd4\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgRewards\x10\xd5\x08\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_OsmosisMsgLPAdd\x10\xd6\x08\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_OsmosisMsgLPRemove\x10\xd7\x08\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_OsmosisMsgLPStake\x10\xd8\x08\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_OsmosisMsgLPUnstake\x10\xd9\x08\x1a\x04\x90\xb5\x18\x01\x12,\n!MessageType_OsmosisMsgIBCTransfer\x10\xda\x08\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_OsmosisMsgSwap\x10\xdb\x08\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_OsmosisSignedTx\x10\xdc\x08\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_MayachainGetAddress\x10\xb0\t\x1a\x04\x90\xb5\x18\x01\x12\'\n\x1cMessageType_MayachainAddress\x10\xb1\t\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_MayachainSignTx\x10\xb2\t\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_MayachainMsgRequest\x10\xb3\t\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_MayachainMsgAck\x10\xb4\t\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_MayachainSignedTx\x10\xb5\t\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_ZcashSignPCZT\x10\x94\n\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_ZcashPCZTAction\x10\x95\n\x1a\x04\x90\xb5\x18\x01\x12)\n\x1eMessageType_ZcashPCZTActionAck\x10\x96\n\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_ZcashSignedPCZT\x10\x97\n\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_ZcashGetOrchardFVK\x10\x98\n\x1a\x04\x90\xb5\x18\x01\x12&\n\x1bMessageType_ZcashOrchardFVK\x10\x99\n\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_ZcashTransparentInput\x10\x9a\n\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_ZcashTransparentSigned\x10\x9b\n\x1a\x04\x98\xb5\x18\x01\x12*\n\x1fMessageType_ZcashDisplayAddress\x10\x9c\n\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_ZcashAddress\x10\x9d\n\x1a\x04\x98\xb5\x18\x01\x12-\n\"MessageType_ZcashTransparentOutput\x10\x9e\n\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_ZcashTransparentAck\x10\x9f\n\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_TronGetAddress\x10\xf8\n\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TronAddress\x10\xf9\n\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_TronSignTx\x10\xfa\n\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_TronSignedTx\x10\xfb\n\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_TronSignMessage\x10\xfc\n\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_TronMessageSignature\x10\xfd\n\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_TronVerifyMessage\x10\xfe\n\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_TronSignTypedHash\x10\xff\n\x1a\x04\x90\xb5\x18\x01\x12-\n\"MessageType_TronTypedDataSignature\x10\x80\x0b\x1a\x04\x98\xb5\x18\x01\x12$\n\x19MessageType_TonGetAddress\x10\xdc\x0b\x1a\x04\x90\xb5\x18\x01\x12!\n\x16MessageType_TonAddress\x10\xdd\x0b\x1a\x04\x98\xb5\x18\x01\x12 \n\x15MessageType_TonSignTx\x10\xde\x0b\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_TonSignedTx\x10\xdf\x0b\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_TonSignMessage\x10\xe0\x0b\x1a\x04\x90\xb5\x18\x01\x12*\n\x1fMessageType_TonMessageSignature\x10\xe1\x0b\x1a\x04\x98\xb5\x18\x01\x12\'\n\x1cMessageType_HiveGetPublicKey\x10\xc0\x0c\x1a\x04\x90\xb5\x18\x01\x12$\n\x19MessageType_HivePublicKey\x10\xc1\x0c\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_HiveSignTx\x10\xc2\x0c\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_HiveSignedTx\x10\xc3\x0c\x1a\x04\x98\xb5\x18\x01\x12(\n\x1dMessageType_HiveGetPublicKeys\x10\xc4\x0c\x1a\x04\x90\xb5\x18\x01\x12%\n\x1aMessageType_HivePublicKeys\x10\xc5\x0c\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_HiveSignAccountCreate\x10\xc6\x0c\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_HiveSignedAccountCreate\x10\xc7\x0c\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_HiveSignAccountUpdate\x10\xc8\x0c\x1a\x04\x90\xb5\x18\x01\x12.\n#MessageType_HiveSignedAccountUpdate\x10\xc9\x0c\x1a\x04\x98\xb5\x18\x01\x12%\n\x1aMessageType_NearGetAddress\x10\xca\x0c\x1a\x04\x90\xb5\x18\x01\x12\"\n\x17MessageType_NearAddress\x10\xcb\x0c\x1a\x04\x98\xb5\x18\x01\x12!\n\x16MessageType_NearSignTx\x10\xcc\x0c\x1a\x04\x90\xb5\x18\x01\x12#\n\x18MessageType_NearSignedTx\x10\xcd\x0c\x1a\x04\x98\xb5\x18\x01\x12&\n\x1bMessageType_HiveSignMessage\x10\xce\x0c\x1a\x04\x90\xb5\x18\x01\x12(\n\x1dMessageType_HiveSignedMessage\x10\xcf\x0c\x1a\x04\x98\xb5\x18\x01\x12)\n\x1eMessageType_HiveSignOperations\x10\xd0\x0c\x1a\x04\x90\xb5\x18\x01\x12+\n MessageType_HiveSignedOperations\x10\xd1\x0c\x1a\x04\x98\xb5\x18\x01\x12\x34\n)MessageType_ClearsignAttestorGetPublicKey\x10\xa4\r\x1a\x04\x90\xb5\x18\x01\x12\x31\n&MessageType_ClearsignAttestorPublicKey\x10\xa5\r\x1a\x04\x98\xb5\x18\x01\x12,\n!MessageType_ClearsignAttestorSign\x10\xa6\r\x1a\x04\x90\xb5\x18\x01\x12\x31\n&MessageType_ClearsignAttestorSignature\x10\xa7\r\x1a\x04\x98\xb5\x18\x01\x42,\n\x1a\x63om.keepkey.deviceprotocolB\x0eKeepKeyMessage') , dependencies=[types__pb2.DESCRIPTOR,]) @@ -926,8 +926,8 @@ ], containing_type=None, options=None, - serialized_start=6636, - serialized_end=15663, + serialized_start=6674, + serialized_end=15701, ) _sym_db.RegisterEnumDescriptor(_MESSAGETYPE) @@ -1267,11 +1267,15 @@ name='CAPABILITY_TRON_TRC20_REVIEW', index=25, number=25, options=None, type=None), + _descriptor.EnumValueDescriptor( + name='CAPABILITY_EIP712_CHUNKED_VALUES', index=26, number=26, + options=None, + type=None), ], containing_type=None, options=None, serialized_start=762, - serialized_end=1744, + serialized_end=1782, ) _sym_db.RegisterEnumDescriptor(_FEATURES_CAPABILITY) @@ -1541,7 +1545,7 @@ oneofs=[ ], serialized_start=61, - serialized_end=1744, + serialized_end=1782, ) @@ -1578,8 +1582,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1746, - serialized_end=1788, + serialized_start=1784, + serialized_end=1826, ) @@ -1623,8 +1627,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1790, - serialized_end=1866, + serialized_start=1828, + serialized_end=1904, ) @@ -1647,8 +1651,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1868, - serialized_end=1882, + serialized_start=1906, + serialized_end=1920, ) @@ -1706,8 +1710,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1884, - serialized_end=2005, + serialized_start=1922, + serialized_end=2043, ) @@ -1737,8 +1741,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2007, - serialized_end=2034, + serialized_start=2045, + serialized_end=2072, ) @@ -1796,8 +1800,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2037, - serialized_end=2172, + serialized_start=2075, + serialized_end=2210, ) @@ -1827,8 +1831,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2174, - serialized_end=2200, + serialized_start=2212, + serialized_end=2238, ) @@ -1865,8 +1869,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2202, - serialized_end=2256, + serialized_start=2240, + serialized_end=2294, ) @@ -1903,8 +1907,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2258, - serialized_end=2321, + serialized_start=2296, + serialized_end=2359, ) @@ -1927,8 +1931,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2323, - serialized_end=2334, + serialized_start=2361, + serialized_end=2372, ) @@ -1958,8 +1962,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2336, - serialized_end=2391, + serialized_start=2374, + serialized_end=2429, ) @@ -1989,8 +1993,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2393, - serialized_end=2420, + serialized_start=2431, + serialized_end=2458, ) @@ -2013,8 +2017,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2422, - serialized_end=2430, + serialized_start=2460, + serialized_end=2468, ) @@ -2037,8 +2041,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2432, - serialized_end=2451, + serialized_start=2470, + serialized_end=2489, ) @@ -2068,8 +2072,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2453, - serialized_end=2488, + serialized_start=2491, + serialized_end=2526, ) @@ -2099,8 +2103,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2490, - serialized_end=2516, + serialized_start=2528, + serialized_end=2554, ) @@ -2130,8 +2134,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2518, - serialized_end=2544, + serialized_start=2556, + serialized_end=2582, ) @@ -2189,8 +2193,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2547, - serialized_end=2709, + serialized_start=2585, + serialized_end=2747, ) @@ -2227,8 +2231,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2711, - serialized_end=2763, + serialized_start=2749, + serialized_end=2801, ) @@ -2286,8 +2290,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2766, - serialized_end=2945, + serialized_start=2804, + serialized_end=2983, ) @@ -2317,8 +2321,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2947, - serialized_end=2973, + serialized_start=2985, + serialized_end=3011, ) @@ -2341,8 +2345,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2975, - serialized_end=2987, + serialized_start=3013, + serialized_end=3025, ) @@ -2421,8 +2425,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2990, - serialized_end=3177, + serialized_start=3028, + serialized_end=3215, ) @@ -2522,8 +2526,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3180, - serialized_end=3446, + serialized_start=3218, + serialized_end=3484, ) @@ -2546,8 +2550,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3448, - serialized_end=3464, + serialized_start=3486, + serialized_end=3502, ) @@ -2577,8 +2581,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3466, - serialized_end=3495, + serialized_start=3504, + serialized_end=3533, ) @@ -2671,8 +2675,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3498, - serialized_end=3753, + serialized_start=3536, + serialized_end=3791, ) @@ -2695,8 +2699,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3755, - serialized_end=3768, + serialized_start=3793, + serialized_end=3806, ) @@ -2726,8 +2730,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3770, - serialized_end=3793, + serialized_start=3808, + serialized_end=3831, ) @@ -2764,8 +2768,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3795, - serialized_end=3854, + serialized_start=3833, + serialized_end=3892, ) @@ -2809,8 +2813,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3856, - serialized_end=3919, + serialized_start=3894, + serialized_end=3957, ) @@ -2861,8 +2865,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3922, - serialized_end=4052, + serialized_start=3960, + serialized_end=4090, ) @@ -2913,8 +2917,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4054, - serialized_end=4150, + serialized_start=4092, + serialized_end=4188, ) @@ -2951,8 +2955,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4152, - serialized_end=4206, + serialized_start=4190, + serialized_end=4244, ) @@ -3010,8 +3014,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4208, - serialized_end=4326, + serialized_start=4246, + serialized_end=4364, ) @@ -3055,8 +3059,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4328, - serialized_end=4392, + serialized_start=4366, + serialized_end=4430, ) @@ -3107,8 +3111,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4394, - serialized_end=4475, + serialized_start=4432, + serialized_end=4513, ) @@ -3145,8 +3149,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4477, - serialized_end=4529, + serialized_start=4515, + serialized_end=4567, ) @@ -3218,8 +3222,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4532, - serialized_end=4672, + serialized_start=4570, + serialized_end=4710, ) @@ -3249,8 +3253,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4674, - serialized_end=4707, + serialized_start=4712, + serialized_end=4745, ) @@ -3287,8 +3291,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4709, - serialized_end=4762, + serialized_start=4747, + serialized_end=4800, ) @@ -3318,8 +3322,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4764, - serialized_end=4797, + serialized_start=4802, + serialized_end=4835, ) @@ -3405,8 +3409,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4800, - serialized_end=5006, + serialized_start=4838, + serialized_end=5044, ) @@ -3450,8 +3454,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5009, - serialized_end=5142, + serialized_start=5047, + serialized_end=5180, ) @@ -3481,8 +3485,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5144, - serialized_end=5181, + serialized_start=5182, + serialized_end=5219, ) @@ -3512,8 +3516,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5183, - serialized_end=5226, + serialized_start=5221, + serialized_end=5264, ) @@ -3564,8 +3568,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5228, - serialized_end=5353, + serialized_start=5266, + serialized_end=5391, ) @@ -3609,8 +3613,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5355, - serialized_end=5427, + serialized_start=5393, + serialized_end=5465, ) @@ -3640,8 +3644,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5429, - serialized_end=5473, + serialized_start=5467, + serialized_end=5511, ) @@ -3685,8 +3689,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5475, - serialized_end=5538, + serialized_start=5513, + serialized_end=5576, ) @@ -3730,8 +3734,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5540, - serialized_end=5598, + serialized_start=5578, + serialized_end=5636, ) @@ -3761,8 +3765,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5600, - serialized_end=5633, + serialized_start=5638, + serialized_end=5671, ) @@ -3799,8 +3803,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5635, - serialized_end=5688, + serialized_start=5673, + serialized_end=5726, ) @@ -3830,8 +3834,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5690, - serialized_end=5732, + serialized_start=5728, + serialized_end=5770, ) @@ -3854,8 +3858,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5734, - serialized_end=5745, + serialized_start=5772, + serialized_end=5783, ) @@ -3878,8 +3882,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5747, - serialized_end=5762, + serialized_start=5785, + serialized_end=5800, ) @@ -3916,8 +3920,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5764, - serialized_end=5819, + serialized_start=5802, + serialized_end=5857, ) @@ -3954,8 +3958,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5821, - serialized_end=5871, + serialized_start=5859, + serialized_end=5909, ) @@ -3978,8 +3982,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5873, - serialized_end=5892, + serialized_start=5911, + serialized_end=5930, ) @@ -4121,8 +4125,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5895, - serialized_end=6304, + serialized_start=5933, + serialized_end=6342, ) @@ -4145,8 +4149,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6306, - serialized_end=6321, + serialized_start=6344, + serialized_end=6359, ) @@ -4190,8 +4194,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6323, - serialized_end=6382, + serialized_start=6361, + serialized_end=6420, ) @@ -4214,8 +4218,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6384, - serialized_end=6405, + serialized_start=6422, + serialized_end=6443, ) @@ -4245,8 +4249,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6407, - serialized_end=6439, + serialized_start=6445, + serialized_end=6477, ) @@ -4269,8 +4273,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6441, - serialized_end=6472, + serialized_start=6479, + serialized_end=6510, ) @@ -4300,8 +4304,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6474, - serialized_end=6522, + serialized_start=6512, + serialized_end=6560, ) @@ -4331,8 +4335,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6524, - serialized_end=6564, + serialized_start=6562, + serialized_end=6602, ) @@ -4369,8 +4373,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=6566, - serialized_end=6633, + serialized_start=6604, + serialized_end=6671, ) _FEATURES.fields_by_name['coins'].message_type = types__pb2._COINTYPE diff --git a/tests/eip712_corpus.json b/tests/eip712_corpus.json index 8807d01c..a323cd7e 100644 --- a/tests/eip712_corpus.json +++ b/tests/eip712_corpus.json @@ -6189,5 +6189,208 @@ "message_hash": "b8c2d5a7b402683b9422031588d6231566d3813eb284905088fdab5bbbfb3e27", "digest": "6e576a58ecf5e234ea6fc3eae0b97fe2ac8fa90c5a8e822da53fad929b5977b2" } + }, + { + "id": "safe-SafeTx-multisend-4900", + "source": [ + "https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/GnosisSafe.sol", + "https://api.safe.global/tx-service/eth/api/v1/multisig-transactions/0x41bb8e482fadb98e15032ad92890e4289e3986030b7eee9ad061284481ceefc5/", + "https://etherscan.io/tx/0x9ab909ba050b277e8b426b90e299b6bd107a1baf8c59f95153b45e067475bf38" + ], + "requires_capability": "eip712-chunked-values", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "SafeTx": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "operation", + "type": "uint8" + }, + { + "name": "safeTxGas", + "type": "uint256" + }, + { + "name": "baseGas", + "type": "uint256" + }, + { + "name": "gasPrice", + "type": "uint256" + }, + { + "name": "gasToken", + "type": "address" + }, + { + "name": "refundReceiver", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + } + ] + }, + "primaryType": "SafeTx", + "domain": { + "chainId": 1, + "verifyingContract": "0x849D52316331967b6fF1198e5E32A0eB168D039d" + }, + "message": { + "to": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", + "value": "0", + "data": "0x8d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000012d20027d8bb2e33bc38a9ce93fdd90c80677b8436affb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000440c6c76b8475541524449414e2d4175726152656c6f636b657200000000000000000000000000000000000000000000005b2364fd757e262253423373e4d57c5c011ad7f40027d8bb2e33bc38a9ce93fdd90c80677b8436affb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011e47508dd98475541524449414e2d4175726152656c6f636b657200000000000000000000000000000000000000000000005b2364fd757e262253423373e4d57c5c011ad7f4d34640b20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004a0000000000000000000000000000000000000000000000000000000000000054000000000000000000000000000000000000000000000000000000000000005e00000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000860000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000ae00000000000000000000000000000000000000000000000000000000000000b800000000000000000000000000000000000000000000000000000000000000c200000000000000000000000000000000000000000000000000000000000000cc00000000000000000000000000000000000000000000000000000000000000d600000000000000000000000000000000000000000000000000000000000000e200000000000000000000000000000000000000000000000000000000000000ee00000000000000000000000000000000000000000000000000000000000000fa0000000000000000000000000000000000000000000000000000000000000106000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000", + "operation": 1, + "safeTxGas": "0", + "baseGas": "0", + "gasPrice": "0", + "gasToken": "0x0000000000000000000000000000000000000000", + "refundReceiver": "0x0000000000000000000000000000000000000000", + "nonce": "2372" + } + }, + "expected": { + "domain_separator": "561d71a273896bbd4ecfb3e135730658c966531ed15bdee93660d109a8970f01", + "message_hash": "0d579dc9c09d2e219fdeb0e2e000ca33371e6f426ebf26f2a73e0559dd5b8e78", + "digest": "41bb8e482fadb98e15032ad92890e4289e3986030b7eee9ad061284481ceefc5" + } + }, + { + "id": "snapshot-Proposal-body-3929", + "source": [ + "https://github.com/snapshot-labs/snapshot.js/blob/ae2a8209e03ca1fa133a9105949a9386f3b539ed/src/sign/types.ts", + "https://snapshot.box/#/s:arbitrumfoundation.eth/proposal/0xa69fce8c756ac96160ddb6ac09653d3254415d046fdd43e5876663e9f343f183", + "ipfs://bafkreibjtqf4eeo3lstytfcqaxeyr2ycxzxysjhtuyyxe7bkf5rzowfvtu" + ], + "requires_capability": "eip712-chunked-values", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Proposal": [ + { + "name": "from", + "type": "string" + }, + { + "name": "space", + "type": "string" + }, + { + "name": "timestamp", + "type": "uint64" + }, + { + "name": "type", + "type": "string" + }, + { + "name": "title", + "type": "string" + }, + { + "name": "body", + "type": "string" + }, + { + "name": "discussion", + "type": "string" + }, + { + "name": "choices", + "type": "string[]" + }, + { + "name": "labels", + "type": "string[]" + }, + { + "name": "start", + "type": "uint64" + }, + { + "name": "end", + "type": "uint64" + }, + { + "name": "snapshot", + "type": "uint64" + }, + { + "name": "plugins", + "type": "string" + }, + { + "name": "privacy", + "type": "string" + }, + { + "name": "app", + "type": "string" + } + ] + }, + "primaryType": "Proposal", + "domain": { + "name": "snapshot", + "version": "0.1.4" + }, + "message": { + "from": "0xb4c064f466931B8d0F637654c916E3F203c46f13", + "timestamp": 1781800138, + "space": "arbitrumfoundation.eth", + "title": "Extending DRIP\u2019s Mandate", + "body": "### Non-Constitutional\n\n## Abstract\n\nThis proposal extends the DRIP program's mandate end date from July 1st, 2026 to July 1st, 2027, giving the Season Selection Committee additional time to deploy remaining funds across future seasons. No additional funding is requested in this proposal. All other terms of the original DRIP proposal remain unchanged, such as the committee composition and the DAO's ability to terminate the program via an offchain vote.\n\n## Motivation\n\nAs background, when the DAO approved DRIP in June 2025, the proposal outlined a 1-year mandate to execute up to 4 seasons with a total budget of 80M ARB. Season durations were originally estimated to be ~3-months, despite the proposal acknowledging this length was somewhat [arbitrary](https://forum.arbitrum.foundation/t/defi-renaissance-incentive-program-drip/29049#:~:text=The%20committee%20will%20maintain%20the%20right%20to%20modify%2C%20extend%2C%20or%20discontinue%20a%20season%20during%20its%20lifecycle.%20The%20%E2%80%9C3%2Dmonth%E2%80%9D%20timeline%20is%20somewhat%20arbitrary%2C%20and%20is%20open%20to%20change%20dependent%20on%20perceived%20program%20success%20and%20market%20conditions.) and the Season Selection Committee retained the ability to modify or extend season lengths.\n\nWhen designing Season 1 in collaboration with the selected distribution partner, Merkl, it became clear that a 3-month season was likely too short of a time period to run an effective campaign. Going live in early September, Season 1 ultimately ran for approximately 5.5 months to fully utilize the distribution partnerships secured and to create a slower tapering of rewards. We believe these design decisions helped the campaign achieve an overall adjusted cost-effectiveness ratio of 51 (76 on USD assets specifically), the highest of any major Arbitrum incentive program to date, alongside yield-bearing stablecoin supply growth on Arbitrum from $130M to over $1B, as detailed in the season [retrospective](https://forum.arbitrum.foundation/t/drip-season-1-retrospective/30644).\n\nWe want to address the most obvious question upfront. Since DRIP Season 1 concluded on February 18, 2026, the program has been quiet. We recognize that work on the next season has taken longer than we anticipated and appreciate the DAO\u2019s patience.\n\nThere are several strategic developments in flight that materially affect what an effective Season 2 should be designed around. We judged it more prudent to wait than to deploy meaningful ARB in a shortened season that did not fully align with these developments should they be confirmed. Spending 15-20M ARB on the wrong campaign is a much worse outcome than waiting a few additional months to run the right one. We are not in a position to detail every development driving this judgment publicly, but the OAT, Offchain, and the Arbitrum Foundation have been looped in throughout, and we hope to be able to share more in the coming weeks. The remaining ~63.7M ARB budget continues to sit with the AF, with deployment subject to Season Selection Committee approval.\n\n## Specification\n\nExtend the DRIP mandate end date from July 1st, 2026 to July 1st, 2027.\n\nThe following language from the original proposal:\n\n> \"DRIP End Date (funds returned if not used or another proposal is not passed): July 1, 2026. An ongoing season may go past this date, but new seasons will not begin after this date.\"\n\nIs amended to:\n\n> \"DRIP End Date (funds returned if not used or another proposal is not passed): July 1, 2027. An ongoing season may go past this date, but new seasons will not begin after this date.\"\n\nAll other terms of the original DRIP proposal, including the 80M ARB total budget, season structure, Season Selection Committee composition and approval thresholds, and the DAO's ability to terminate the program remain unchanged.\n\n## Timeline\n\n- Forum: Thursday June 11th, 2026\n \n- Offchain vote: Thursday June 18th, 2026", + "type": "basic", + "discussion": "https://forum.arbitrum.foundation/t/extending-drips-mandate/30985/1", + "choices": [ + "For", + "Against", + "Abstain" + ], + "privacy": "", + "labels": [], + "start": 1781800800, + "end": 1782405600, + "snapshot": 474836218, + "plugins": "{}", + "app": "snapshot-v2" + } + }, + "expected": { + "domain_separator": "484fce18f892e8535a4b6700e197a8026f4213f809d23ae117da03b497e18670", + "message_hash": "19195d2352d10330a4ad01c832298eea9b353e113154932be82e856eb3aa87c1", + "digest": "a69fce8c756ac96160ddb6ac09653d3254415d046fdd43e5876663e9f343f183" + } } ] diff --git a/tests/test_msg_eip712_corpus.py b/tests/test_msg_eip712_corpus.py index 26b3cb61..25612caa 100644 --- a/tests/test_msg_eip712_corpus.py +++ b/tests/test_msg_eip712_corpus.py @@ -55,6 +55,7 @@ class TestMsgEip712Corpus(common.KeepKeyTest): # The device-driven walk from the streaming suite; borrowed, not # inherited, so its tests do not run twice. _walk = streaming.TestMsgEip712Streaming._walk + _chunked_values = streaming.TestMsgEip712Streaming._chunked_values def setUp(self): super(TestMsgEip712Corpus, self).setUp() diff --git a/tests/test_msg_eip712_streaming.py b/tests/test_msg_eip712_streaming.py index 7d1f4be6..2aaec87d 100644 --- a/tests/test_msg_eip712_streaming.py +++ b/tests/test_msg_eip712_streaming.py @@ -158,6 +158,59 @@ def test_reference_encoder_reproduces_the_published_spec_hashes(self): self.assertEqual(domain.hex(), SPEC_DOMAIN_SEPARATOR) self.assertEqual(message.hex(), SPEC_MESSAGE_HASH) + def test_long_values_are_chunked_only_for_capable_firmware(self): + """Over 1 KB, a value goes in 1 KB chunks: the first names the total, + each later one echoes the offset asked for. Without the capability, + or in the domain, the host refuses rather than send a value an older + device would truncate.""" + doc = { + "types": { + "EIP712Domain": [{"name": "name", "type": "string"}], + "Msg": [{"name": "data", "type": "bytes"}, + {"name": "note", "type": "string"}, + {"name": "list", "type": "uint8[]"}], + }, + "primaryType": "Msg", + "domain": {"name": "x" * 1025}, + "message": {"data": "0x" + "ab" * 1025, "note": "y" * 1024, + "list": [1]}, + } + + def request(path, offset=None): + req = eth.EthereumTypedDataValueRequest(member_path=path) + if offset is not None: + req.value_offset = offset + return req + + with self.assertRaisesRegex(es.Eip712Error, 'chunked values'): + es.value_ack(doc, request([1, 0])) + first = es.value_ack(doc, request([1, 0]), chunked=True) + self.assertEqual(first.value, b'\xab' * 1024) + self.assertEqual(first.value_total_length, 1025) + self.assertFalse(first.HasField('value_offset')) + last = es.value_ack(doc, request([1, 0], 1024), chunked=True) + self.assertEqual((last.value, last.value_offset), (b'\xab', 1024)) + self.assertFalse(last.HasField('value_total_length')) + # A string is read a second time from 0. + again = es.value_ack(doc, request([1, 0], 0), chunked=True) + self.assertEqual((len(again.value), again.value_offset), (1024, 0)) + with self.assertRaises(es.Eip712Error): + es.value_ack(doc, request([1, 0], 1025), chunked=True) + # 1024 bytes still go whole, with or without the capability. + for chunked in (False, True): + whole = es.value_ack(doc, request([1, 1]), chunked) + self.assertEqual(whole.value, b'y' * 1024) + self.assertFalse(whole.HasField('value_total_length')) + with self.assertRaises(es.Eip712Error): + es.value_ack(doc, request([1, 1], 1024), chunked) + with self.assertRaisesRegex(es.Eip712Error, 'domain'): + es.value_ack(doc, request([0, 0]), chunked=True) + with self.assertRaises(es.Eip712Error): + es.value_ack(doc, request([1, 2], 0), chunked=True) + doc["message"]["data"] = "0x" + "00" * (es.MAX_VALUE_BYTES + 1) + with self.assertRaises(es.Eip712Error): + es.value_ack(doc, request([1, 0]), chunked=True) + def test_review_identifiers_are_exact_and_unambiguous(self): doc = { 'types': { @@ -243,6 +296,8 @@ def _walk(self, doc, max_steps=400, decline_final=False): msg.address_n.append(n) msg.primary_type = doc['primaryType'] msg.metamask_v4_compat = True + # Read before the walk: reading Features mid-walk would end it. + chunked = self._chunked_values() # (ButtonRequest code, framebuffer) for every screen, in order. self.frames = [] @@ -267,15 +322,15 @@ def _walk(self, doc, max_steps=400, decline_final=False): resp = self.client.call_raw( es.build_struct_ack(es.struct_members(doc, resp.name))) elif isinstance(resp, eth.EthereumTypedDataValueRequest): - r = es.resolve_member_path(doc, list(resp.member_path)) - ack = eth.EthereumTypedDataValueAck() - ack.value = (es.encode_array_length(r[1]) if r[0] == 'length' - else es.encode_value(r[1], r[2])) - resp = self.client.call_raw(ack) + resp = self.client.call_raw(es.value_ack(doc, resp, chunked)) else: return resp raise AssertionError('walk did not terminate') + def _chunked_values(self): + """Whether the firmware takes values over 1 KB in chunks.""" + return es.CHUNKED_VALUES in (self.firmware_capabilities() or ()) + def _assert_final_sign_screen(self, first_line): """Every typed-data signature ends with ONE "Sign Typed Data" screen (ButtonRequest_SignTx); only its own continuation pages follow it.""" @@ -862,11 +917,7 @@ def test_declining_the_unlimited_warning_signs_nothing(self): resp = self.client.call_raw( es.build_struct_ack(es.struct_members(doc, resp.name))) elif isinstance(resp, eth.EthereumTypedDataValueRequest): - r = es.resolve_member_path(doc, list(resp.member_path)) - ack = eth.EthereumTypedDataValueAck() - ack.value = (es.encode_array_length(r[1]) if r[0] == 'length' - else es.encode_value(r[1], r[2])) - resp = self.client.call_raw(ack) + resp = self.client.call_raw(es.value_ack(doc, resp)) else: break self.assertIsInstance(resp, proto.Failure) @@ -966,5 +1017,181 @@ def test_1024_byte_dynamic_bytes_leaf_signs(self): self._assert_reference_signature(doc, resp) + # -- Values over one ValueAck ------------------------------------------ + # Safe MultiSend data and Snapshot proposal bodies run past 1 KB. Firmware + # with CAPABILITY_EIP712_CHUNKED_VALUES takes them in 1 KB chunks and + # shows every byte, numbered over the whole value. + + def _walk_text(self, doc, tamper=None, decline=None, max_steps=600): + """The walk, recording each confirmation's (title, body) exactly as + formatted (once per confirmation, however it is paged). `tamper` + may rewrite a value answer; `decline` names a body prefix to reject.""" + msg = eth.EthereumSignTypedData(address_n=PATH, + primary_type=doc['primaryType'], + metamask_v4_compat=True) + screens = [] + resp = self.client.call_raw(msg) + for _ in range(max_steps): + if isinstance(resp, proto.ButtonRequest): + text = self.client.debug.read_confirm_text() + if not screens or screens[-1] != text: + screens.append(text) + if decline and text[1].startswith(decline): + self.client.debug.press_no() + else: + self.client.debug.press_yes() + resp = self.client.call_raw(proto.ButtonAck()) + elif isinstance(resp, eth.EthereumTypedDataStructRequest): + resp = self.client.call_raw( + es.build_struct_ack(es.struct_members(doc, resp.name))) + elif isinstance(resp, eth.EthereumTypedDataValueRequest): + ack = es.value_ack(doc, resp, chunked=True) + if tamper: + tamper(ack) + resp = self.client.call_raw(ack) + else: + return resp, screens + raise AssertionError('walk did not terminate') + + @staticmethod + def _escaped(data): + """A string as the review draws it: 0x21-0x7e as itself but a + backslash doubled, any other byte (a space too) as \\xNN.""" + return ''.join('\\\\' if b == 0x5c else chr(b) if 0x21 <= b <= 0x7e + else '\\x%02x' % b for b in data) + + def _parts(self, screens, path, type_name): + """The parts of `path`, checked to count 1..N in order, joined.""" + joined, total = '', None + for number, (_, body) in enumerate( + b for b in screens if b[1].startswith(path + ' (')): + head, _, rest = body.partition('\n') + index, of = head[len(path) + 2:-1].split('/') + self.assertEqual(int(index), number + 1) + self.assertIn(total, (None, int(of))) + total = int(of) + self.assertTrue(rest.startswith(type_name + ': '), body[:60]) + joined += rest[len(type_name) + 2:] + self.assertIsNotNone(total, 'no part of ' + path) + self.assertEqual(number + 1, total) + return joined, total + + def _assert_signed(self, doc, resp): + self.assertIsInstance(resp, eth.EthereumTypedDataSignature, + getattr(resp, 'message', resp)) + domain, message, digest = reference_eip712_hashes(doc) + self.assertEqual(resp.domain_separator_hash.hex(), domain.hex()) + self.assertEqual(resp.message_hash.hex(), message.hex()) + address = self.client.ethereum_get_address(PATH) + self.assertEqual(recover_signer(resp.signature, digest).hex(), + address.hex()) + + @staticmethod + def _blob(data_type, value): + return { + "types": { + "EIP712Domain": [{"name": "name", "type": "string"}], + "Blob": [{"name": "data", "type": data_type}, + {"name": "n", "type": "uint256"}], + }, + "primaryType": "Blob", + "domain": {"name": "Blob"}, + "message": {"data": value, "n": 7}, + } + + def test_values_over_1kb_sign_in_chunks(self): + """1024, 1025 and 2048 bytes, and a string with a three-byte + character split across the first chunk boundary: each signs with the + independent digest, every byte is shown once and in order, and the + part counter covers the whole value from its first screen.""" + self.requires_release_capability(es.CHUNKED_VALUES) + euro = 'a' * 1023 + '\u20ac' + 'b' * 500 + cases = [('bytes', bytes(range(256)) * 4), + ('bytes', bytes(range(256)) * 4 + b'\xfe'), + ('bytes', bytes(range(256)) * 8), + ('string', ('x' * 1025).encode()), + ('string', euro.encode('utf-8'))] + for data_type, data in cases: + with self.subTest(type=data_type, length=len(data)): + value = ('0x' + data.hex() if data_type == 'bytes' + else data.decode('utf-8')) + doc = self._blob(data_type, value) + resp, screens = self._walk_text(doc) + self._assert_signed(doc, resp) + shown, parts = self._parts(screens, 'data', data_type) + self.assertEqual(shown, '0x' + data.hex() + if data_type == 'bytes' + else self._escaped(data)) + self.assertGreater(parts, 1) + + def test_misframed_chunks_are_refused(self): + """A chunk at another offset than the one asked for, or one running + past the total, is refused, and the session is gone.""" + self.requires_release_capability(es.CHUNKED_VALUES) + doc = self._blob('bytes', '0x' + 'ab' * 1500) + + def wrong_offset(ack): + if ack.HasField('value_offset'): + ack.value_offset += 1 + + def past_total(ack): + if ack.HasField('value_offset'): + ack.value = ack.value + b'\xab' * 100 + + for tamper, error in ((wrong_offset, 'EIP-712 value chunk out of order'), + (past_total, + 'EIP-712 value chunk has the wrong length')): + with self.subTest(error=error): + resp, _ = self._walk_text(doc, tamper) + self.assertIsInstance(resp, proto.Failure) + self.assertEqual((resp.code, resp.message), + (types.Failure_SyntaxError, error)) + stale = self.client.call_raw(eth.EthereumTypedDataValueAck( + value=b'\xab' * 476, value_offset=1024)) + self.assertIsInstance(stale, proto.Failure) + self._assert_signed(doc, self._walk_text(doc)[0]) + + def test_declining_a_part_mid_value_signs_nothing(self): + """Rejecting a part of the second chunk cancels; the next document + signs with its own digest.""" + self.requires_release_capability(es.CHUNKED_VALUES) + doc = self._blob('bytes', '0x' + 'cd' * 3000) + resp, screens = self._walk_text(doc, decline='data (8/') + self.assertIsInstance(resp, proto.Failure) + self.assertEqual(resp.code, types.Failure_ActionCancelled) + self.assertTrue(screens[-1][1].startswith('data (8/')) + self._assert_signed(doc, self._walk_text(doc)[0]) + + def test_unlimited_approve_in_chunked_safe_data_warns_first(self): + """approve(spender, 2^256-1) heading a 3 KB SafeTx.data: the + UNLIMITED warning is read from the first chunk and shown before any + screen of the bytes.""" + self.requires_release_capability(es.CHUNKED_VALUES) + self.requires_release_capability("erc20-unlimited-approve-review") + spender = '68b3465833fb72a70ecdf485e0e4c7bd8665fc45' + data = ('095ea7b3' + '00' * 12 + spender + 'ff' * 32).ljust(6000, '0') + doc = { + "types": { + "EIP712Domain": [{"name": "chainId", "type": "uint256"}], + "SafeTx": [{"name": "to", "type": "address"}, + {"name": "data", "type": "bytes"}], + }, + "primaryType": "SafeTx", + "domain": {"chainId": 1}, + "message": {"to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "data": "0x" + data}, + } + resp, screens = self._walk_text(doc) + self._assert_signed(doc, resp) + titles = [title for title, _ in screens] + self.assertEqual(titles.count('UNLIMITED approval'), 1) + warning = titles.index('UNLIMITED approval') + first = next(i for i, (_, body) in enumerate(screens) + if body.startswith('data (')) + self.assertLess(warning, first) + self.assertIn('to spend ALL your USDC', screens[warning][1]) + self.assertEqual(self._parts(screens, 'data', 'bytes')[0], + '0x' + data) + if __name__ == '__main__': unittest.main() From 3a914736fd52ee2026d032f145fdda3fbf113fce Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 21:08:00 -0500 Subject: [PATCH 26/37] test(display): distinguish pipe and quote substitutions in signed messages --- tests/oled_text.py | 6 +++--- tests/test_msg_display_disclosure.py | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/oled_text.py b/tests/oled_text.py index a2247aa0..df8fa3f8 100644 --- a/tests/oled_text.py +++ b/tests/oled_text.py @@ -67,12 +67,12 @@ def _font(table): 7:00000000001f03303303301f003003 7:00000000003e03303303303e030030 6:00000000001f007003003003000000 7:00000000003e00301e03001f000000 6:00000600601f00600600601c000000 7:00000000003303303303303e000000 7:00000000003303301e01e00c000000 9:0000000000db0db0db0db07e000000 7:00000000003301e00c01e033000000 7:00000000003303303303303e03001e 7:00000000003f01800c00603f000000 6:00001c00600600300600601c000000 -3:000003003003003003003003000000 6:00000700c00c01800c00c007000000 7:00003e01f000000000000000000000 +3:003003003003003003003003003003 6:00000700c00c01800c00c007000000 7:00003e01f000000000000000000000 """) BODY_FONT = _font(""" 4:00000000000000000000 2:00010101010100010000 4:00050500000000000000 7:00123f12123f12000000 -6:041e05050e14140f0400 8:00422512082452210000 7:000609090629112e0000 2:00010100000000000000 +6:041e05050e14140f0400 8:00422512082452210000 7:000609090629112e0000 3:00020100000000000000 3:02010101010101010200 3:01020202020202020100 6:04150e15040000000000 6:000004041f0404000000 3:00000000000003030201 6:000000001f0000000000 3:00000000000003030000 8:00402010080402010000 6:000e11191513110e0000 3:00030202020202020000 6:000f10100e01011f0000 6:000f10100e10100f0000 @@ -94,7 +94,7 @@ def _font(table): 6:0000000f1111110f0101 6:0000001e1111111e1010 5:0000000d030101010000 6:0000001e010e100f0000 5:0002020f0202020c0000 6:000000111111111e0000 6:00000011110a0a040000 8:00000049494949360000 6:000000110a040a110000 6:000000111111111e100e 6:0000001f0804021f0000 5:000c02020102020c0000 -2:00010101010101010000 5:00030404080404030000 7:00404020201214080000 +2:01010101010101010101 5:00030404080404030000 7:00404020201214080000 """) diff --git a/tests/test_msg_display_disclosure.py b/tests/test_msg_display_disclosure.py index c7493380..62379388 100644 --- a/tests/test_msg_display_disclosure.py +++ b/tests/test_msg_display_disclosure.py @@ -243,6 +243,14 @@ def _assert_distinguishable(self, a_label, a_msg, b_label, b_msg): # ── the property, at each place an implementation stops looking ───── + def test_pipe_is_distinguishable_from_lowercase_l(self): + self._assert_distinguishable( + 'lowercase letters', b'allow', 'vertical bars', b'a||ow') + + def test_double_quote_is_distinguishable_from_two_apostrophes(self): + self._assert_distinguishable( + 'one double quote', b'"', 'two apostrophes', b"''") + def test_bytes_past_an_embedded_nul_are_disclosed(self): """A protobuf `bytes` field is not a C string. From b858639dc8819b66d8784b40d13781c0a26607b8 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 21:08:00 -0500 Subject: [PATCH 27/37] test(eip712): distinguish colliding text across streamed values --- tests/test_msg_eip712_streaming.py | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_msg_eip712_streaming.py b/tests/test_msg_eip712_streaming.py index 2aaec87d..676a4133 100644 --- a/tests/test_msg_eip712_streaming.py +++ b/tests/test_msg_eip712_streaming.py @@ -384,6 +384,41 @@ def setUp(self): # The report entries describe typed-data fields, not the policy prompt. self.client.reset_screenshots() + def _assert_strings_have_distinct_screens(self, first, second): + doc = { + 'types': { + 'EIP712Domain': [], + 'Message': [{'name': 'note', 'type': 'string'}], + }, + 'primaryType': 'Message', 'domain': {}, 'message': {'note': first}, + } + responses, screens = [], [] + for value in (first, second): + doc['message']['note'] = value + response = self._walk(doc) + self._assert_reference_signature(doc, response) + responses.append(response) + screens.append(tuple(self.frames)) + self.assertNotEqual(responses[0].message_hash, responses[1].message_hash) + self.assertNotEqual(responses[0].signature, responses[1].signature) + self.assertFalse(screens[0] == screens[1], + 'Different signed values produced identical OLED screens') + + def test_pipe_is_distinguishable_from_lowercase_l(self): + self._assert_strings_have_distinct_screens('allow', 'a||ow') + + def test_pipe_after_chunk_boundary_is_distinguishable_from_lowercase_l(self): + if not self._chunked_values(): + self.skipTest('firmware does not support chunked EIP-712 values') + self._assert_strings_have_distinct_screens( + 'a' * 1024 + 'allow', 'a' * 1024 + 'a||ow') + + def test_distinct_visible_character_changes_review_pixels(self): + self._assert_strings_have_distinct_screens('allow', 'a!!ow') + + def test_double_quote_is_distinguishable_from_two_apostrophes(self): + self._assert_strings_have_distinct_screens('"', "''") + def test_spec_example_matches_the_published_hashes(self): """The device's own hashes equal the EIP-712 reference implementation's. From 57f2749477c2eb2c04c7c67062b30aeaee0f4e81 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 21:42:35 -0500 Subject: [PATCH 28/37] test(ethereum): verify normalized integer signatures against canonical RLP --- ...st_msg_ethereum_signtx_integer_encoding.py | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/test_msg_ethereum_signtx_integer_encoding.py b/tests/test_msg_ethereum_signtx_integer_encoding.py index ad186788..bb0b4bd6 100644 --- a/tests/test_msg_ethereum_signtx_integer_encoding.py +++ b/tests/test_msg_ethereum_signtx_integer_encoding.py @@ -17,6 +17,9 @@ import unittest import common +import rlp +from eth_keys import keys +from eth_utils import keccak from keepkeylib import messages_ethereum_pb2 as eth_proto TO = bytes.fromhex("1d1c328764a41bda0492b66baa30c4a339ff85ef") @@ -30,13 +33,34 @@ def setUp(self): # The stripping shipped in the same release block as this capability. self.requires_release_capability("evm-max-amount-review") self.setup_mnemonic_allallall() + self.path = self.client.expand_path("m/44'/60'/0'/0/0") + self.signer = self.client.ethereum_get_address(self.path) def _sign(self, **fields): resp = self.client.call(eth_proto.EthereumSignTx( - address_n=self.client.expand_path("m/44'/60'/0'/0/0"), + address_n=self.path, to=TO, chain_id=1, **fields)) self.assertIsInstance(resp, eth_proto.EthereumTxRequest) self.assertTrue(resp.HasField("signature_r")) + def integer(name): + return int.from_bytes(fields.get(name, b""), "big") + + if fields.get("type") == 2: + preimage = b"\x02" + rlp.encode([ + 1, integer("nonce"), integer("max_priority_fee_per_gas"), + integer("max_fee_per_gas"), integer("gas_limit"), TO, + integer("value"), b"", []]) + recovery_id = resp.signature_v + else: + preimage = rlp.encode([ + integer("nonce"), integer("gas_price"), integer("gas_limit"), + TO, integer("value"), b"", 1, 0, 0]) + recovery_id = resp.signature_v - 37 + signature = keys.Signature(vrs=( + recovery_id, int.from_bytes(resp.signature_r, "big"), + int.from_bytes(resp.signature_s, "big"))) + recovered = signature.recover_public_key_from_msg_hash(keccak(preimage)) + self.assertEqual(recovered.to_canonical_address(), self.signer) return resp.signature_v, resp.signature_r, resp.signature_s def _assert_same_signature(self, canonical, padded): @@ -68,6 +92,16 @@ def test_eip1559_fees_with_leading_zeros(self): max_priority_fee_per_gas=b"\x00\x3b\x9a\xca\x00", gas_limit=b"\x00\x00\x52\x08", value=b"\x00\x01")) + def test_eip1559_zero_value_in_every_spelling(self): + canonical = dict(type=2, nonce=b"", max_fee_per_gas=b"\x77\x35\x94\x00", + max_priority_fee_per_gas=b"", gas_limit=b"\x52\x08", + value=b"") + for zero in (b"\x00", b"\x00\x00\x00"): + with self.subTest(zero=zero): + self._assert_same_signature( + canonical, dict(canonical, nonce=zero, value=zero, + max_priority_fee_per_gas=zero)) + if __name__ == '__main__': unittest.main() From e55a1b5d00e276746fd4618377f1468e7cf5b9f2 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Wed, 7 Oct 2026 22:48:52 -0500 Subject: [PATCH 29/37] fix(report): require the active dice frequency threshold test --- scripts/generate-test-report.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/generate-test-report.py b/scripts/generate-test-report.py index cd4ae9f7..68b25ca1 100644 --- a/scripts/generate-test-report.py +++ b/scripts/generate-test-report.py @@ -920,7 +920,8 @@ def _arg_shown(a): ('K1c', 'test_msg_resetdevice', 'test_reset_device_dice_rejects_biased_rolls', 'Loaded die is refused', 'Fifty ones -- one face on 100% of the rolls. Refused with SyntaxError before any digest ' - 'is drawn, per Coldcard\'s 30%-per-face rule, so a biased die never becomes a wallet.', + 'is drawn: this exceeds the one-in-a-million fair-die frequency threshold. ' + 'The frequency check detects severe bias; it is not an entropy guarantee.', []), ('K1d', 'test_msg_resetdevice', 'test_reset_device_dice_only_requires_dice_entropy', 'dice_only without dice_entropy is refused', @@ -982,10 +983,11 @@ def _arg_shown(a): 'Bytes past the declared roll count must not affect the result, so uninitialized tail ' 'bytes of the roll buffer can never leak into seed material.', []), - ('K7b', 'Dice', 'BiasGateIsThirtyPercentPerFace', + ('K7b', 'Dice', 'BiasGateRefusesOnlyAtOneInAMillion', 'Loaded-die gate threshold', - 'Coldcard\'s rule: any face over 30% of the rolls is refused. 30/99 fails, 29/99 passes; ' - '16/50 fails, 15/50 (exactly 30%) passes.', + 'For every face, 25/50, 32/75, and 39/99 rolls are refused; one fewer passes. ' + 'Each threshold bounds the fair-die probability of any face reaching that count ' + 'below one in a million. This avoids rejecting ordinary fair-die sequences.', []), ('K7c', 'Dice', 'BiasGateRejectsNonDiceBytes', 'Non-d6 bytes are refused', From 405cc450508d52f772398c59e276be6ac3bd366f Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Thu, 8 Oct 2026 04:34:29 -0500 Subject: [PATCH 30/37] test(eip712): sign a real Safe MultiSend with an unlimited approve Olympus DAO's Safe batched approve(WETH, max) to the CoW vault relayer with a setPreSignature swap order (nonce 1802). The device names the spender and WETH on the UNLIMITED screen; the digest is its safeTxHash. Gated on eip712-chunked-values, the block that brings the warning. --- tests/eip712_corpus.json | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tests/eip712_corpus.json b/tests/eip712_corpus.json index a323cd7e..ff9544cf 100644 --- a/tests/eip712_corpus.json +++ b/tests/eip712_corpus.json @@ -6277,6 +6277,95 @@ "digest": "41bb8e482fadb98e15032ad92890e4289e3986030b7eee9ad061284481ceefc5" } }, + { + "id": "safe-SafeTx-multisend-approve-unlimited", + "source": [ + "https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/GnosisSafe.sol", + "https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/libraries/MultiSendCallOnly.sol", + "https://api.safe.global/tx-service/eth/api/v1/multisig-transactions/0xc5a2fac8f82fb6185796bf07e1c1d17b52538414c6e87c3dbc7b4f1bb2e16c12/", + "https://etherscan.io/tx/0x171f08630810946c52d3b379839e66b2dcb67db91f1be9ab3acbe74bac16af06" + ], + "requires_capability": "eip712-chunked-values", + "unlimited_screen": "Allow 0xC92E8bdf79f0507f65a392b0ab4667716BFE0110 to spend ALL your WETH", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "SafeTx": [ + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "operation", + "type": "uint8" + }, + { + "name": "safeTxGas", + "type": "uint256" + }, + { + "name": "baseGas", + "type": "uint256" + }, + { + "name": "gasPrice", + "type": "uint256" + }, + { + "name": "gasToken", + "type": "address" + }, + { + "name": "refundReceiver", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + } + ] + }, + "primaryType": "SafeTx", + "domain": { + "chainId": 1, + "verifyingContract": "0x245cc372C84B3645Bf0Ffe6538620B04a217988B" + }, + "message": { + "to": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", + "value": "0", + "data": "0x8d80ff0a0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019200c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000c92e8bdf79f0507f65a392b0ab4667716bfe0110ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009008d19f58aabd9ed0d60971565aa8510560ab41000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a4ec6cb13f0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000380c22c8071c7af43fd1f72d8f076e5ca5ef1bc341cd07607f2eef6a63d5940c80245cc372c84b3645bf0ffe6538620b04a217988b692df75700000000000000000000000000000000000000000000", + "operation": 1, + "safeTxGas": 0, + "baseGas": 0, + "gasPrice": "0", + "gasToken": "0x0000000000000000000000000000000000000000", + "refundReceiver": "0x0000000000000000000000000000000000000000", + "nonce": "1802" + } + }, + "expected": { + "domain_separator": "962a762e4486fcd558237b6934e27ec2383bc843e1bba8996935225f84c910ec", + "message_hash": "9b200c822d235c9321d93e495fa4e3b53eb7aa570ea8746e886b96a8a72386f0", + "digest": "c5a2fac8f82fb6185796bf07e1c1d17b52538414c6e87c3dbc7b4f1bb2e16c12" + } + }, { "id": "snapshot-Proposal-body-3929", "source": [ From cbc5e64da2ea043df8f7fbc43b05b95aca1547c2 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Thu, 8 Oct 2026 08:21:53 -0500 Subject: [PATCH 31/37] test(eip712): sign Ethermint domains and 16-point UniswapX V3 curves Evmos and Injective sign EIP-712 with a string verifyingContract and salt; the firmware refused them. Adds an Evmos MsgSend (evmosjs eip712 helpers) and Injective's EIP-712 v2 MsgSend fixture (injective-ts eip712.spec.ts), plus the Arbitrum V3 Dutch order with 16-point curves, the most the decay library accepts. Hashes match eth-sig-util. Gated on eip712-chunked-values, the block that brings the fixes. --- tests/eip712_corpus.json | 483 ++++++++++++++++++++++++++++++++ tests/test_msg_eip712_corpus.py | 3 +- 2 files changed, 485 insertions(+), 1 deletion(-) diff --git a/tests/eip712_corpus.json b/tests/eip712_corpus.json index ff9544cf..6e498a6c 100644 --- a/tests/eip712_corpus.json +++ b/tests/eip712_corpus.json @@ -5727,6 +5727,264 @@ "digest": "2a1fba9e8c26066eadd659350dc6e6cc17a9a63c8c4fbdbd771049adb754c33a" } }, + { + "id": "uniswapx-V3DutchOrder-arbitrum-curve16", + "source": [ + "https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/libraries/PermitHash.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/V3DutchOrderLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/OrderInfoLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/DutchOrderLib.sol", + "https://arbiscan.io/tx/0x1f2f270bf65d2b7f02b2a141f71e23bfcc3a84b37c9a0921577516c58031678d", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/lib/NonlinearDutchDecayLib.sol", + "https://github.com/Uniswap/UniswapX/blob/eb7ce077036258b9510b42ad37aa920cf3aaf308/src/types/Uint16Array.sol" + ], + "requires_capability": "eip712-chunked-values", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "PermitWitnessTransferFrom": [ + { + "name": "permitted", + "type": "TokenPermissions" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "witness", + "type": "V3DutchOrder" + } + ], + "NonlinearDutchDecay": [ + { + "name": "relativeBlocks", + "type": "uint256" + }, + { + "name": "relativeAmounts", + "type": "int256[]" + } + ], + "OrderInfo": [ + { + "name": "reactor", + "type": "address" + }, + { + "name": "swapper", + "type": "address" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + }, + { + "name": "additionalValidationContract", + "type": "address" + }, + { + "name": "additionalValidationData", + "type": "bytes" + } + ], + "TokenPermissions": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "V3DutchInput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "curve", + "type": "NonlinearDutchDecay" + }, + { + "name": "maxAmount", + "type": "uint256" + }, + { + "name": "adjustmentPerGweiBaseFee", + "type": "uint256" + } + ], + "V3DutchOrder": [ + { + "name": "info", + "type": "OrderInfo" + }, + { + "name": "cosigner", + "type": "address" + }, + { + "name": "startingBaseFee", + "type": "uint256" + }, + { + "name": "baseInput", + "type": "V3DutchInput" + }, + { + "name": "baseOutputs", + "type": "V3DutchOutput[]" + } + ], + "V3DutchOutput": [ + { + "name": "token", + "type": "address" + }, + { + "name": "startAmount", + "type": "uint256" + }, + { + "name": "curve", + "type": "NonlinearDutchDecay" + }, + { + "name": "recipient", + "type": "address" + }, + { + "name": "minAmount", + "type": "uint256" + }, + { + "name": "adjustmentPerGweiBaseFee", + "type": "uint256" + } + ] + }, + "primaryType": "PermitWitnessTransferFrom", + "domain": { + "name": "Permit2", + "chainId": 42161, + "verifyingContract": "0x000000000022D473030F116dDEE9F6B43aC78BA3" + }, + "message": { + "permitted": { + "token": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", + "amount": "12514477" + }, + "spender": "0xB274d5F4b833b61B340b654d600A864fB604a87c", + "nonce": "1993355410060173619975858542007397613024848003638926934372819132074995782145", + "deadline": "1791402675", + "witness": { + "info": { + "reactor": "0xB274d5F4b833b61B340b654d600A864fB604a87c", + "swapper": "0x7f05C94cE299Eea9FCdAE798Be34E226a0cb1e77", + "nonce": "1993355410060173619975858542007397613024848003638926934372819132074995782145", + "deadline": "1791402675", + "additionalValidationContract": "0x0000000000000000000000000000000000000000", + "additionalValidationData": "0x" + }, + "cosigner": "0x4449Cd34d1eb1FEDCF02A1Be3834FfDe8E6A6180", + "startingBaseFee": "0", + "baseInput": { + "token": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", + "startAmount": "12514477", + "curve": { + "relativeBlocks": "56539914882827008174051697847791522700364166669018632573655171137406238722", + "relativeAmounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + }, + "maxAmount": "12514477", + "adjustmentPerGweiBaseFee": "0" + }, + "baseOutputs": [ + { + "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + "startAmount": "10407262726", + "curve": { + "relativeBlocks": "56539914882827008174051697847791522700364166669018632573655171137406238722", + "relativeAmounts": [ + "16261348", + "32522696", + "48784044", + "65045392", + "81306740", + "97568088", + "113829436", + "130090784", + "146352132", + "162613480", + "178874828", + "195136176", + "211397524", + "227658872", + "243920220", + "260181568" + ] + }, + "recipient": "0x7f05C94cE299Eea9FCdAE798Be34E226a0cb1e77", + "minAmount": "10147081157", + "adjustmentPerGweiBaseFee": "0" + } + ] + } + } + }, + "expected": { + "domain_separator": "8a6e6e19bdfb3db3409910416b47c2f8fc28b49488d6555c7fceaa4479135bc3", + "message_hash": "1b7a77c239ffc05a83f3ac787234661b980a447d8791a54a785d9bb8950f3255", + "digest": "95fbdb5178abf6c7d90c1f5d463cc767e7b08ae701238bde83b16d002e2d5a5a" + } + }, { "id": "uniswapx-V3DutchOrder-arbitrum-fee-output", "source": [ @@ -6481,5 +6739,230 @@ "message_hash": "19195d2352d10330a4ad01c832298eea9b353e113154932be82e856eb3aa87c1", "digest": "a69fce8c756ac96160ddb6ac09653d3254415d046fdd43e5876663e9f343f183" } + }, + { + "id": "evmos-MsgSend", + "source": [ + "https://github.com/evmos/evmosjs/blob/e47e44b1683ffa57737cee2f6abbd138436d9fc3/packages/eip712/src/messages/base.ts", + "https://github.com/evmos/evmosjs/blob/e47e44b1683ffa57737cee2f6abbd138436d9fc3/packages/eip712/src/messages/bank/send.ts", + "https://github.com/evmos/evmosjs/blob/e47e44b1683ffa57737cee2f6abbd138436d9fc3/packages/eip712/src/tests/utils.ts" + ], + "requires_capability": "eip712-chunked-values", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "string" + }, + { + "name": "salt", + "type": "string" + } + ], + "Tx": [ + { + "name": "account_number", + "type": "string" + }, + { + "name": "chain_id", + "type": "string" + }, + { + "name": "fee", + "type": "Fee" + }, + { + "name": "memo", + "type": "string" + }, + { + "name": "msgs", + "type": "Msg[]" + }, + { + "name": "sequence", + "type": "string" + } + ], + "Fee": [ + { + "name": "feePayer", + "type": "string" + }, + { + "name": "amount", + "type": "Coin[]" + }, + { + "name": "gas", + "type": "string" + } + ], + "Coin": [ + { + "name": "denom", + "type": "string" + }, + { + "name": "amount", + "type": "string" + } + ], + "Msg": [ + { + "name": "type", + "type": "string" + }, + { + "name": "value", + "type": "MsgValue" + } + ], + "MsgValue": [ + { + "name": "from_address", + "type": "string" + }, + { + "name": "to_address", + "type": "string" + }, + { + "name": "amount", + "type": "TypeAmount[]" + } + ], + "TypeAmount": [ + { + "name": "denom", + "type": "string" + }, + { + "name": "amount", + "type": "string" + } + ] + }, + "primaryType": "Tx", + "domain": { + "name": "Cosmos Web3", + "version": "1.0.0", + "chainId": 9001, + "verifyingContract": "cosmos", + "salt": "0" + }, + "message": { + "account_number": "10", + "chain_id": "evmos_9001-2", + "fee": { + "feePayer": "evmos1pmk2r32ssqwps42y3c9d4clqlca403yd9wymgr", + "amount": [ + { + "denom": "aevmos", + "amount": "4000000000000000" + } + ], + "gas": "200000" + }, + "memo": "Transaction Memo", + "msgs": [ + { + "type": "cosmos-sdk/MsgSend", + "value": { + "from_address": "evmos1pmk2r32ssqwps42y3c9d4clqlca403yd9wymgr", + "to_address": "evmos12nml2w93uva0smjw3c36stczfay0k67ny94ecz", + "amount": [ + { + "denom": "aevmos", + "amount": "1000000000000000000" + } + ] + } + } + ], + "sequence": "6" + } + }, + "expected": { + "domain_separator": "1633cfb1cf33b8790808cb60604cfd1f261c59a367ccb3aaff568a000149ace9", + "message_hash": "57c04fd23163cd0f2f0d20d6e9228010f7b111e0a499c208bc798b6e1aae9d0c", + "digest": "a475e759cc3637f92de75029aeb2eee6e410efae4a0b1834d8529853f06cf5f8" + } + }, + { + "id": "injective-MsgSend-v2", + "source": [ + "https://github.com/InjectiveLabs/injective-ts/blob/f8373753d6fe0662c1a8af7fb88192ad126e2e2e/packages/sdk-ts/src/core/tx/eip712/eip712.spec.ts", + "https://github.com/InjectiveLabs/injective-ts/blob/f8373753d6fe0662c1a8af7fb88192ad126e2e2e/packages/sdk-ts/src/core/tx/eip712/utils.ts", + "https://github.com/InjectiveLabs/injective-ts/blob/f8373753d6fe0662c1a8af7fb88192ad126e2e2e/packages/utils/src/test-utils/mocks/index.ts" + ], + "requires_capability": "eip712-chunked-values", + "doc": { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "string" + } + ], + "Tx": [ + { + "name": "context", + "type": "string" + }, + { + "name": "msgs", + "type": "string" + } + ] + }, + "primaryType": "Tx", + "domain": { + "name": "Injective Web3", + "version": "1.0.0", + "chainId": "0x5", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "salt": "0" + }, + "message": { + "context": "{\"account_number\":11,\"chain_id\":\"injective-777\",\"fee\":{\"amount\":[{\"denom\":\"inj\",\"amount\":\"64000000000000\"}],\"gas\":400000,\"payer\":\"inj18j2myhaf2at75kwwaqxfstk4q28n4am45nlfg7\",\"granter\":\"inj1granter\"},\"memo\":\"\",\"sequence\":88,\"timeout_height\":1896}", + "msgs": "[{\"@type\":\"/cosmos.bank.v1beta1.MsgSend\",\"from_address\":\"inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku\",\"to_address\":\"inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku\",\"amount\":[{\"denom\":\"inj\",\"amount\":\"10000000000000000\"}]}]" + } + }, + "expected": { + "domain_separator": "a908147858193dcbc852a78aa9ab32d35da72e7229292dd41a6d1b02b305d1cd", + "message_hash": "699085c19a1eae36c57c33a44f907a1e805ce9f7c5ee1a0cdec62057839e85fb", + "digest": "0a599095745e1057fe40b2c5e36bcf753d38d1fcb8106f12c15c51aed3237e2e" + } } ] diff --git a/tests/test_msg_eip712_corpus.py b/tests/test_msg_eip712_corpus.py index 25612caa..a288af64 100644 --- a/tests/test_msg_eip712_corpus.py +++ b/tests/test_msg_eip712_corpus.py @@ -2,7 +2,8 @@ # # eip712_corpus.json holds one document per protocol (Permit2, UniswapX, # Seaport, LooksRare, Blur, 1inch, CoW, 0x, Safe, EIP-2612/DAI, Hyperliquid, -# Polymarket, Compound, Aave, dYdX, Across, Snapshot, eth-sig-util), written +# Polymarket, Compound, Aave, dYdX, Across, Snapshot, eth-sig-util, and the +# Ethermint chains Evmos and Injective), written # from the type strings in each protocol's own source ("source" in each # entry). It is generated by unittests/firmware/eip712_corpus_gen.py in the # firmware repository, whose "expected" hashes were computed by a reference From 679353921b67d864aa93b1d53403cd71ee3274fd Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Thu, 8 Oct 2026 12:31:46 -0500 Subject: [PATCH 32/37] test(storage): retry the emulator boot when its UDP port is taken The port pair is only known to be free when it is picked; another process can bind it before kkemu does, and the reboot test then failed at random. Pick a fresh pair and boot again when kkemu reports it could not bind. --- tests/test_storage_version_gate.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_storage_version_gate.py b/tests/test_storage_version_gate.py index 437e8957..0950dca1 100644 --- a/tests/test_storage_version_gate.py +++ b/tests/test_storage_version_gate.py @@ -482,6 +482,10 @@ def _free_port_pair(): raise RuntimeError("no free UDP port pair for the emulator") +class _PortTaken(Exception): + pass + + class Emulator(object): """One kkemu process over one flash image, restartable. @@ -511,7 +515,21 @@ def _ping(self): s.close() def boot(self): + # _free_port_pair() can only say the pair was free a moment ago; + # another process may bind it before kkemu does. kkemu then exits + # with "Failed to bind socket", and the image is untouched, so try + # a fresh pair rather than fail the test on a port race. + for _ in range(5): + try: + return self._boot_once() + except _PortTaken: + self.port = _free_port_pair() + raise RuntimeError("emulator could not bind a UDP port pair") + + def _boot_once(self): assert self.proc is None, "already booted" + log_path = os.path.join(self.workdir, "emu.log") + log_start = os.path.getsize(log_path) if os.path.exists(log_path) else 0 env = dict(os.environ, KEEPKEY_UDP_PORT=str(self.port)) with open(os.path.join(self.workdir, "emu.log"), "ab") as log: self.proc = subprocess.Popen( @@ -524,6 +542,10 @@ def boot(self): for _ in range(100): time.sleep(0.1) if self.proc.poll() is not None: + with open(log_path, "rb") as log: + log.seek(log_start) + if b"Failed to bind socket" in log.read(): + raise _PortTaken() raise RuntimeError( "emulator exited rc=%s before answering; see %s" % (self.proc.returncode, os.path.join(self.workdir, "emu.log"))) From f2a229236debf45289f07b1993688a91ca4a74a0 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Thu, 8 Oct 2026 14:43:14 -0500 Subject: [PATCH 33/37] test(thorchain,maya): the sign screen shows the fee in whole coins A 0.02 RUNE fee was printed as "Fee: 2000000 rune". Fails on the old firmware with exactly that text. --- tests/test_msg_mayachain_signtx.py | 27 +++++++++++++++++++++++++++ tests/test_msg_thorchain_signtx.py | 26 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/tests/test_msg_mayachain_signtx.py b/tests/test_msg_mayachain_signtx.py index 12c99ed3..55ca1cde 100644 --- a/tests/test_msg_mayachain_signtx.py +++ b/tests/test_msg_mayachain_signtx.py @@ -138,6 +138,33 @@ def _sign_and_verify_send(self, memo, amount=10000, self.assertTrue(vk.verify_digest(resp.signature, digest, sigdecode=sigdecode_string)) + def _confirm_bodies(self): + bodies = [] + original = self.client.callback_ButtonRequest + + def capture(msg): + bodies.append(self.client.debug.read_confirm_text()[1]) + return original(msg) + + self.client.callback_ButtonRequest = capture + return bodies + def test_fee_is_shown_in_whole_cacao(self): + """fee_amount is in 1e-10 CACAO; the sign screen must not print the + raw count.""" + self.requires_fullFeature() + self.requires_firmware("7.15.0") + self.setup_mnemonic_nopin_nopassphrase() + bodies = self._confirm_bodies() + self.client.mayachain_sign_tx( + address_n=parse_path(DEFAULT_BIP32_PATH), account_number=92, + chain_id="mayachain", fee=2000000, gas=200000, + msgs=[make_send(self.client.mayachain_get_address( + parse_path(DEFAULT_BIP32_PATH)), + "maya1jvt443rvhq5h8yrna55yjysvhtju0el7mdujp3", + 10000)], + memo="foobar", sequence=3, testnet=False) + self.assertIn("Fee: 0.0002 cacao. Gas: 200000.", "\n".join(bodies)) + def test_mayachain_sign_tx(self): """Native CACAO MsgSend with a plain memo; the full raw memo is paged on the OLED before signing (thorchain_confirm_full_memo is the sole diff --git a/tests/test_msg_thorchain_signtx.py b/tests/test_msg_thorchain_signtx.py index 43d11a7c..e1d86b33 100644 --- a/tests/test_msg_thorchain_signtx.py +++ b/tests/test_msg_thorchain_signtx.py @@ -142,6 +142,32 @@ def test_ack_rejects_send_and_deposit_together(self): asset="THOR.RUNE", amount=1, memo="unused", signer="thor1ls33ayg26kmltw7jjy55p32ghjna09zp6z69y8"))) + def _confirm_bodies(self): + bodies = [] + original = self.client.callback_ButtonRequest + + def capture(msg): + bodies.append(self.client.debug.read_confirm_text()[1]) + return original(msg) + + self.client.callback_ButtonRequest = capture + return bodies + def test_fee_is_shown_in_whole_rune(self): + """fee_amount is in 1e-8 RUNE. The sign screen once printed the raw + count, so a 0.02 RUNE fee read as "Fee: 2000000 rune".""" + self.requires_fullFeature() + self.requires_firmware("7.15.0") + self.setup_mnemonic_nopin_nopassphrase() + bodies = self._confirm_bodies() + self.client.thorchain_sign_tx( + address_n=parse_path(DEFAULT_BIP32_PATH), account_number=92, + chain_id="thorchain", fee=2000000, gas=200000, + msgs=[make_send("tthor1ls33ayg26kmltw7jjy55p32ghjna09zp6z69y8", + "tthor1jvt443rvhq5h8yrna55yjysvhtju0el7ldnwwy", + 10000)], + memo="foobar", sequence=3, testnet=True) + self.assertIn("Fee: 0.02 RUNE. Gas: 200000.", "\n".join(bodies)) + def test_thorchain_sign_tx(self): self.requires_fullFeature() self.requires_firmware("7.0.2") From 5c678ed7b333a6ec11630447baafe2b610f1613a Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Thu, 8 Oct 2026 14:43:14 -0500 Subject: [PATCH 34/37] test(solana): stake authorize and withdraw fit one page A forced line break before "to" paged the last characters of a 44-char address onto a screen of their own. --- tests/test_msg_solana_signtx.py | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/test_msg_solana_signtx.py b/tests/test_msg_solana_signtx.py index 0ee75dfa..da04a031 100644 --- a/tests/test_msg_solana_signtx.py +++ b/tests/test_msg_solana_signtx.py @@ -451,6 +451,42 @@ def test_solana_sign_stake_withdraw(self): address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx)) self.assertEqual(len(resp.signature), 64) + def test_solana_stake_addresses_fit_one_page(self): + """A base58 address is at most 44 characters and fits the body, so a + stake authorize or withdraw screen must not page it. A forced line + break before "to" once pushed the address tail onto a second page + on its own. A paged confirm asks once per page, so a repeated body + is a page split.""" + self.requires_fullFeature() + self.requires_firmware("7.15.0") + self.setup_mnemonic_allallall() + from_pubkey = self._get_from_pubkey() + sysvars = [b'\xC1' * 32, b'\xC2' * 32] + cases = [ + ([sysvars[0], b'\x77' * 32], + struct.pack(' Date: Thu, 8 Oct 2026 14:43:15 -0500 Subject: [PATCH 35/37] report: match 7.15 behaviour in the K, E, V and L sections - drop K8, K8b and K9: no such tests exist in this firmware, so they rendered as pending inside a passing section - unlimited approvals sign after a warning from 7.15; say so, and capture the warning screen instead of withholding it - read section L from the bitcoin-only JUnit beside the full one, where those tests actually run, and update the L3 and L11 text now that both pass --- scripts/generate-test-report.py | 119 ++++++++++++++------------------ 1 file changed, 50 insertions(+), 69 deletions(-) diff --git a/scripts/generate-test-report.py b/scripts/generate-test-report.py index 68b25ca1..f2e29fc1 100644 --- a/scripts/generate-test-report.py +++ b/scripts/generate-test-report.py @@ -202,6 +202,23 @@ def gap(self, h=4): def finish(self): self._flush() +def parse_report_junit(path): + """parse_junit, plus section L from the bitcoin-only JUnit CI writes + beside it: those tests only run on the bitcoin-only image and skip on + the full one, so the full JUnit alone shows them as withheld.""" + results = parse_junit(path) + btc = os.path.join(os.path.dirname(path), 'junit-merged-bitcoin-only.xml') + if os.path.abspath(btc) != os.path.abspath(path) and os.path.isfile(btc): + # The census and missing capabilities describe the full run only. + census, missing = dict(JUNIT_CENSUS), set(JUNIT_MISSING_CAPABILITIES) + btc_results = parse_junit(btc) + JUNIT_CENSUS.clear(); JUNIT_CENSUS.update(census) + JUNIT_MISSING_CAPABILITIES.clear(); JUNIT_MISSING_CAPABILITIES.update(missing) + for key, status in btc_results.items(): + if key.startswith('test_msg_bitcoin_only_variant::'): + results[key] = status + return results + def _lookup(results, mod, meth): """Look up a test result by module::method. Every SECTIONS module is a test_msg_* module, so parse_junit always emits a 'mod::meth' key -- there is @@ -220,8 +237,11 @@ def ver_ge(a, b): return ver_t(a) >= ver_t(b) # Tests whose newer fail-closed behavior deliberately returns before drawing a # confirmation screen. Keep their historical catalog text, but do not schedule # or audit an OLED capture once the refusal behavior is active. +# (floor, ceiling): no capture from floor up to, not including, ceiling. _NO_SCREEN_FROM = { - ('test_msg_signtx_ethereum_erc20', 'test_approve_all'): '7.14.2', + # 7.14.x refuses an unlimited approve before drawing; from 7.15 it signs + # after an UNLIMITED APPROVAL warning, which is the evidence to capture. + ('test_msg_signtx_ethereum_erc20', 'test_approve_all'): ('7.14.2', '7.15.0'), } @@ -246,8 +266,8 @@ def _screens_for(fw_version, mod, meth, screens): 'test_reset_device_dice_mixed_is_verifiable', 'test_reset_device_dice_only_is_verifiable')): return [] - floor = _NO_SCREEN_FROM.get((mod, meth)) - if floor and ver_ge(fw_version, floor): + window = _NO_SCREEN_FROM.get((mod, meth)) + if window and ver_ge(fw_version, window[0]) and not ver_ge(fw_version, window[1]): return [] return screens @@ -490,9 +510,8 @@ def _v_catalog_tests(start_id=17): silently go stale (as happened when the old hand-written V17-V23 test names drifted from the dynamically-generated ones). - Signing entries get screenshot hints. The unlimited-approval refusal is - deliberately screenless: its wire assertion requires immediate Failure, - so setup or home frames must never be used as approval evidence. + Signing entries get screenshot hints, the unlimited approval included: + from 7.15 it signs after a warning screen. """ if not CLEARSIGN_FLOWS: return [] @@ -505,12 +524,12 @@ def _v_catalog_tests(start_id=17): if f['key'] == 'erc20-approve-unlimited': out.append(( 'V%d' % i, 'test_msg_ethereum_clear_signing', method, - 'Unlimited ERC-20 approval refused before confirmation', - 'Even with AdvancedMode and valid runtime metadata, unlimited ' - 'approval returns Failure before any ButtonRequest or signature. ' - 'The wire test requires that immediate refusal; no OLED approval ' - 'screen is expected.', - [], + 'Unlimited ERC-20 approval signs after a warning', + 'From 7.15 an unlimited approval is signed after an UNLIMITED ' + 'APPROVAL warning naming the spender and token; the signature is ' + 'checked against the device address. 7.14.x refused it before ' + 'any ButtonRequest.', + ['Unlimited approval warning'], )) i += 1 continue @@ -994,28 +1013,6 @@ def _arg_shown(a): 'A byte outside \'1\'-\'6\' anywhere inside the counted rolls is refused regardless of the ' 'distribution of the rest.', []), - ('K8', 'Storage', 'PinKdfRewrapsToActiveVersionAfterCorrectPin', - 'Correct PIN unlocks and rewraps to the ACTIVE KDF', - 'The migration path for the hardened PIN KDF: an existing device must still unlock with ' - 'its current PIN, and any rewrap must target whatever KDF the build actually has ' - 'enabled. Renamed from PinKdfV16RewrapsToV19AfterCorrectPin because it is no longer ' - 'v19-specific -- the test now asserts BOTH sides of the STORAGE_PIN_KDF_V19 gate, so it ' - 'is meaningful in the shipping build where v19 is off. If this regressed, every ' - 'upgrading device would be locked out of its own seed.', - []), - ('K8b', 'Storage', 'PinUnlocksAfterRebootUnderV17', - 'The PIN still opens the wallet after a reboot', - 'The whole round trip in device order: create, set a PIN, serialize the V17 record as ' - 'storage_commit() does, reload into fresh state as a boot would, unlock, decrypt. Every ' - 'other storage test stays in RAM, and the wallet lockout this guards against lived ' - 'exactly on the serialize/reboot boundary -- a wrap the persisted record could not ' - 'describe, so the next boot derived the wrong KDF and every PIN failed.', - []), - ('K9', 'Storage', 'PinKdfV2FlagIsVersionedInV19', - 'KDF version flag is recorded in v19', - 'The new KDF is marked in the storage version band, so firmware can tell which derivation ' - 'a blob was written with instead of guessing.', - []), ('K10', 'Storage', 'StorageUpgrade_Normal', 'Normal storage upgrade path', 'Baseline upgrade across storage versions with policies and cache preserved.', @@ -1248,8 +1245,8 @@ def _arg_shown(a): ['Approval screen']), ('E11', 'test_msg_signtx_ethereum_erc20', 'test_approve_all', 'ERC-20 approve unlimited', - 'MAX_UINT256 approval. Older firmware showed an "UNLIMITED" warning; 7.14.2 and later ' - 'refuse it before drawing a confirmation screen.', + 'MAX_UINT256 approval. 7.14.2 through 7.14.x refuse it before drawing anything. From ' + '7.15 it signs after an UNLIMITED APPROVAL warning that names the spender and token.', ['Unlimited approval warning']), ('E12', 'test_msg_ethereum_makerdao', 'test_generate', 'MakerDAO generate DAI', 'Complex DeFi contract interaction (MakerDAO CDP).', []), @@ -1285,10 +1282,10 @@ def _arg_shown(a): 'Failure on the wire.', []), ('E17', 'test_msg_ethereum_erc20_uniswap_liquidity', 'test_sign_uni_approve_liquidity_ETH_refused', - 'Uniswap V2 unlimited LP-token approval refused', + 'Uniswap V2 unlimited LP-token approval refused (7.14.x)', 'Enables AdvancedMode, then attempts an unlimited FOX/WETH LP-token approval. ' - 'The device refuses it with Failure_ActionCancelled and the explicit disabled-approval ' - 'reason before any signing consent. The refusal itself is checked on the wire.', + '7.14.x refuses it with Failure_ActionCancelled before any signing consent, checked on ' + 'the wire. From 7.15 unlimited approvals sign after a warning, so this skips there.', ['Enable Policy: AdvancedMode']), ('E18', 'test_msg_ethereum_erc20_uniswap_liquidity', 'test_sign_uni_add_liquidity_ETH', 'Uniswap V2 add liquidity ETH+token', @@ -2819,17 +2816,11 @@ def _arg_shown(a): []), ('L3', 'test_msg_bitcoin_only_variant', 'test_firmware_variant_names_the_bitcoin_only_product', 'features.firmware_variant must name the product', - 'FAILED ON THE BITCOIN-ONLY IMAGE AS MEASURED, and the failure is the finding. ' - 'firmware_variant is the only wire-visible product identifier and the whole pyk suite ' - 'gates on it: common.requires_fullFeature() skips a test when it reads "KeepKeyBTC" or ' - '"EmulatorBTC". The bitcoin-only emulator reported plain "Emulator", so ' - 'requires_fullFeature() is dead code and every altcoin test in the directory runs ' - 'against a bitcoin-only image and fails instead of skipping. Section X of this report ' - 'states the KeepKeyBTC contract as fact. variant_getName() has two arms and only the ' - 'EMULATOR one returns a literal; the hardware arm takes the model variant name from ' - 'variant_getInfo() and has no BITCOIN_ONLY case at all, so bitcoin-only HARDWARE reports ' - 'exactly what a multi-chain device of the same model reports. The assertion is by ' - 'suffix, not against a fixed string, so it stays honest for both arms.', + 'The bitcoin-only emulator once reported plain "Emulator", so ' + 'common.requires_fullFeature(), which skips on "KeepKeyBTC" or "EmulatorBTC", was dead ' + 'code and altcoin tests failed against the bitcoin-only image instead of skipping. The ' + 'variant now names the product on both the emulator and hardware arms. The assertion is ' + 'by suffix, not a fixed string, so it holds for both.', []), ('L4', 'test_msg_bitcoin_only_variant', 'test_altcoin_message_handlers_are_absent', 'Every stripped chain refuses without drawing', @@ -2905,24 +2896,14 @@ def _arg_shown(a): []), ('L11', 'test_msg_bitcoin_only_variant', 'test_op_return_does_not_poison_the_duplicate_detector', 'An OP_RETURN output must not poison the duplicate detector', - 'FAILS ON BOTH PRODUCTS, and the failure is the finding. Sign a transaction whose last ' - 'output is OP_RETURN, then sign the transaction L10 just proved is allowed, and the ' - 'device answers "WARNING: DUPLICATE TRANSACTION! Already signed a tx with the same ' - 'outputs. To try again, unplug/replug KeepKey." and aborts. signing.c calls ' - 'txin_dgst_final() once per output, but txin_dgst_save_and_reset() -- the only thing that ' - 're-initialises the SHA-256 context -- is reached only on the pay-to-address path; an ' - 'OP_RETURN output returns before it. So a transaction ending in OP_RETURN leaves the ' - 'context finalised and never re-initialised, the next transaction\'s inputs are hashed ' - 'into a finalised context, and its digest no longer matches while amount and address ' - 'still do -- precisely the (same outputs, different inputs) pattern the check exists to ' - 'flag. Fail-safe, in that it refuses rather than signs, but it refuses a legitimate ' - 'transaction and demands a replug, and every OP_RETURN-terminated transaction arms it: ' - 'that is every THORChain and Maya swap the wallet builds. Nothing had caught it because ' - 'common.KeepKeyTest wipes the device in setUp, so no existing test signs two transactions ' - 'in one session.', + 'Sign a transaction whose last output is OP_RETURN, then sign the transaction L10 proved ' + 'is allowed: it must sign. It once failed on both products with "WARNING: DUPLICATE ' + 'TRANSACTION!", because an OP_RETURN output returned before txin_dgst_save_and_reset() ' + 're-initialised the digest context, so the next transaction hashed its inputs into a ' + 'finalised context. Every THORChain and Maya swap ends in OP_RETURN and armed it. The ' + 'common setUp wipes the device, so no other test signs two transactions in one session.', ['Send 0.0038 BTC to 1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1 (first transaction)', - 'CONFIRM OP_RETURN: the memo that arms the detector', - 'WARNING: DUPLICATE TRANSACTION! Already signed a tx with the same outputs']), + 'CONFIRM OP_RETURN: the memo that used to arm the detector']), ]), ('U', 'Storage Upgrade Preservation', '7.15.0', 'A signed UPGRADE must never wipe. An older firmware must never parse a newer storage ' @@ -4150,7 +4131,7 @@ def main(): if not args.junit: print('ERROR: --validate-junit requires --junit=', file=sys.stderr) sys.exit(2) - results = parse_junit(args.junit) + results = parse_report_junit(args.junit) ok, failures = validate_junit(fw, results, args.variant) if ok: print(f'SECTIONS validation passed: all tests for fw {fw} are pass or skip') @@ -4172,7 +4153,7 @@ def main(): if supplied: os.environ['KK_BUILD_LABEL'] = ' | '.join(supplied) - results = parse_junit(args.junit) if args.junit else {} + results = parse_report_junit(args.junit) if args.junit else {} render(args.output, fw, results, args.screenshots) if __name__ == '__main__': From 0601dda1b941cdc6c42cc46cfb67edd2d4170448 Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Thu, 8 Oct 2026 15:10:25 -0500 Subject: [PATCH 36/37] report: keep the staged PIN-KDF controls; render them withheld K8, K8b and K9 are staged controls: a complete release requires them, and validate_junit exempts them only while the firmware reports storage-v19-kdf missing. Restore the rows, and in the PDF show a staged control as withheld when its capability is missing instead of pending. --- scripts/generate-test-report.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/scripts/generate-test-report.py b/scripts/generate-test-report.py index f2e29fc1..b114ca37 100644 --- a/scripts/generate-test-report.py +++ b/scripts/generate-test-report.py @@ -1013,6 +1013,28 @@ def _arg_shown(a): 'A byte outside \'1\'-\'6\' anywhere inside the counted rolls is refused regardless of the ' 'distribution of the rest.', []), + ('K8', 'Storage', 'PinKdfRewrapsToActiveVersionAfterCorrectPin', + 'Correct PIN unlocks and rewraps to the ACTIVE KDF', + 'The migration path for the hardened PIN KDF: an existing device must still unlock with ' + 'its current PIN, and any rewrap must target whatever KDF the build actually has ' + 'enabled. Renamed from PinKdfV16RewrapsToV19AfterCorrectPin because it is no longer ' + 'v19-specific -- the test now asserts BOTH sides of the STORAGE_PIN_KDF_V19 gate, so it ' + 'is meaningful in the shipping build where v19 is off. If this regressed, every ' + 'upgrading device would be locked out of its own seed.', + []), + ('K8b', 'Storage', 'PinUnlocksAfterRebootUnderV17', + 'The PIN still opens the wallet after a reboot', + 'The whole round trip in device order: create, set a PIN, serialize the V17 record as ' + 'storage_commit() does, reload into fresh state as a boot would, unlock, decrypt. Every ' + 'other storage test stays in RAM, and the wallet lockout this guards against lived ' + 'exactly on the serialize/reboot boundary -- a wrap the persisted record could not ' + 'describe, so the next boot derived the wrong KDF and every PIN failed.', + []), + ('K9', 'Storage', 'PinKdfV2FlagIsVersionedInV19', + 'KDF version flag is recorded in v19', + 'The new KDF is marked in the storage version band, so firmware can tell which derivation ' + 'a blob was written with instead of guessing.', + []), ('K10', 'Storage', 'StorageUpgrade_Normal', 'Normal storage upgrade path', 'Baseline upgrade across storage versions with policies and cache preserved.', @@ -4154,6 +4176,12 @@ def main(): os.environ['KK_BUILD_LABEL'] = ' | '.join(supplied) results = parse_report_junit(args.junit) if args.junit else {} + # A staged control has no row of its own in a tree whose firmware reports + # its capability missing; show it withheld rather than pending. Rendering + # only: validate_junit applies the same exemption itself. + for (mod, meth), cap in _TEST_CAPABILITY.items(): + if cap in JUNIT_MISSING_CAPABILITIES: + results.setdefault('%s::%s' % (mod, meth), 'skip') render(args.output, fw, results, args.screenshots) if __name__ == '__main__': From 8d628501f9a8b2c9484a1f4df1639b10909bd72b Mon Sep 17 00:00:00 2001 From: Matt Highlander Date: Thu, 8 Oct 2026 16:50:37 -0500 Subject: [PATCH 37/37] report: describe E16 and E16b as 7.15 behaves Hash-only signing is no longer the only EIP-712 path: the streamed protocol in section TD reads the document. E16 is the blind path for hash-only hosts; E16b proves the old JSON endpoint stays refused. --- scripts/generate-test-report.py | 35 +++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/scripts/generate-test-report.py b/scripts/generate-test-report.py index b114ca37..bf934cbb 100644 --- a/scripts/generate-test-report.py +++ b/scripts/generate-test-report.py @@ -1279,29 +1279,22 @@ def _arg_shown(a): ('E15', 'test_msg_ethereum_cfunc', 'test_sign_execTx', 'Contract function call', 'Generic contract call signing.', []), ('E16', 'test_sign_typed_data', 'test_ethereum_sign_typed_data_hash', - 'EIP-712 typed data is BLIND-signed, behind AdvancedMode', - 'The only working EIP-712 path. The host computes both 32-byte hashes and the device ' - 'signs them, so it cannot show a recipient, an amount or a chain -- it shows the two ' - 'digests and asks whether to trust the host. The test proves both halves of the gate: ' - 'with AdvancedMode ON the signature is produced, and with it OFF the device refuses ' - 'with "Enable AdvancedMode to blind-sign typed hashes". Every EIP-712 signature a ' - 'KeepKey produces today, Permit2 approvals included, takes this path.', + 'Precomputed EIP-712 hashes are BLIND-signed, behind AdvancedMode', + 'For a host that sends only the two 32-byte hashes. The device cannot show a recipient, ' + 'an amount or a chain from them -- it shows the digests and asks whether to trust the ' + 'host. The test proves both halves of the gate: with AdvancedMode ON the signature is ' + 'produced, and with it OFF the device refuses with "Enable AdvancedMode to blind-sign ' + 'typed hashes". From 7.15 a host that sends the document itself gets the streamed path ' + 'in section TD, where the device reads and shows every field it signs.', []), ('E16b', 'test_sign_typed_data', 'test_ethereum_sign_x402_eip3009', - 'Structured EIP-712 is DISABLED, and x402 EIP-3009 is refused', - 'This entry asserted the opposite until 2026-08-21, and the report shipped it green: it ' - 'claimed the device "computes the EIP-712 hashes itself and displays every ' - 'TransferWithAuthorization field", and declared two screens for fields that are never ' - 'drawn. The test underneath had already been rewritten to assert the REFUSAL. A reader ' - 'would have concluded x402 EVM payments clear-sign. They do not.\n' - 'What the test actually proves: Ethereum712TypesValues is answered with ' - '"Structured EIP-712 disabled pending canonical display hardening". The JSON parser ' - 'could not guarantee the displayed value was the value hashed, so 7.14.2 withdrew the ' - 'path rather than ship it. The EIP-712 V4 reference hashes stay in the fixture, unused, ' - 'as the vector to re-assert when the streaming implementation lands (SRS-7.16 R-4.1, ' - 'R-4.2).\n' - 'The screen list is EMPTY because a refusal draws nothing -- the evidence is the ' - 'Failure on the wire.', + 'The legacy one-shot JSON EIP-712 endpoint stays refused', + 'Ethereum712TypesValues is answered with "Structured EIP-712 disabled". Its JSON parser ' + 'could not guarantee that the value displayed was the value hashed, so 7.14.2 withdrew it ' + 'and 7.15 keeps it closed: structured EIP-712 goes through the streamed protocol in ' + 'section TD instead. The vector is an x402 EIP-3009 TransferWithAuthorization; its EIP-712 ' + 'V4 reference hashes stay in the fixture as an independent oracle. The screen list is ' + 'empty because a refusal draws nothing -- the evidence is the Failure on the wire.', []), ('E17', 'test_msg_ethereum_erc20_uniswap_liquidity', 'test_sign_uni_approve_liquidity_ETH_refused', 'Uniswap V2 unlimited LP-token approval refused (7.14.x)',