From c5983f3aa7fd15daef79df28d251e4d344a13a4b Mon Sep 17 00:00:00 2001 From: highlander Date: Mon, 23 Mar 2026 15:26:11 -0600 Subject: [PATCH 01/45] feat(zcash): add Orchard shielded transaction protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PCZT streaming protocol: ZcashSignPCZT, ZcashPCZTAction, ZcashPCZTActionAck - Orchard FVK: ZcashGetOrchardFVK, ZcashOrchardFVK - Transparent shielding: ZcashTransparentInput, ZcashTransparentSig - Wire IDs 1300-1307 - nanopb options for all fields Multi-phase protocol: session init → action streaming → transparent signing. Supports on-device Orchard digest verification via sub-digest fields. --- messages-zcash.options | 33 +++++++++ messages-zcash.proto | 150 +++++++++++++++++++++++++++++++++++++++++ messages.proto | 9 +++ 3 files changed, 192 insertions(+) create mode 100644 messages-zcash.options create mode 100644 messages-zcash.proto diff --git a/messages-zcash.options b/messages-zcash.options new file mode 100644 index 00000000..c1d904a0 --- /dev/null +++ b/messages-zcash.options @@ -0,0 +1,33 @@ +ZcashSignPCZT.address_n max_count:8 +ZcashSignPCZT.pczt_data max_size:4096 +ZcashSignPCZT.header_digest max_size:32 +ZcashSignPCZT.transparent_digest max_size:32 +ZcashSignPCZT.sapling_digest max_size:32 +ZcashSignPCZT.orchard_digest max_size:32 +ZcashSignPCZT.orchard_anchor max_size:32 + +ZcashPCZTAction.alpha max_size:32 +ZcashPCZTAction.sighash max_size:32 +ZcashPCZTAction.cv_net max_size:32 +ZcashPCZTAction.nullifier max_size:32 +ZcashPCZTAction.cmx max_size:32 +ZcashPCZTAction.epk max_size:32 +ZcashPCZTAction.enc_compact max_size:52 +ZcashPCZTAction.enc_memo max_size:512 +ZcashPCZTAction.enc_noncompact max_size:612 +ZcashPCZTAction.rk max_size:32 +ZcashPCZTAction.out_ciphertext max_size:80 + +ZcashSignedPCZT.signatures max_count:64 max_size:64 +ZcashSignedPCZT.txid max_size:32 + +ZcashGetOrchardFVK.address_n max_count:8 + +ZcashOrchardFVK.ak max_size:32 +ZcashOrchardFVK.nk max_size:32 +ZcashOrchardFVK.rivk max_size:32 + +ZcashTransparentInput.sighash max_size:32 +ZcashTransparentInput.address_n max_count:8 + +ZcashTransparentSig.signature max_size:73 diff --git a/messages-zcash.proto b/messages-zcash.proto new file mode 100644 index 00000000..4f47c245 --- /dev/null +++ b/messages-zcash.proto @@ -0,0 +1,150 @@ +/* + * Messages (Zcash specific) for KeepKey Communication + * + * Zcash shielded transaction signing via PCZT (Partially Constructed + * Zcash Transaction) format. Supports Orchard spend authorization. + */ + +syntax = "proto2"; + +// Sugar for easier handling in Java +option java_package = "com.keepkey.deviceprotocol"; +option java_outer_classname = "KeepKeyMessageZcash"; + +/** + * Request: Sign a Zcash shielded transaction (PCZT format) + * + * The PCZT contains pre-constructed transaction data with proofs. + * The device derives spend authorization keys, computes the sighash, + * and returns RedPallas signatures for each Orchard action. + * + * @next ZcashPCZTActionAck + * @next Failure + */ +message ZcashSignPCZT { + repeated uint32 address_n = 1; // ZIP-32 derivation path [32', 133', account'] + optional uint32 account = 2; // Account index (alternative to full path) + optional bytes pczt_data = 3; // Serialized PCZT data (may be chunked) + optional uint32 n_actions = 4; // Number of Orchard actions to sign + optional uint64 total_amount = 5; // Total ZEC amount (zatoshis) for user confirmation + optional uint64 fee = 6; // Transaction fee (zatoshis) + optional uint32 branch_id = 7; // Consensus branch ID + // Phase 2a: sub-digests for on-device sighash computation + optional bytes header_digest = 8; // 32-byte pre-computed header digest + optional bytes transparent_digest = 9; // 32-byte transparent digest (or empty) + optional bytes sapling_digest = 10; // 32-byte sapling digest (or empty) + optional bytes orchard_digest = 11; // 32-byte orchard digest + // Phase 2b: bundle metadata for orchard digest verification + optional uint32 orchard_flags = 12; // Orchard bundle flags byte + optional int64 orchard_value_balance = 13; // Orchard value balance (LE i64) + optional bytes orchard_anchor = 14; // 32-byte orchard anchor + // Phase 3: transparent shielding support + optional uint32 n_transparent_inputs = 30; // 0 for shielded-only (default), >0 for hybrid shielding tx +} + +/** + * Per-action signing data extracted from PCZT. + * Sent as individual messages for streaming large transactions. + * + * @next ZcashSignedPCZT + * @next ZcashPCZTActionAck + * @next Failure + */ +message ZcashPCZTAction { + optional uint32 index = 1; // Action index within the Orchard bundle + optional bytes alpha = 2; // 32-byte spend authorization randomizer + optional bytes sighash = 3; // 32-byte transaction sighash (ZIP 244) - legacy mode + optional bytes cv_net = 4; // 32-byte value commitment + optional uint64 value = 5; // Action value in zatoshis (for display) + optional bool is_spend = 6; // True if this action spends a note + // Phase 2b: action fields for incremental orchard digest verification + optional bytes nullifier = 7; // 32-byte nullifier + optional bytes cmx = 8; // 32-byte note commitment + optional bytes epk = 9; // 32-byte ephemeral key + optional bytes enc_compact = 10; // 52-byte compact encrypted note + optional bytes enc_memo = 11; // 512-byte encrypted memo + optional bytes enc_noncompact = 12; // Remaining encrypted note bytes + optional bytes rk = 13; // 32-byte randomized verification key + optional bytes out_ciphertext = 14; // 80-byte output ciphertext +} + +/** + * Response: Acknowledgment requesting next action data + * + * @prev ZcashSignPCZT + * @prev ZcashPCZTAction + */ +message ZcashPCZTActionAck { + optional uint32 next_index = 1; // Index of next action to process +} + +/** + * Response: Signed PCZT with spend authorization signatures + * + * @prev ZcashPCZTAction + */ +message ZcashSignedPCZT { + repeated bytes signatures = 1; // 64-byte RedPallas signatures, one per action + optional bytes txid = 2; // 32-byte computed transaction ID +} + +/** + * Request: Get the Orchard Full Viewing Key for a given account. + * + * The FVK (ak, nk, rivk) is safe to export - it allows viewing + * transactions but cannot spend funds. Used to construct unified addresses. + * + * @next ZcashOrchardFVK + * @next Failure + */ +message ZcashGetOrchardFVK { + repeated uint32 address_n = 1; // ZIP-32 derivation path [32', 133', account'] + optional uint32 account = 2; // Account index (alternative to full path) + optional bool show_display = 3; // Show on device display +} + +/** + * Response: Orchard Full Viewing Key components. + * + * ak = [ask]G on Pallas curve (serialized point, 32 bytes) + * nk = nullifier deriving key (32 bytes) + * rivk = commitment randomness key (32 bytes) + * + * @prev ZcashGetOrchardFVK + */ +message ZcashOrchardFVK { + optional bytes ak = 1; // 32-byte authorizing key (Pallas point) + optional bytes nk = 2; // 32-byte nullifier deriving key + optional bytes rivk = 3; // 32-byte commitment randomness key +} + +/** + * Request: Transparent input data for hybrid shielding transactions. + * Sent one per transparent input during the transparent signing phase. + * The device ECDSA-signs the per-input sighash with the secp256k1 key + * at the provided BIP44 path. + * + * Flow: after ZcashSignPCZT with n_transparent_inputs > 0, the device + * responds with ZcashPCZTActionAck. For each transparent input, the host + * sends ZcashTransparentInput and receives ZcashTransparentSig. After + * all transparent inputs, the device transitions to the Orchard phase. + * + * @next ZcashTransparentSig + * @next Failure + */ +message ZcashTransparentInput { + required uint32 index = 1; // Input index within the transaction + required bytes sighash = 2; // 32-byte per-input sighash (host-computed, ZIP-244) + repeated uint32 address_n = 3; // BIP44 path [44', 133', 0', 0, 0] + optional uint64 amount = 4; // Input value in zatoshis (for display verification) +} + +/** + * Response: ECDSA signature for a transparent input. + * + * @prev ZcashTransparentInput + */ +message ZcashTransparentSig { + required bytes signature = 1; // DER ECDSA signature (72-73 bytes) + optional uint32 next_index = 2; // Next transparent input index, or 0xFF = done +} diff --git a/messages.proto b/messages.proto index b26f1b01..7b12d11c 100644 --- a/messages.proto +++ b/messages.proto @@ -203,6 +203,15 @@ enum MessageType { MessageType_MayachainMsgAck = 1204 [ (wire_in) = true ]; MessageType_MayachainSignedTx = 1205 [ (wire_out) = true ]; + // Zcash (Orchard shielded) + MessageType_ZcashSignPCZT = 1300 [ (wire_in) = true ]; + MessageType_ZcashPCZTAction = 1301 [ (wire_in) = true ]; + MessageType_ZcashPCZTActionAck = 1302 [ (wire_out) = true ]; + MessageType_ZcashSignedPCZT = 1303 [ (wire_out) = true ]; + MessageType_ZcashGetOrchardFVK = 1304 [ (wire_in) = true ]; + MessageType_ZcashOrchardFVK = 1305 [ (wire_out) = true ]; + MessageType_ZcashTransparentInput = 1306 [ (wire_in) = true ]; + MessageType_ZcashTransparentSig = 1307 [ (wire_out) = true ]; // TRON MessageType_TronGetAddress = 1400 [ (wire_in) = true ]; MessageType_TronAddress = 1401 [ (wire_out) = true ]; From 4ce26e7278f5e94822e5f5867f16385e488d2cf0 Mon Sep 17 00:00:00 2001 From: highlander Date: Mon, 23 Mar 2026 15:35:41 -0600 Subject: [PATCH 02/45] fix: include messages-zcash.proto in package build scripts --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ca6eb0ae..b9d2c012 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "scripts": { "clean": "rm -rf ./lib/*.js ./lib/*.ts", "build": "npm run build:js && npm run build:json && npm run build:postprocess", - "build:js": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto", - "build:json": "pbjs --keep-case -t json ./types.proto ./messages.proto ./messages-ethereum.proto ./messages-eos.proto ./messages-nano.proto ./messages-cosmos.proto ./messages-binance.proto ./messages-ripple.proto ./messages-tendermint.proto ./messages-thorchain.proto ./messages-osmosis.proto ./messages-mayachain.proto > ./lib/proto.json", + "build:js": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-zcash.proto", + "build:json": "pbjs --keep-case -t json ./types.proto ./messages.proto ./messages-ethereum.proto ./messages-eos.proto ./messages-nano.proto ./messages-cosmos.proto ./messages-binance.proto ./messages-ripple.proto ./messages-tendermint.proto ./messages-thorchain.proto ./messages-osmosis.proto ./messages-mayachain.proto ./messages-zcash.proto > ./lib/proto.json", "build:postprocess": "find ./lib -name \"*.js\" -exec sed -i '' -e \"s/var global = Function(\\'return this\\')();/var global = (function(){ return this }).call(null);/g\" {} \\;", "prepublishOnly": "npm run build", "test": "echo \"Error: no test specified\" && exit 1" From 742f17263946802291053a959c4d1cb940f63f0d Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 24 Mar 2026 19:54:19 -0600 Subject: [PATCH 03/45] feat(zcash): add ZcashDisplayAddress protocol message (IDs 1308/1309) New messages for displaying a Zcash unified address on the device screen with FVK verification. The host provides the UA string and FVK components; the device independently derives FVK from seed and verifies the match before displaying the address with a QR code. - ZcashDisplayAddress (wire_in 1308): address + ak/nk/rivk for verification - ZcashAddress (wire_out 1309): confirmed address after user approval - nanopb options: address max_size:128, key fields max_size:32 --- messages-zcash.options | 8 ++++++++ messages-zcash.proto | 33 +++++++++++++++++++++++++++++++++ messages.proto | 2 ++ 3 files changed, 43 insertions(+) diff --git a/messages-zcash.options b/messages-zcash.options index c1d904a0..a80f1ec3 100644 --- a/messages-zcash.options +++ b/messages-zcash.options @@ -31,3 +31,11 @@ ZcashTransparentInput.sighash max_size:32 ZcashTransparentInput.address_n max_count:8 ZcashTransparentSig.signature max_size:73 + +ZcashDisplayAddress.address_n max_count:8 +ZcashDisplayAddress.address max_size:128 +ZcashDisplayAddress.ak max_size:32 +ZcashDisplayAddress.nk max_size:32 +ZcashDisplayAddress.rivk max_size:32 + +ZcashAddress.address max_size:128 diff --git a/messages-zcash.proto b/messages-zcash.proto index 4f47c245..667721f1 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -148,3 +148,36 @@ message ZcashTransparentSig { required bytes signature = 1; // DER ECDSA signature (72-73 bytes) optional uint32 next_index = 2; // Next transparent input index, or 0xFF = done } + +/** + * Request: Display a Zcash unified address on the device screen. + * + * The host provides the unified address string and the FVK components + * (ak, nk, rivk) used to derive it. The device independently derives + * the FVK from the seed and verifies it matches the provided components + * before displaying the address with a QR code. + * + * Security: The device confirms the address belongs to the correct + * seed/account by verifying the FVK. The address itself is host-computed + * (full on-device UA derivation requires Sinsemilla/SWU not yet in firmware). + * + * @next ZcashAddress + * @next Failure + */ +message ZcashDisplayAddress { + repeated uint32 address_n = 1; // ZIP-32 derivation path [32', 133', account'] + optional uint32 account = 2; // Account index (alternative to full path) + optional string address = 3; // Host-computed unified address ("u1...") + optional bytes ak = 4; // 32-byte ak for FVK verification + optional bytes nk = 5; // 32-byte nk for FVK verification + optional bytes rivk = 6; // 32-byte rivk for FVK verification +} + +/** + * Response: Confirmed Zcash address after user approval on device. + * + * @prev ZcashDisplayAddress + */ +message ZcashAddress { + optional string address = 1; // Confirmed unified address +} diff --git a/messages.proto b/messages.proto index 7b12d11c..346653e6 100644 --- a/messages.proto +++ b/messages.proto @@ -212,6 +212,8 @@ enum MessageType { MessageType_ZcashOrchardFVK = 1305 [ (wire_out) = true ]; MessageType_ZcashTransparentInput = 1306 [ (wire_in) = true ]; MessageType_ZcashTransparentSig = 1307 [ (wire_out) = true ]; + MessageType_ZcashDisplayAddress = 1308 [ (wire_in) = true ]; + MessageType_ZcashAddress = 1309 [ (wire_out) = true ]; // TRON MessageType_TronGetAddress = 1400 [ (wire_in) = true ]; MessageType_TronAddress = 1401 [ (wire_out) = true ]; From 41be377d9f84edb6b0483a1c21156c9472e9c24e Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 24 Mar 2026 20:11:15 -0600 Subject: [PATCH 04/45] docs(zcash): clarify ZcashDisplayAddress verification scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device only verifies the Orchard FVK — it cannot verify transparent or Sapling receivers that may also be bundled in a Unified Address. Updated proto comments to explicitly state the guarantee: "This UA contains an Orchard receiver from this account" rather than implying full address ownership. Also clarified that account or address_n is required (no silent fallback to account 0). --- messages-zcash.proto | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/messages-zcash.proto b/messages-zcash.proto index 667721f1..4eaf7dd2 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -157,20 +157,31 @@ message ZcashTransparentSig { * the FVK from the seed and verifies it matches the provided components * before displaying the address with a QR code. * - * Security: The device confirms the address belongs to the correct - * seed/account by verifying the FVK. The address itself is host-computed - * (full on-device UA derivation requires Sinsemilla/SWU not yet in firmware). + * VERIFICATION SCOPE: The device only verifies that the Orchard FVK + * (ak, nk, rivk) in this request matches what it derives from the seed + * at the given account. A Unified Address may bundle receivers from + * multiple pools (transparent, Sapling, Orchard). The device CANNOT + * verify non-Orchard receivers — the guarantee is limited to: + * "This UA contains an Orchard receiver from this account." + * It does NOT guarantee that transparent or Sapling receivers (if + * present) are also controlled by this device. + * + * Full on-device UA derivation (Sinsemilla + SWU hash-to-curve) + * is planned for a future firmware release. + * + * Either account or a complete address_n path is REQUIRED. + * The device will reject requests that omit both. * * @next ZcashAddress * @next Failure */ message ZcashDisplayAddress { - repeated uint32 address_n = 1; // ZIP-32 derivation path [32', 133', account'] - optional uint32 account = 2; // Account index (alternative to full path) + repeated uint32 address_n = 1; // ZIP-32 path [32', 133', account'] — required if account omitted + optional uint32 account = 2; // Account index — required if address_n omitted optional string address = 3; // Host-computed unified address ("u1...") - optional bytes ak = 4; // 32-byte ak for FVK verification - optional bytes nk = 5; // 32-byte nk for FVK verification - optional bytes rivk = 6; // 32-byte rivk for FVK verification + optional bytes ak = 4; // 32-byte ak for Orchard FVK verification + optional bytes nk = 5; // 32-byte nk for Orchard FVK verification + optional bytes rivk = 6; // 32-byte rivk for Orchard FVK verification } /** From 0e3dc97f3eabfdaeb68e7c08b0fc88452eba990a Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 28 Apr 2026 19:54:03 -0500 Subject: [PATCH 05/45] feat(tron): add SignMessage (TIP-191), VerifyMessage, SignTypedHash (TIP-712) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds proto definitions for TRON message-signing parity: - TronSignMessage / TronMessageSignature (1404/1405) — TIP-191 personal_sign - TronVerifyMessage (1406) — host-asserted signature verification - TronSignTypedHash / TronTypedDataSignature (1407/1408) — TIP-712 hash mode Mirrors the Ethereum personal_sign + EIP-712 hash-mode shape. Firmware implementation will reuse the secp256k1 + keccak256 primitives already present for Ethereum, swapping the message prefix to '\x19TRON Signed Message:\n' for TIP-191 and using '\x19\x01' for TIP-712. Reserves IDs 1404-1408 contiguous to existing TRON range (1400-1403). --- messages-tron.options | 14 ++++++++++ messages-tron.proto | 65 +++++++++++++++++++++++++++++++++++++++++++ messages.proto | 5 ++++ 3 files changed, 84 insertions(+) diff --git a/messages-tron.options b/messages-tron.options index c6437134..1e2ca71a 100644 --- a/messages-tron.options +++ b/messages-tron.options @@ -14,3 +14,17 @@ TronTriggerSmartContract.contract_address max_size:35 TronTriggerSmartContract.data max_size:512 TronSignTx.data max_size:256 TronSignedTx.serialized_tx max_size:1024 +TronSignMessage.address_n max_count:8 +TronSignMessage.coin_name max_size:21 +TronSignMessage.message max_size:1024 +TronMessageSignature.address max_size:35 +TronMessageSignature.signature max_size:65 +TronVerifyMessage.address max_size:35 +TronVerifyMessage.signature max_size:65 +TronVerifyMessage.message max_size:1024 +TronSignTypedHash.address_n max_count:8 +TronSignTypedHash.coin_name max_size:21 +TronSignTypedHash.domain_separator_hash max_size:32 +TronSignTypedHash.message_hash max_size:32 +TronTypedDataSignature.address max_size:35 +TronTypedDataSignature.signature max_size:65 diff --git a/messages-tron.proto b/messages-tron.proto index cb566450..f091c785 100644 --- a/messages-tron.proto +++ b/messages-tron.proto @@ -81,3 +81,68 @@ message TronSignedTx { optional bytes signature = 1; // ECDSA signature (65 bytes: r + s + recovery_id) optional bytes serialized_tx = 2; // Reconstructed raw_data bytes (for host verification) } + +////////////////////////////////////// +// TRON: Message signing (TIP-191) // +////////////////////////////////////// + +/** + * Request: Ask device to sign a message using TIP-191 personal_sign + * Hash: keccak256("\x19TRON Signed Message:\n" + len(message) + message) + * @next TronMessageSignature + * @next Failure + */ +message TronSignMessage { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional string coin_name = 2 [default='Tron']; + optional bytes message = 3; // Message bytes to sign + optional bool show_display = 4; // Show message on device display +} + +/** + * Response: Signed message + * @prev TronSignMessage + */ +message TronMessageSignature { + optional string address = 1; // Base58Check TRON address that signed + optional bytes signature = 2; // 65-byte signature (r + s + v) +} + +/** + * Request: Ask device to verify a TIP-191 message signature + * @next Success + * @next Failure + */ +message TronVerifyMessage { + optional string address = 1; // Base58Check TRON address claimed to have signed + optional bytes signature = 2; // 65-byte signature to verify + optional bytes message = 3; // Message that was signed +} + +////////////////////////////////////// +// TRON: Typed data signing (TIP-712)// +////////////////////////////////////// + +/** + * Request: Ask device to sign hash of typed data (TIP-712 hash mode) + * Domain separation: keccak256("\x19\x01" + domain_separator_hash + message_hash) + * Host pre-computes the EIP-712-style domainSeparator and message hashes. + * @start + * @next TronTypedDataSignature + * @next Failure + */ +message TronSignTypedHash { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional string coin_name = 2 [default='Tron']; + required bytes domain_separator_hash = 3; // 32-byte domainSeparator hash + optional bytes message_hash = 4; // 32-byte message hash (empty if domain-only) +} + +/** + * Response: Signed typed data + * @prev TronSignTypedHash + */ +message TronTypedDataSignature { + required string address = 1; // Base58Check TRON address that signed + required bytes signature = 2; // 65-byte signature (r + s + v) +} diff --git a/messages.proto b/messages.proto index e9f37e02..4dc4393a 100644 --- a/messages.proto +++ b/messages.proto @@ -224,6 +224,11 @@ enum MessageType { MessageType_TronAddress = 1401 [ (wire_out) = true ]; MessageType_TronSignTx = 1402 [ (wire_in) = true ]; MessageType_TronSignedTx = 1403 [ (wire_out) = true ]; + MessageType_TronSignMessage = 1404 [ (wire_in) = true ]; + MessageType_TronMessageSignature = 1405 [ (wire_out) = true ]; + MessageType_TronVerifyMessage = 1406 [ (wire_in) = true ]; + MessageType_TronSignTypedHash = 1407 [ (wire_in) = true ]; + MessageType_TronTypedDataSignature = 1408 [ (wire_out) = true ]; // TON MessageType_TonGetAddress = 1500 [ (wire_in) = true ]; From 20e646ad97b6aed005c8e54625076122f14c9d29 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 28 Apr 2026 19:54:31 -0500 Subject: [PATCH 06/45] feat(ton): add SignMessage Ed25519 message-signing primitive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds TonSignMessage / TonMessageSignature (1504/1505) — basic Ed25519 arbitrary-bytes signing, mirroring SolanaSignMessage's shape. This primitive lacks domain separation by design (raw Ed25519 over message bytes). Firmware should gate it behind the AdvancedMode policy — same fence used for SolanaSignMessage in fsm_msg_solana.h — until a TON Connect ton_proof envelope is added as a separate proto. Reserves IDs 1504-1505 contiguous to existing TON range (1500-1503). --- messages-ton.options | 5 +++++ messages-ton.proto | 28 ++++++++++++++++++++++++++++ messages.proto | 2 ++ 3 files changed, 35 insertions(+) diff --git a/messages-ton.options b/messages-ton.options index 3b046331..91bf283e 100644 --- a/messages-ton.options +++ b/messages-ton.options @@ -8,3 +8,8 @@ TonSignTx.memo max_size:121 TonAddress.address max_size:50 TonAddress.raw_address max_size:70 TonSignedTx.signature max_size:64 +TonSignMessage.address_n max_count:8 +TonSignMessage.coin_name max_size:21 +TonSignMessage.message max_size:1024 +TonMessageSignature.public_key max_size:32 +TonMessageSignature.signature max_size:64 diff --git a/messages-ton.proto b/messages-ton.proto index 6813fbd4..09e44eba 100644 --- a/messages-ton.proto +++ b/messages-ton.proto @@ -70,3 +70,31 @@ message TonSignTx { message TonSignedTx { optional bytes signature = 1; // Ed25519 signature (64 bytes) } + +////////////////////////////////////// +// TON: Message signing // +////////////////////////////////////// + +/** + * Request: Ask device to sign an arbitrary message with Ed25519 + * Note: lacks domain separation by default. Firmware policy may gate + * this behind AdvancedMode (matching the SolanaSignMessage pattern) + * until a TON Connect-style envelope (ton_proof) is implemented. + * @next TonMessageSignature + * @next Failure + */ +message TonSignMessage { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional string coin_name = 2 [default='Ton']; + optional bytes message = 3; // Message bytes to sign + optional bool show_display = 4; // Show message on device display +} + +/** + * Response: Ed25519 signature and public key for the signed message + * @prev TonSignMessage + */ +message TonMessageSignature { + optional bytes public_key = 1; // 32-byte Ed25519 public key + optional bytes signature = 2; // 64-byte Ed25519 signature +} diff --git a/messages.proto b/messages.proto index 4dc4393a..fc5ffdd0 100644 --- a/messages.proto +++ b/messages.proto @@ -235,6 +235,8 @@ enum MessageType { MessageType_TonAddress = 1501 [ (wire_out) = true ]; MessageType_TonSignTx = 1502 [ (wire_in) = true ]; MessageType_TonSignedTx = 1503 [ (wire_out) = true ]; + MessageType_TonSignMessage = 1504 [ (wire_in) = true ]; + MessageType_TonMessageSignature = 1505 [ (wire_out) = true ]; } //////////////////// From c0ef415fb77517d666200c2ffca381130d1b1733 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 28 Apr 2026 19:55:01 -0500 Subject: [PATCH 07/45] feat(solana): add SignOffchainMessage with domain-separated envelope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds SolanaSignOffchainMessage / SolanaOffchainMessageSignature (756/757) implementing the Solana off-chain message spec: '\xff' || 'solana offchain' || version || format || length || message The '\xff' lead byte is invalid as a Solana transaction prefix, providing the domain separation that plain SolanaSignMessage (754/755) lacks. With this primitive, firmware can drop the AdvancedMode policy gate currently required for SolanaSignMessage (fsm_msg_solana.h:461-472) for ASCII/UTF8 off-chain messages, since the envelope makes transaction-shaped attacks impossible. message_format values per spec: 0 = Restricted ASCII (max 1212 bytes) — display-renderable 1 = UTF-8 limited (max 1212 bytes) — display-renderable with care 2 = UTF-8 extended (max 65515) — blind-sign only Reserves IDs 756-757 contiguous to existing Solana range (750-755). Bumped message max_size to 1212 to match the spec ceiling for formats 0/1. --- messages-solana.options | 5 +++++ messages-solana.proto | 37 +++++++++++++++++++++++++++++++++++++ messages.proto | 2 ++ 3 files changed, 44 insertions(+) diff --git a/messages-solana.options b/messages-solana.options index 411233ab..ee812da1 100644 --- a/messages-solana.options +++ b/messages-solana.options @@ -13,3 +13,8 @@ SolanaSignMessage.coin_name max_size:21 SolanaSignMessage.message max_size:1024 SolanaMessageSignature.public_key max_size:32 SolanaMessageSignature.signature max_size:64 +SolanaSignOffchainMessage.address_n max_count:8 +SolanaSignOffchainMessage.coin_name max_size:21 +SolanaSignOffchainMessage.message max_size:1212 +SolanaOffchainMessageSignature.public_key max_size:32 +SolanaOffchainMessageSignature.signature max_size:64 diff --git a/messages-solana.proto b/messages-solana.proto index e0c341e7..f0ca4484 100644 --- a/messages-solana.proto +++ b/messages-solana.proto @@ -79,3 +79,40 @@ message SolanaMessageSignature { optional bytes public_key = 1; // 32-byte Ed25519 public key optional bytes signature = 2; // 64-byte Ed25519 signature } + +/** + * Request: Ask device to sign a Solana off-chain message with domain separation + * + * Per the Solana off-chain message spec, the device signs over the envelope: + * "\xff" || "solana offchain" || || + * || || + * + * The "\xff" lead byte is invalid as a Solana transaction prefix, providing + * domain separation that plain SolanaSignMessage lacks. Firmware constructs + * the envelope from the components below; host supplies version/format/message. + * + * Format values: + * 0 = Restricted ASCII (printable, max 1212 bytes) — renderable on display + * 1 = UTF-8 (max 1212 bytes) — renderable, may need policy gate + * 2 = UTF-8 (max 65515 bytes) — Ledger-only mode, blind-sign + * + * @next SolanaOffchainMessageSignature + * @next Failure + */ +message SolanaSignOffchainMessage { + repeated uint32 address_n = 1; // BIP-32/BIP-44 path to signing key + optional string coin_name = 2 [default = "Solana"]; + optional uint32 version = 3 [default = 0]; // Off-chain message spec version (0 = current) + optional uint32 message_format = 4; // 0=ASCII, 1=UTF8 limited, 2=UTF8 extended + optional bytes message = 5; // Raw message payload (firmware wraps with envelope) + optional bool show_display = 6; // Show message on device display +} + +/** + * Response: Ed25519 signature over the off-chain message envelope + * @prev SolanaSignOffchainMessage + */ +message SolanaOffchainMessageSignature { + optional bytes public_key = 1; // 32-byte Ed25519 public key + optional bytes signature = 2; // 64-byte Ed25519 signature over the envelope +} diff --git a/messages.proto b/messages.proto index fc5ffdd0..ca35898c 100644 --- a/messages.proto +++ b/messages.proto @@ -140,6 +140,8 @@ enum MessageType { MessageType_SolanaSignedTx = 753 [ (wire_out) = true ]; MessageType_SolanaSignMessage = 754 [ (wire_in) = true ]; MessageType_SolanaMessageSignature = 755 [ (wire_out) = true ]; + MessageType_SolanaSignOffchainMessage = 756 [ (wire_in) = true ]; + MessageType_SolanaOffchainMessageSignature = 757 [ (wire_out) = true ]; // Binance MessageType_BinanceGetAddress = 800 [ (wire_in) = true ]; From 872151217c295bad1ccf707af12376b6b60b5e57 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 28 Apr 2026 20:55:57 -0500 Subject: [PATCH 08/45] feat(zcash): add seed_fingerprint binding to FVK / address / sign messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ZIP-32 §6.1 seed fingerprint: SeedFingerprint := BLAKE2b-256("Zcash_HD_Seed_FP", seed) A 32-byte stable identity of the device's seed. Adds optional bytes seed_fingerprint fields across the existing zcash messages so hosts and devices can bind FVKs, addresses, and signing sessions to a specific seed identity. Four new fields, all optional, fully backward compatible: ZcashOrchardFVK.seed_fingerprint (4) Returned alongside (ak, nk, rivk). Lets a host pin an FVK to this device's seed. ZcashAddress.seed_fingerprint (2) Returned alongside the confirmed UA after on-device verification. Lets a host record "this address is on this device's seed." ZcashSignPCZT.expected_seed_fingerprint (31) Sent by host. If present, device checks against its own fingerprint and rejects with Failure on mismatch before signing. Mirrors Keystone3's PCZT zip32_derivation seed_fingerprint check at the session level (one tx = one seed, no per-action duplication needed for our flow). ZcashDisplayAddress.expected_seed_fingerprint (7) Sent by host. Same rejection semantics as above before displaying. Matching nanopb max_size:32 entries added to messages-zcash.options. No existing fields modified. Devices and hosts that don't populate the new fields continue to work unchanged. --- messages-zcash.options | 4 ++++ messages-zcash.proto | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/messages-zcash.options b/messages-zcash.options index a80f1ec3..c32611d3 100644 --- a/messages-zcash.options +++ b/messages-zcash.options @@ -5,6 +5,7 @@ ZcashSignPCZT.transparent_digest max_size:32 ZcashSignPCZT.sapling_digest max_size:32 ZcashSignPCZT.orchard_digest max_size:32 ZcashSignPCZT.orchard_anchor max_size:32 +ZcashSignPCZT.expected_seed_fingerprint max_size:32 ZcashPCZTAction.alpha max_size:32 ZcashPCZTAction.sighash max_size:32 @@ -26,6 +27,7 @@ ZcashGetOrchardFVK.address_n max_count:8 ZcashOrchardFVK.ak max_size:32 ZcashOrchardFVK.nk max_size:32 ZcashOrchardFVK.rivk max_size:32 +ZcashOrchardFVK.seed_fingerprint max_size:32 ZcashTransparentInput.sighash max_size:32 ZcashTransparentInput.address_n max_count:8 @@ -37,5 +39,7 @@ ZcashDisplayAddress.address max_size:128 ZcashDisplayAddress.ak max_size:32 ZcashDisplayAddress.nk max_size:32 ZcashDisplayAddress.rivk max_size:32 +ZcashDisplayAddress.expected_seed_fingerprint max_size:32 ZcashAddress.address max_size:128 +ZcashAddress.seed_fingerprint max_size:32 diff --git a/messages-zcash.proto b/messages-zcash.proto index 4eaf7dd2..0a00b927 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -40,6 +40,10 @@ message ZcashSignPCZT { optional bytes orchard_anchor = 14; // 32-byte orchard anchor // Phase 3: transparent shielding support optional uint32 n_transparent_inputs = 30; // 0 for shielded-only (default), >0 for hybrid shielding tx + // Seed identity binding (ZIP-32 §6.1) + optional bytes expected_seed_fingerprint = 31; // 32-byte BLAKE2b-256("Zcash_HD_Seed_FP", seed). + // If present, device verifies match against its own + // seed fingerprint and rejects with Failure on mismatch. } /** @@ -116,6 +120,9 @@ message ZcashOrchardFVK { optional bytes ak = 1; // 32-byte authorizing key (Pallas point) optional bytes nk = 2; // 32-byte nullifier deriving key optional bytes rivk = 3; // 32-byte commitment randomness key + optional bytes seed_fingerprint = 4; // 32-byte BLAKE2b-256("Zcash_HD_Seed_FP", seed) — ZIP-32 §6.1 + // Stable identity of the device's seed; lets a host pin an FVK + // to a specific seed across sessions. } /** @@ -182,6 +189,9 @@ message ZcashDisplayAddress { optional bytes ak = 4; // 32-byte ak for Orchard FVK verification optional bytes nk = 5; // 32-byte nk for Orchard FVK verification optional bytes rivk = 6; // 32-byte rivk for Orchard FVK verification + optional bytes expected_seed_fingerprint = 7; // 32-byte ZIP-32 §6.1 seed fingerprint. + // If present, device verifies match against its own + // seed fingerprint and rejects with Failure on mismatch. } /** @@ -191,4 +201,7 @@ message ZcashDisplayAddress { */ message ZcashAddress { optional string address = 1; // Confirmed unified address + optional bytes seed_fingerprint = 2; // 32-byte ZIP-32 §6.1 seed fingerprint of the attesting + // device. Returned alongside the confirmed address so a host + // can record that this UA is bound to this device's seed. } From ef80b30a823e18739b2354838be5d2a50b159fad Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 28 Apr 2026 21:54:42 -0500 Subject: [PATCH 09/45] fix(zcash): correct seed_fingerprint formula in proto comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Field comments documented the formula as BLAKE2b-256("Zcash_HD_Seed_FP", seed) but ZIP-32 §6.1 (and the actual conforming implementations in the upstream zip32 Rust crate, keystone3-firmware, and our own firmware) prepend a 1-byte length: BLAKE2b-256("Zcash_HD_Seed_FP", I2LEBSP_8(len(seed)) || seed) A host implementer following the proto comments would compute the wrong fingerprint and have the device reject every signing/display request with "seed fingerprint mismatch." Comment-only change. No wire-format impact. --- messages-zcash.proto | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/messages-zcash.proto b/messages-zcash.proto index 0a00b927..10c13a2a 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -41,7 +41,9 @@ message ZcashSignPCZT { // Phase 3: transparent shielding support optional uint32 n_transparent_inputs = 30; // 0 for shielded-only (default), >0 for hybrid shielding tx // Seed identity binding (ZIP-32 §6.1) - optional bytes expected_seed_fingerprint = 31; // 32-byte BLAKE2b-256("Zcash_HD_Seed_FP", seed). + optional bytes expected_seed_fingerprint = 31; // 32-byte ZIP-32 §6.1 seed fingerprint: + // BLAKE2b-256("Zcash_HD_Seed_FP", + // I2LEBSP_8(len(seed)) || seed) // If present, device verifies match against its own // seed fingerprint and rejects with Failure on mismatch. } @@ -120,7 +122,9 @@ message ZcashOrchardFVK { optional bytes ak = 1; // 32-byte authorizing key (Pallas point) optional bytes nk = 2; // 32-byte nullifier deriving key optional bytes rivk = 3; // 32-byte commitment randomness key - optional bytes seed_fingerprint = 4; // 32-byte BLAKE2b-256("Zcash_HD_Seed_FP", seed) — ZIP-32 §6.1 + optional bytes seed_fingerprint = 4; // 32-byte ZIP-32 §6.1 seed fingerprint: + // BLAKE2b-256("Zcash_HD_Seed_FP", + // I2LEBSP_8(len(seed)) || seed) // Stable identity of the device's seed; lets a host pin an FVK // to a specific seed across sessions. } From 870b8eaf5bcad0e62231bc0947207d6863c0553e Mon Sep 17 00:00:00 2001 From: highlander Date: Thu, 30 Apr 2026 17:12:49 -0500 Subject: [PATCH 10/45] feat(zcash): drop host-supplied UA from ZcashDisplayAddress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove fields 3-6 (address, ak, nk, rivk) from ZcashDisplayAddress. Field numbers are reserved to prevent reuse. The on-device UA derivation (Sinsemilla + SWU hash-to-curve) shipped — FVK-match attestation against a host-built UA is strictly weaker than device-derived display and is no longer supported. What stays: address_n / account / expected_seed_fingerprint. What ZcashAddress returns: address (now device-derived) + seed_fingerprint. --- messages-zcash.options | 4 ---- messages-zcash.proto | 36 ++++++++++++++---------------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/messages-zcash.options b/messages-zcash.options index 9e9f9992..31426964 100644 --- a/messages-zcash.options +++ b/messages-zcash.options @@ -35,10 +35,6 @@ ZcashTransparentInput.address_n max_count:8 ZcashTransparentSig.signature max_size:73 ZcashDisplayAddress.address_n max_count:8 -ZcashDisplayAddress.address max_size:256 -ZcashDisplayAddress.ak max_size:32 -ZcashDisplayAddress.nk max_size:32 -ZcashDisplayAddress.rivk max_size:32 ZcashDisplayAddress.expected_seed_fingerprint max_size:32 ZcashAddress.address max_size:256 diff --git a/messages-zcash.proto b/messages-zcash.proto index 10c13a2a..ac65c1c5 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -161,38 +161,30 @@ message ZcashTransparentSig { } /** - * Request: Display a Zcash unified address on the device screen. + * Request: Display the device-derived Orchard unified address on screen. * - * The host provides the unified address string and the FVK components - * (ak, nk, rivk) used to derive it. The device independently derives - * the FVK from the seed and verifies it matches the provided components - * before displaying the address with a QR code. + * The device derives the Orchard-only Unified Address (Sinsemilla + SWU + * hash-to-curve, default diversifier index 0) from its own seed at the + * requested account and shows it on the OLED with a QR code. What appears + * on screen is bound to this device — there is no host-supplied address + * to validate. * - * VERIFICATION SCOPE: The device only verifies that the Orchard FVK - * (ak, nk, rivk) in this request matches what it derives from the seed - * at the given account. A Unified Address may bundle receivers from - * multiple pools (transparent, Sapling, Orchard). The device CANNOT - * verify non-Orchard receivers — the guarantee is limited to: - * "This UA contains an Orchard receiver from this account." - * It does NOT guarantee that transparent or Sapling receivers (if - * present) are also controlled by this device. + * Either account or a complete address_n path (m/32'/133'/account', all + * hardened) is REQUIRED. The device rejects requests that omit both. * - * Full on-device UA derivation (Sinsemilla + SWU hash-to-curve) - * is planned for a future firmware release. - * - * Either account or a complete address_n path is REQUIRED. - * The device will reject requests that omit both. + * Fields 3–6 (host-supplied address, ak, nk, rivk) were removed when the + * device gained on-device UA derivation: FVK-match attestation against a + * host-built UA is strictly weaker than device-derived display and was + * dropped. Field numbers are reserved to prevent reuse. * * @next ZcashAddress * @next Failure */ message ZcashDisplayAddress { + reserved 3, 4, 5, 6; + reserved "address", "ak", "nk", "rivk"; repeated uint32 address_n = 1; // ZIP-32 path [32', 133', account'] — required if account omitted optional uint32 account = 2; // Account index — required if address_n omitted - optional string address = 3; // Host-computed unified address ("u1...") - optional bytes ak = 4; // 32-byte ak for Orchard FVK verification - optional bytes nk = 5; // 32-byte nk for Orchard FVK verification - optional bytes rivk = 6; // 32-byte rivk for Orchard FVK verification optional bytes expected_seed_fingerprint = 7; // 32-byte ZIP-32 §6.1 seed fingerprint. // If present, device verifies match against its own // seed fingerprint and rejects with Failure on mismatch. From 8f80bcdcd04e91ea80bb04d9c5f5328081938299 Mon Sep 17 00:00:00 2001 From: highlander Date: Fri, 15 May 2026 14:10:52 -0300 Subject: [PATCH 11/45] feat(ripple): add memo field to RippleSignTx for THORChain swap routing Add optional string memo field (field 7) to RippleSignTx protobuf message. This enables THORChain swap routing memos and other arbitrary memo data to be included in XRP transactions signed by the device. --- lib/messages-ripple_pb.d.ts | 179 +++++ lib/messages-ripple_pb.js | 1425 +++++++++++++++++++++++++++++++++++ messages-ripple.proto | 1 + 3 files changed, 1605 insertions(+) create mode 100644 lib/messages-ripple_pb.d.ts create mode 100644 lib/messages-ripple_pb.js diff --git a/lib/messages-ripple_pb.d.ts b/lib/messages-ripple_pb.d.ts new file mode 100644 index 00000000..3c47e1ca --- /dev/null +++ b/lib/messages-ripple_pb.d.ts @@ -0,0 +1,179 @@ +// package: +// file: messages-ripple.proto + +import * as jspb from "google-protobuf"; + +export class RippleGetAddress extends jspb.Message { + clearAddressNList(): void; + getAddressNList(): Array; + setAddressNList(value: Array): void; + addAddressN(value: number, index?: number): number; + + hasShowDisplay(): boolean; + clearShowDisplay(): void; + getShowDisplay(): boolean | undefined; + setShowDisplay(value: boolean): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): RippleGetAddress.AsObject; + static toObject(includeInstance: boolean, msg: RippleGetAddress): RippleGetAddress.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: RippleGetAddress, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): RippleGetAddress; + static deserializeBinaryFromReader(message: RippleGetAddress, reader: jspb.BinaryReader): RippleGetAddress; +} + +export namespace RippleGetAddress { + export type AsObject = { + addressNList: Array, + showDisplay?: boolean, + } +} + +export class RippleAddress extends jspb.Message { + hasAddress(): boolean; + clearAddress(): void; + getAddress(): string | undefined; + setAddress(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): RippleAddress.AsObject; + static toObject(includeInstance: boolean, msg: RippleAddress): RippleAddress.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: RippleAddress, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): RippleAddress; + static deserializeBinaryFromReader(message: RippleAddress, reader: jspb.BinaryReader): RippleAddress; +} + +export namespace RippleAddress { + export type AsObject = { + address?: string, + } +} + +export class RippleSignTx extends jspb.Message { + clearAddressNList(): void; + getAddressNList(): Array; + setAddressNList(value: Array): void; + addAddressN(value: number, index?: number): number; + + hasFee(): boolean; + clearFee(): void; + getFee(): number | undefined; + setFee(value: number): void; + + hasFlags(): boolean; + clearFlags(): void; + getFlags(): number | undefined; + setFlags(value: number): void; + + hasSequence(): boolean; + clearSequence(): void; + getSequence(): number | undefined; + setSequence(value: number): void; + + hasLastLedgerSequence(): boolean; + clearLastLedgerSequence(): void; + getLastLedgerSequence(): number | undefined; + setLastLedgerSequence(value: number): void; + + hasPayment(): boolean; + clearPayment(): void; + getPayment(): RipplePayment | undefined; + setPayment(value?: RipplePayment): void; + + hasMemo(): boolean; + clearMemo(): void; + getMemo(): string | undefined; + setMemo(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): RippleSignTx.AsObject; + static toObject(includeInstance: boolean, msg: RippleSignTx): RippleSignTx.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: RippleSignTx, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): RippleSignTx; + static deserializeBinaryFromReader(message: RippleSignTx, reader: jspb.BinaryReader): RippleSignTx; +} + +export namespace RippleSignTx { + export type AsObject = { + addressNList: Array, + fee?: number, + flags?: number, + sequence?: number, + lastLedgerSequence?: number, + payment?: RipplePayment.AsObject, + memo?: string, + } +} + +export class RipplePayment extends jspb.Message { + hasAmount(): boolean; + clearAmount(): void; + getAmount(): number | undefined; + setAmount(value: number): void; + + hasDestination(): boolean; + clearDestination(): void; + getDestination(): string | undefined; + setDestination(value: string): void; + + hasDestinationTag(): boolean; + clearDestinationTag(): void; + getDestinationTag(): number | undefined; + setDestinationTag(value: number): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): RipplePayment.AsObject; + static toObject(includeInstance: boolean, msg: RipplePayment): RipplePayment.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: RipplePayment, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): RipplePayment; + static deserializeBinaryFromReader(message: RipplePayment, reader: jspb.BinaryReader): RipplePayment; +} + +export namespace RipplePayment { + export type AsObject = { + amount?: number, + destination?: string, + destinationTag?: number, + } +} + +export class RippleSignedTx extends jspb.Message { + hasSignature(): boolean; + clearSignature(): void; + getSignature(): Uint8Array | string; + getSignature_asU8(): Uint8Array; + getSignature_asB64(): string; + setSignature(value: Uint8Array | string): void; + + hasSerializedTx(): boolean; + clearSerializedTx(): void; + getSerializedTx(): Uint8Array | string; + getSerializedTx_asU8(): Uint8Array; + getSerializedTx_asB64(): string; + setSerializedTx(value: Uint8Array | string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): RippleSignedTx.AsObject; + static toObject(includeInstance: boolean, msg: RippleSignedTx): RippleSignedTx.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: RippleSignedTx, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): RippleSignedTx; + static deserializeBinaryFromReader(message: RippleSignedTx, reader: jspb.BinaryReader): RippleSignedTx; +} + +export namespace RippleSignedTx { + export type AsObject = { + signature: Uint8Array | string, + serializedTx: Uint8Array | string, + } +} + diff --git a/lib/messages-ripple_pb.js b/lib/messages-ripple_pb.js new file mode 100644 index 00000000..296100ad --- /dev/null +++ b/lib/messages-ripple_pb.js @@ -0,0 +1,1425 @@ +// source: messages-ripple.proto +/** + * @fileoverview + * @enhanceable + * @suppress {missingRequire} reports error on implicit type usages. + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = (function() { + if (this) { return this; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + if (typeof self !== 'undefined') { return self; } + return Function('return this')(); +}.call(null)); + +goog.exportSymbol('proto.RippleAddress', null, global); +goog.exportSymbol('proto.RippleGetAddress', null, global); +goog.exportSymbol('proto.RipplePayment', null, global); +goog.exportSymbol('proto.RippleSignTx', null, global); +goog.exportSymbol('proto.RippleSignedTx', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.RippleGetAddress = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.RippleGetAddress.repeatedFields_, null); +}; +goog.inherits(proto.RippleGetAddress, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.RippleGetAddress.displayName = 'proto.RippleGetAddress'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.RippleAddress = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.RippleAddress, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.RippleAddress.displayName = 'proto.RippleAddress'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.RippleSignTx = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.RippleSignTx.repeatedFields_, null); +}; +goog.inherits(proto.RippleSignTx, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.RippleSignTx.displayName = 'proto.RippleSignTx'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.RipplePayment = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.RipplePayment, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.RipplePayment.displayName = 'proto.RipplePayment'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.RippleSignedTx = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.RippleSignedTx, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.RippleSignedTx.displayName = 'proto.RippleSignedTx'; +} + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.RippleGetAddress.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.RippleGetAddress.prototype.toObject = function(opt_includeInstance) { + return proto.RippleGetAddress.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.RippleGetAddress} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RippleGetAddress.toObject = function(includeInstance, msg) { + var f, obj = { + addressNList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f, + showDisplay: (f = jspb.Message.getBooleanField(msg, 2)) == null ? undefined : f + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.RippleGetAddress} + */ +proto.RippleGetAddress.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.RippleGetAddress; + return proto.RippleGetAddress.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.RippleGetAddress} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.RippleGetAddress} + */ +proto.RippleGetAddress.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var values = /** @type {!Array} */ (reader.isDelimited() ? reader.readPackedUint32() : [reader.readUint32()]); + for (var i = 0; i < values.length; i++) { + msg.addAddressN(values[i]); + } + break; + case 2: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setShowDisplay(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.RippleGetAddress.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.RippleGetAddress.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.RippleGetAddress} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RippleGetAddress.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getAddressNList(); + if (f.length > 0) { + writer.writeRepeatedUint32( + 1, + f + ); + } + f = /** @type {boolean} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeBool( + 2, + f + ); + } +}; + + +/** + * repeated uint32 address_n = 1; + * @return {!Array} + */ +proto.RippleGetAddress.prototype.getAddressNList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.RippleGetAddress} returns this + */ +proto.RippleGetAddress.prototype.setAddressNList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {number} value + * @param {number=} opt_index + * @return {!proto.RippleGetAddress} returns this + */ +proto.RippleGetAddress.prototype.addAddressN = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.RippleGetAddress} returns this + */ +proto.RippleGetAddress.prototype.clearAddressNList = function() { + return this.setAddressNList([]); +}; + + +/** + * optional bool show_display = 2; + * @return {boolean} + */ +proto.RippleGetAddress.prototype.getShowDisplay = function() { + return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false)); +}; + + +/** + * @param {boolean} value + * @return {!proto.RippleGetAddress} returns this + */ +proto.RippleGetAddress.prototype.setShowDisplay = function(value) { + return jspb.Message.setField(this, 2, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RippleGetAddress} returns this + */ +proto.RippleGetAddress.prototype.clearShowDisplay = function() { + return jspb.Message.setField(this, 2, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleGetAddress.prototype.hasShowDisplay = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.RippleAddress.prototype.toObject = function(opt_includeInstance) { + return proto.RippleAddress.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.RippleAddress} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RippleAddress.toObject = function(includeInstance, msg) { + var f, obj = { + address: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.RippleAddress} + */ +proto.RippleAddress.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.RippleAddress; + return proto.RippleAddress.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.RippleAddress} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.RippleAddress} + */ +proto.RippleAddress.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setAddress(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.RippleAddress.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.RippleAddress.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.RippleAddress} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RippleAddress.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = /** @type {string} */ (jspb.Message.getField(message, 1)); + if (f != null) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string address = 1; + * @return {string} + */ +proto.RippleAddress.prototype.getAddress = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.RippleAddress} returns this + */ +proto.RippleAddress.prototype.setAddress = function(value) { + return jspb.Message.setField(this, 1, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RippleAddress} returns this + */ +proto.RippleAddress.prototype.clearAddress = function() { + return jspb.Message.setField(this, 1, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleAddress.prototype.hasAddress = function() { + return jspb.Message.getField(this, 1) != null; +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.RippleSignTx.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.RippleSignTx.prototype.toObject = function(opt_includeInstance) { + return proto.RippleSignTx.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.RippleSignTx} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RippleSignTx.toObject = function(includeInstance, msg) { + var f, obj = { + addressNList: (f = jspb.Message.getRepeatedField(msg, 1)) == null ? undefined : f, + fee: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f, + flags: (f = jspb.Message.getField(msg, 3)) == null ? undefined : f, + sequence: (f = jspb.Message.getField(msg, 4)) == null ? undefined : f, + lastLedgerSequence: (f = jspb.Message.getField(msg, 5)) == null ? undefined : f, + payment: (f = msg.getPayment()) && proto.RipplePayment.toObject(includeInstance, f), + memo: (f = jspb.Message.getField(msg, 7)) == null ? undefined : f + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.RippleSignTx} + */ +proto.RippleSignTx.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.RippleSignTx; + return proto.RippleSignTx.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.RippleSignTx} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.RippleSignTx} + */ +proto.RippleSignTx.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var values = /** @type {!Array} */ (reader.isDelimited() ? reader.readPackedUint32() : [reader.readUint32()]); + for (var i = 0; i < values.length; i++) { + msg.addAddressN(values[i]); + } + break; + case 2: + var value = /** @type {number} */ (reader.readUint64()); + msg.setFee(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setFlags(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint32()); + msg.setSequence(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint32()); + msg.setLastLedgerSequence(value); + break; + case 6: + var value = new proto.RipplePayment; + reader.readMessage(value,proto.RipplePayment.deserializeBinaryFromReader); + msg.setPayment(value); + break; + case 7: + var value = /** @type {string} */ (reader.readString()); + msg.setMemo(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.RippleSignTx.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.RippleSignTx.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.RippleSignTx} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RippleSignTx.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getAddressNList(); + if (f.length > 0) { + writer.writeRepeatedUint32( + 1, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeUint64( + 2, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 3)); + if (f != null) { + writer.writeUint32( + 3, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 4)); + if (f != null) { + writer.writeUint32( + 4, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 5)); + if (f != null) { + writer.writeUint32( + 5, + f + ); + } + f = message.getPayment(); + if (f != null) { + writer.writeMessage( + 6, + f, + proto.RipplePayment.serializeBinaryToWriter + ); + } + f = /** @type {string} */ (jspb.Message.getField(message, 7)); + if (f != null) { + writer.writeString( + 7, + f + ); + } +}; + + +/** + * repeated uint32 address_n = 1; + * @return {!Array} + */ +proto.RippleSignTx.prototype.getAddressNList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.setAddressNList = function(value) { + return jspb.Message.setField(this, 1, value || []); +}; + + +/** + * @param {number} value + * @param {number=} opt_index + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.addAddressN = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 1, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.clearAddressNList = function() { + return this.setAddressNList([]); +}; + + +/** + * optional uint64 fee = 2; + * @return {number} + */ +proto.RippleSignTx.prototype.getFee = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.setFee = function(value) { + return jspb.Message.setField(this, 2, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.clearFee = function() { + return jspb.Message.setField(this, 2, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleSignTx.prototype.hasFee = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional uint32 flags = 3; + * @return {number} + */ +proto.RippleSignTx.prototype.getFlags = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.setFlags = function(value) { + return jspb.Message.setField(this, 3, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.clearFlags = function() { + return jspb.Message.setField(this, 3, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleSignTx.prototype.hasFlags = function() { + return jspb.Message.getField(this, 3) != null; +}; + + +/** + * optional uint32 sequence = 4; + * @return {number} + */ +proto.RippleSignTx.prototype.getSequence = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.setSequence = function(value) { + return jspb.Message.setField(this, 4, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.clearSequence = function() { + return jspb.Message.setField(this, 4, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleSignTx.prototype.hasSequence = function() { + return jspb.Message.getField(this, 4) != null; +}; + + +/** + * optional uint32 last_ledger_sequence = 5; + * @return {number} + */ +proto.RippleSignTx.prototype.getLastLedgerSequence = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.setLastLedgerSequence = function(value) { + return jspb.Message.setField(this, 5, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.clearLastLedgerSequence = function() { + return jspb.Message.setField(this, 5, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleSignTx.prototype.hasLastLedgerSequence = function() { + return jspb.Message.getField(this, 5) != null; +}; + + +/** + * optional RipplePayment payment = 6; + * @return {?proto.RipplePayment} + */ +proto.RippleSignTx.prototype.getPayment = function() { + return /** @type{?proto.RipplePayment} */ ( + jspb.Message.getWrapperField(this, proto.RipplePayment, 6)); +}; + + +/** + * @param {?proto.RipplePayment|undefined} value + * @return {!proto.RippleSignTx} returns this +*/ +proto.RippleSignTx.prototype.setPayment = function(value) { + return jspb.Message.setWrapperField(this, 6, value); +}; + + +/** + * Clears the message field making it undefined. + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.clearPayment = function() { + return this.setPayment(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleSignTx.prototype.hasPayment = function() { + return jspb.Message.getField(this, 6) != null; +}; + + +/** + * optional string memo = 7; + * @return {string} + */ +proto.RippleSignTx.prototype.getMemo = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, "")); +}; + + +/** + * @param {string} value + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.setMemo = function(value) { + return jspb.Message.setField(this, 7, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RippleSignTx} returns this + */ +proto.RippleSignTx.prototype.clearMemo = function() { + return jspb.Message.setField(this, 7, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleSignTx.prototype.hasMemo = function() { + return jspb.Message.getField(this, 7) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.RipplePayment.prototype.toObject = function(opt_includeInstance) { + return proto.RipplePayment.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.RipplePayment} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RipplePayment.toObject = function(includeInstance, msg) { + var f, obj = { + amount: (f = jspb.Message.getField(msg, 1)) == null ? undefined : f, + destination: (f = jspb.Message.getField(msg, 2)) == null ? undefined : f, + destinationTag: (f = jspb.Message.getField(msg, 3)) == null ? undefined : f + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.RipplePayment} + */ +proto.RipplePayment.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.RipplePayment; + return proto.RipplePayment.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.RipplePayment} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.RipplePayment} + */ +proto.RipplePayment.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint64()); + msg.setAmount(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setDestination(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint32()); + msg.setDestinationTag(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.RipplePayment.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.RipplePayment.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.RipplePayment} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RipplePayment.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = /** @type {number} */ (jspb.Message.getField(message, 1)); + if (f != null) { + writer.writeUint64( + 1, + f + ); + } + f = /** @type {string} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeString( + 2, + f + ); + } + f = /** @type {number} */ (jspb.Message.getField(message, 3)); + if (f != null) { + writer.writeUint32( + 3, + f + ); + } +}; + + +/** + * optional uint64 amount = 1; + * @return {number} + */ +proto.RipplePayment.prototype.getAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.RipplePayment} returns this + */ +proto.RipplePayment.prototype.setAmount = function(value) { + return jspb.Message.setField(this, 1, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RipplePayment} returns this + */ +proto.RipplePayment.prototype.clearAmount = function() { + return jspb.Message.setField(this, 1, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RipplePayment.prototype.hasAmount = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional string destination = 2; + * @return {string} + */ +proto.RipplePayment.prototype.getDestination = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.RipplePayment} returns this + */ +proto.RipplePayment.prototype.setDestination = function(value) { + return jspb.Message.setField(this, 2, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RipplePayment} returns this + */ +proto.RipplePayment.prototype.clearDestination = function() { + return jspb.Message.setField(this, 2, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RipplePayment.prototype.hasDestination = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional uint32 destination_tag = 3; + * @return {number} + */ +proto.RipplePayment.prototype.getDestinationTag = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.RipplePayment} returns this + */ +proto.RipplePayment.prototype.setDestinationTag = function(value) { + return jspb.Message.setField(this, 3, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RipplePayment} returns this + */ +proto.RipplePayment.prototype.clearDestinationTag = function() { + return jspb.Message.setField(this, 3, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RipplePayment.prototype.hasDestinationTag = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.RippleSignedTx.prototype.toObject = function(opt_includeInstance) { + return proto.RippleSignedTx.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.RippleSignedTx} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RippleSignedTx.toObject = function(includeInstance, msg) { + var f, obj = { + signature: msg.getSignature_asB64(), + serializedTx: msg.getSerializedTx_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.RippleSignedTx} + */ +proto.RippleSignedTx.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.RippleSignedTx; + return proto.RippleSignedTx.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.RippleSignedTx} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.RippleSignedTx} + */ +proto.RippleSignedTx.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSerializedTx(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.RippleSignedTx.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.RippleSignedTx.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.RippleSignedTx} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.RippleSignedTx.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 1)); + if (f != null) { + writer.writeBytes( + 1, + f + ); + } + f = /** @type {!(string|Uint8Array)} */ (jspb.Message.getField(message, 2)); + if (f != null) { + writer.writeBytes( + 2, + f + ); + } +}; + + +/** + * optional bytes signature = 1; + * @return {!(string|Uint8Array)} + */ +proto.RippleSignedTx.prototype.getSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes signature = 1; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +proto.RippleSignedTx.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +proto.RippleSignedTx.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.RippleSignedTx} returns this + */ +proto.RippleSignedTx.prototype.setSignature = function(value) { + return jspb.Message.setField(this, 1, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RippleSignedTx} returns this + */ +proto.RippleSignedTx.prototype.clearSignature = function() { + return jspb.Message.setField(this, 1, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleSignedTx.prototype.hasSignature = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional bytes serialized_tx = 2; + * @return {!(string|Uint8Array)} + */ +proto.RippleSignedTx.prototype.getSerializedTx = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes serialized_tx = 2; + * This is a type-conversion wrapper around `getSerializedTx()` + * @return {string} + */ +proto.RippleSignedTx.prototype.getSerializedTx_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSerializedTx())); +}; + + +/** + * optional bytes serialized_tx = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSerializedTx()` + * @return {!Uint8Array} + */ +proto.RippleSignedTx.prototype.getSerializedTx_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSerializedTx())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.RippleSignedTx} returns this + */ +proto.RippleSignedTx.prototype.setSerializedTx = function(value) { + return jspb.Message.setField(this, 2, value); +}; + + +/** + * Clears the field making it undefined. + * @return {!proto.RippleSignedTx} returns this + */ +proto.RippleSignedTx.prototype.clearSerializedTx = function() { + return jspb.Message.setField(this, 2, undefined); +}; + + +/** + * Returns whether this field is set. + * @return {boolean} + */ +proto.RippleSignedTx.prototype.hasSerializedTx = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +goog.object.extend(exports, proto); diff --git a/messages-ripple.proto b/messages-ripple.proto index 0fc012d2..bf526ef7 100644 --- a/messages-ripple.proto +++ b/messages-ripple.proto @@ -34,6 +34,7 @@ message RippleSignTx { optional uint32 sequence = 4; // transaction sequence number optional uint32 last_ledger_sequence = 5; // see https://developers.ripple.com/reliable-transaction-submission.html#lastledgersequence optional RipplePayment payment = 6; // Payment transaction type + optional string memo = 7; // transaction memo (e.g. THORChain swap routing memo) } /** From f2b32d8a26aa311e19aebdde5659a10b23cf0765 Mon Sep 17 00:00:00 2001 From: highlander Date: Sat, 16 May 2026 18:00:52 -0300 Subject: [PATCH 12/45] feat(hive): add Hive blockchain message definitions Adds messages-hive.proto with HiveGetPublicKey, HivePublicKey, HiveSignTx, and HiveSignedTx. Assigns message type IDs 1600-1603 in messages.proto. Co-Authored-By: Claude Sonnet 4.6 --- messages-hive.proto | 53 +++++++++++++++++++++++++++++++++++++++++++++ messages.proto | 6 +++++ 2 files changed, 59 insertions(+) create mode 100644 messages-hive.proto diff --git a/messages-hive.proto b/messages-hive.proto new file mode 100644 index 00000000..b4d4e545 --- /dev/null +++ b/messages-hive.proto @@ -0,0 +1,53 @@ +syntax = "proto2"; + +option java_package = "com.shapeshift.keepkey.lib.protobuf"; +option java_outer_classname = "KeepKeyMessageHive"; + +/** + * Request: Ask device for Hive public key at given BIP-32 path + * @start + * @next HivePublicKey + * @next Failure + */ +message HiveGetPublicKey { + repeated uint32 address_n = 1; // BIP-32 path, e.g. m/44'/1275'/0'/0/0 + optional bool show_display = 2; // show on device before returning +} + +/** + * Response: Hive public key + * @end + */ +message HivePublicKey { + optional string public_key = 1; // STM-prefixed base58 public key + optional bytes raw_public_key = 2; // 33-byte compressed public key +} + +/** + * Request: Sign a Hive transfer transaction + * @start + * @next HiveSignedTx + * @next Failure + */ +message HiveSignTx { + repeated uint32 address_n = 1; // BIP-32 path + optional bytes chain_id = 2; // 32-byte chain id (mainnet = beeab0de...) + optional uint32 ref_block_num = 3; // reference block number (uint16) + optional uint32 ref_block_prefix = 4; // reference block prefix (uint32) + optional uint32 expiration = 5; // expiration Unix timestamp (uint32) + optional string from = 6; // sender account name + optional string to = 7; // recipient account name + optional uint64 amount = 8; // amount in smallest unit (e.g. milliHIVE = 1/1000 HIVE) + optional uint32 decimals = 9; // token decimal places (3 for HIVE/HBD) + optional string asset_symbol = 10; // "HIVE" or "HBD" + optional string memo = 11; // optional memo +} + +/** + * Response: Signature for the Hive transaction + * @end + */ +message HiveSignedTx { + optional bytes signature = 1; // 65-byte secp256k1 signature (recoverable) + optional bytes serialized_tx = 2; // full serialized transaction bytes +} diff --git a/messages.proto b/messages.proto index ca35898c..3166ed4e 100644 --- a/messages.proto +++ b/messages.proto @@ -239,6 +239,12 @@ enum MessageType { MessageType_TonSignedTx = 1503 [ (wire_out) = true ]; MessageType_TonSignMessage = 1504 [ (wire_in) = true ]; MessageType_TonMessageSignature = 1505 [ (wire_out) = true ]; + + // Hive + MessageType_HiveGetPublicKey = 1600 [ (wire_in) = true ]; + MessageType_HivePublicKey = 1601 [ (wire_out) = true ]; + MessageType_HiveSignTx = 1602 [ (wire_in) = true ]; + MessageType_HiveSignedTx = 1603 [ (wire_out) = true ]; } //////////////////// From 922b6946e5b50e3662e787811d96c5d238786289 Mon Sep 17 00:00:00 2001 From: highlander Date: Wed, 20 May 2026 17:05:15 -0300 Subject: [PATCH 13/45] feat(zcash): add clear-signing protocol fields --- messages-zcash.options | 8 ++++- messages-zcash.proto | 70 +++++++++++++++++++++++++++++++++--------- messages.proto | 4 ++- 3 files changed, 65 insertions(+), 17 deletions(-) diff --git a/messages-zcash.options b/messages-zcash.options index 31426964..2ebcb224 100644 --- a/messages-zcash.options +++ b/messages-zcash.options @@ -18,6 +18,8 @@ ZcashPCZTAction.enc_memo max_size:512 ZcashPCZTAction.enc_noncompact max_size:612 ZcashPCZTAction.rk max_size:32 ZcashPCZTAction.out_ciphertext max_size:80 +ZcashPCZTAction.recipient max_size:43 +ZcashPCZTAction.rseed max_size:32 ZcashSignedPCZT.signatures max_count:64 max_size:64 ZcashSignedPCZT.txid max_size:32 @@ -29,10 +31,14 @@ ZcashOrchardFVK.nk max_size:32 ZcashOrchardFVK.rivk max_size:32 ZcashOrchardFVK.seed_fingerprint max_size:32 +ZcashTransparentOutput.script_pubkey max_size:128 + ZcashTransparentInput.sighash max_size:32 ZcashTransparentInput.address_n max_count:8 +ZcashTransparentInput.prevout_txid max_size:32 +ZcashTransparentInput.script_pubkey max_size:128 -ZcashTransparentSig.signature max_size:73 +ZcashTransparentSigned.signatures max_count:8 max_size:73 ZcashDisplayAddress.address_n max_count:8 ZcashDisplayAddress.expected_seed_fingerprint max_size:32 diff --git a/messages-zcash.proto b/messages-zcash.proto index ac65c1c5..c9659dd3 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -18,6 +18,7 @@ option java_outer_classname = "KeepKeyMessageZcash"; * The device derives spend authorization keys, computes the sighash, * and returns RedPallas signatures for each Orchard action. * + * @next ZcashTransparentAck * @next ZcashPCZTActionAck * @next Failure */ @@ -32,13 +33,19 @@ message ZcashSignPCZT { // Phase 2a: sub-digests for on-device sighash computation optional bytes header_digest = 8; // 32-byte pre-computed header digest optional bytes transparent_digest = 9; // 32-byte transparent digest (or empty) - optional bytes sapling_digest = 10; // 32-byte sapling digest (or empty) + optional bytes sapling_digest = 10; // Reserved for future Sapling support; currently rejected optional bytes orchard_digest = 11; // 32-byte orchard digest // Phase 2b: bundle metadata for orchard digest verification optional uint32 orchard_flags = 12; // Orchard bundle flags byte optional int64 orchard_value_balance = 13; // Orchard value balance (LE i64) optional bytes orchard_anchor = 14; // 32-byte orchard anchor + // Phase 4: plaintext header fields for on-device header digest verification + optional uint32 tx_version = 15; // Transaction version (without overwinter bit) + optional uint32 version_group_id = 16; // Version group ID + optional uint32 lock_time = 17; // Transaction lock time + optional uint32 expiry_height = 18; // Transaction expiry height // Phase 3: transparent shielding support + optional uint32 n_transparent_outputs = 29; // 0 for shielded-only (default) optional uint32 n_transparent_inputs = 30; // 0 for shielded-only (default), >0 for hybrid shielding tx // Seed identity binding (ZIP-32 §6.1) optional bytes expected_seed_fingerprint = 31; // 32-byte ZIP-32 §6.1 seed fingerprint: @@ -72,6 +79,11 @@ message ZcashPCZTAction { optional bytes enc_noncompact = 12; // Remaining encrypted note bytes optional bytes rk = 13; // 32-byte randomized verification key optional bytes out_ciphertext = 14; // 80-byte output ciphertext + // Phase 4: plaintext Orchard output metadata for trusted display. + // Firmware recomputes cmx from recipient/value/rseed and nullifier before + // displaying the receiver/value and before emitting any signature. + optional bytes recipient = 15; // 43-byte Orchard receiver: d || pk_d + optional bytes rseed = 16; // 32-byte output note rseed } /** @@ -129,35 +141,63 @@ message ZcashOrchardFVK { // to a specific seed across sessions. } +/** + * Request: Transparent output data for hybrid transactions. + * Sent before transparent inputs so the device can review standard + * transparent recipients before any signature is emitted. + * + * @next ZcashTransparentAck + * @next ZcashPCZTActionAck + * @next Failure + */ +message ZcashTransparentOutput { + required uint32 index = 1; // Output index within the transaction + optional uint64 amount = 2; // Output value in zatoshis + optional bytes script_pubkey = 3; // Standard P2PKH/P2SH scriptPubKey +} + /** * Request: Transparent input data for hybrid shielding transactions. - * Sent one per transparent input during the transparent signing phase. - * The device ECDSA-signs the per-input sighash with the secp256k1 key - * at the provided BIP44 path. + * Sent after all transparent outputs have been streamed. * - * Flow: after ZcashSignPCZT with n_transparent_inputs > 0, the device - * responds with ZcashPCZTActionAck. For each transparent input, the host - * sends ZcashTransparentInput and receives ZcashTransparentSig. After - * all transparent inputs, the device transitions to the Orchard phase. + * The device stores every input first because ZIP-244 per-input transparent + * sighashes commit to all transparent prevouts, values, scripts, sequences, + * and outputs. Host-provided sighash is legacy and rejected when present. * - * @next ZcashTransparentSig + * @next ZcashTransparentAck + * @next ZcashTransparentSigned * @next Failure */ message ZcashTransparentInput { required uint32 index = 1; // Input index within the transaction - required bytes sighash = 2; // 32-byte per-input sighash (host-computed, ZIP-244) + optional bytes sighash = 2; // Legacy host-computed sighash; rejected when present repeated uint32 address_n = 3; // BIP44 path [44', 133', 0', 0, 0] - optional uint64 amount = 4; // Input value in zatoshis (for display verification) + optional uint64 amount = 4; // Input value in zatoshis + optional bytes prevout_txid = 5; // Previous transaction ID + optional uint32 prevout_index = 6; // Previous output index + optional uint32 sequence = 7; // Input sequence + optional bytes script_pubkey = 8; // Previous output scriptPubKey +} + +/** + * Response: Acknowledgment requesting the next transparent item. + * + * @prev ZcashSignPCZT + * @prev ZcashTransparentOutput + * @prev ZcashTransparentInput + */ +message ZcashTransparentAck { + optional uint32 next_output_index = 1; // Next transparent output index + optional uint32 next_input_index = 2; // Next transparent input index } /** - * Response: ECDSA signature for a transparent input. + * Response: ECDSA signatures for transparent inputs. * * @prev ZcashTransparentInput */ -message ZcashTransparentSig { - required bytes signature = 1; // DER ECDSA signature (72-73 bytes) - optional uint32 next_index = 2; // Next transparent input index, or 0xFF = done +message ZcashTransparentSigned { + repeated bytes signatures = 1; // DER ECDSA signatures, one per transparent input } /** diff --git a/messages.proto b/messages.proto index 3166ed4e..28dbebf7 100644 --- a/messages.proto +++ b/messages.proto @@ -217,9 +217,11 @@ enum MessageType { MessageType_ZcashGetOrchardFVK = 1304 [ (wire_in) = true ]; MessageType_ZcashOrchardFVK = 1305 [ (wire_out) = true ]; MessageType_ZcashTransparentInput = 1306 [ (wire_in) = true ]; - MessageType_ZcashTransparentSig = 1307 [ (wire_out) = true ]; + MessageType_ZcashTransparentSigned = 1307 [ (wire_out) = true ]; MessageType_ZcashDisplayAddress = 1308 [ (wire_in) = true ]; MessageType_ZcashAddress = 1309 [ (wire_out) = true ]; + MessageType_ZcashTransparentOutput = 1310 [ (wire_in) = true ]; + MessageType_ZcashTransparentAck = 1311 [ (wire_out) = true ]; // TRON MessageType_TronGetAddress = 1400 [ (wire_in) = true ]; From c1dea4449be9fd5d5dfd66f4bd2f852d4b394c84 Mon Sep 17 00:00:00 2001 From: highlander Date: Thu, 21 May 2026 13:59:52 -0300 Subject: [PATCH 14/45] feat(thorchain): add denom field to ThorchainMsgSend (field 11) --- messages-thorchain.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/messages-thorchain.proto b/messages-thorchain.proto index acde357a..00183623 100644 --- a/messages-thorchain.proto +++ b/messages-thorchain.proto @@ -60,6 +60,7 @@ message ThorchainMsgSend { optional uint64 amount = 8 [jstype = JS_STRING]; optional OutputAddressType address_type = 9; reserved 10; + optional string denom = 11; // asset denom, e.g. "rune" or IBC denom } message ThorchainMsgDeposit { From 2b25cf7a0ab4829ab8e7b741074dd794a06b2f58 Mon Sep 17 00:00:00 2001 From: Highlander Date: Sun, 24 May 2026 13:49:05 -0300 Subject: [PATCH 15/45] feat(hive): add Hive blockchain protobuf definitions (#31) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * release: device-protocol 7.14.1 * feat(tron): add SignMessage (TIP-191), VerifyMessage, SignTypedHash (TIP-712) Adds proto definitions for TRON message-signing parity: - TronSignMessage / TronMessageSignature (1404/1405) — TIP-191 personal_sign - TronVerifyMessage (1406) — host-asserted signature verification - TronSignTypedHash / TronTypedDataSignature (1407/1408) — TIP-712 hash mode Mirrors the Ethereum personal_sign + EIP-712 hash-mode shape. Firmware implementation will reuse the secp256k1 + keccak256 primitives already present for Ethereum, swapping the message prefix to '\x19TRON Signed Message:\n' for TIP-191 and using '\x19\x01' for TIP-712. Reserves IDs 1404-1408 contiguous to existing TRON range (1400-1403). * feat(ton): add SignMessage Ed25519 message-signing primitive Adds TonSignMessage / TonMessageSignature (1504/1505) — basic Ed25519 arbitrary-bytes signing, mirroring SolanaSignMessage's shape. This primitive lacks domain separation by design (raw Ed25519 over message bytes). Firmware should gate it behind the AdvancedMode policy — same fence used for SolanaSignMessage in fsm_msg_solana.h — until a TON Connect ton_proof envelope is added as a separate proto. Reserves IDs 1504-1505 contiguous to existing TON range (1500-1503). * feat(solana): add SignOffchainMessage with domain-separated envelope Adds SolanaSignOffchainMessage / SolanaOffchainMessageSignature (756/757) implementing the Solana off-chain message spec: '\xff' || 'solana offchain' || version || format || length || message The '\xff' lead byte is invalid as a Solana transaction prefix, providing the domain separation that plain SolanaSignMessage (754/755) lacks. With this primitive, firmware can drop the AdvancedMode policy gate currently required for SolanaSignMessage (fsm_msg_solana.h:461-472) for ASCII/UTF8 off-chain messages, since the envelope makes transaction-shaped attacks impossible. message_format values per spec: 0 = Restricted ASCII (max 1212 bytes) — display-renderable 1 = UTF-8 limited (max 1212 bytes) — display-renderable with care 2 = UTF-8 extended (max 65515) — blind-sign only Reserves IDs 756-757 contiguous to existing Solana range (750-755). Bumped message max_size to 1212 to match the spec ceiling for formats 0/1. * feat(zcash): drop host-supplied UA from ZcashDisplayAddress Remove fields 3-6 (address, ak, nk, rivk) from ZcashDisplayAddress. Field numbers are reserved to prevent reuse. The on-device UA derivation (Sinsemilla + SWU hash-to-curve) shipped — FVK-match attestation against a host-built UA is strictly weaker than device-derived display and is no longer supported. What stays: address_n / account / expected_seed_fingerprint. What ZcashAddress returns: address (now device-derived) + seed_fingerprint. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * docs: update SolanaSignOffchainMessage to reflect 1212 byte limit and drop format 2 Agent-Logs-Url: https://github.com/keepkey/device-protocol/sessions/880cd954-b4b2-4f87-af05-8d715e1e0dc4 Co-authored-by: pastaghost <62026038+pastaghost@users.noreply.github.com> * feat(hive): add Hive blockchain message definitions Adds messages-hive.proto with HiveGetPublicKey, HivePublicKey, HiveSignTx, and HiveSignedTx. Assigns message type IDs 1600-1603 in messages.proto. Co-Authored-By: Claude Sonnet 4.6 * feat(hive): add messages-hive.proto to build, drop broken build:json step build:json used pbjs v0.0.5 which cannot parse proto3 reserved fields (present in zcash, cosmos, ethereum, etc). proto.json is unused by the vault — only messages_pb.js is imported. Build now runs build:js + build:postprocess only. * feat(hive): add HiveGetPublicKeys, HiveSignAccountCreate, HiveSignAccountUpdate + SLIP-0048 paths --------- Co-authored-by: pastaghost <62026038+pastaghost@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 --- messages-hive.options | 46 +++++++++++++ messages-hive.proto | 149 +++++++++++++++++++++++++++++++++++++++++ messages-solana.proto | 5 +- messages-tron.proto | 1 + messages-zcash.options | 4 -- messages-zcash.proto | 36 ++++------ messages.proto | 12 ++++ package-lock.json | 113 ++++++++++++++++++++----------- package.json | 8 +-- yarn.lock | 76 +++++++++++++-------- 10 files changed, 349 insertions(+), 101 deletions(-) create mode 100644 messages-hive.options create mode 100644 messages-hive.proto diff --git a/messages-hive.options b/messages-hive.options new file mode 100644 index 00000000..e878f3e3 --- /dev/null +++ b/messages-hive.options @@ -0,0 +1,46 @@ +HiveGetPublicKey.address_n max_count:8 + +HivePublicKey.public_key max_size:64 +HivePublicKey.raw_public_key max_size:33 + +HiveGetPublicKeys.account_index int_size:IS_32 + +HivePublicKeys.owner_key max_size:64 +HivePublicKeys.active_key max_size:64 +HivePublicKeys.memo_key max_size:64 +HivePublicKeys.posting_key max_size:64 + +HiveSignTx.address_n max_count:8 +HiveSignTx.chain_id max_size:32 +HiveSignTx.from max_size:16 +HiveSignTx.to max_size:16 +HiveSignTx.amount int_size:IS_64 +HiveSignTx.asset_symbol max_size:10 +HiveSignTx.memo max_size:2048 + +HiveSignedTx.signature max_size:65 +HiveSignedTx.serialized_tx max_size:512 + +HiveSignAccountCreate.address_n max_count:8 +HiveSignAccountCreate.chain_id max_size:32 +HiveSignAccountCreate.creator max_size:16 +HiveSignAccountCreate.new_account_name max_size:16 +HiveSignAccountCreate.owner_key max_size:64 +HiveSignAccountCreate.active_key max_size:64 +HiveSignAccountCreate.posting_key max_size:64 +HiveSignAccountCreate.memo_key max_size:64 +HiveSignAccountCreate.fee_amount int_size:IS_64 + +HiveSignedAccountCreate.signature max_size:65 +HiveSignedAccountCreate.serialized_tx max_size:512 + +HiveSignAccountUpdate.address_n max_count:8 +HiveSignAccountUpdate.chain_id max_size:32 +HiveSignAccountUpdate.account max_size:16 +HiveSignAccountUpdate.new_owner_key max_size:64 +HiveSignAccountUpdate.new_active_key max_size:64 +HiveSignAccountUpdate.new_posting_key max_size:64 +HiveSignAccountUpdate.new_memo_key max_size:64 + +HiveSignedAccountUpdate.signature max_size:65 +HiveSignedAccountUpdate.serialized_tx max_size:512 diff --git a/messages-hive.proto b/messages-hive.proto new file mode 100644 index 00000000..7612ec47 --- /dev/null +++ b/messages-hive.proto @@ -0,0 +1,149 @@ +syntax = "proto2"; + +option java_package = "com.shapeshift.keepkey.lib.protobuf"; +option java_outer_classname = "KeepKeyMessageHive"; + +/** + * Request: Ask device for a single Hive public key at a given SLIP-0048 path. + * Path format: m/48'/13'/role'/account'/0' + * role: 0'=owner 1'=active 3'=memo 4'=posting + * @start + * @next HivePublicKey + * @next Failure + */ +message HiveGetPublicKey { + repeated uint32 address_n = 1; // Full SLIP-0048 path (all 5 components hardened) + optional bool show_display = 2; // Confirm on device before returning + optional uint32 role = 3; // 0=owner 1=active 3=memo 4=posting (for display label only) +} + +/** + * Response: Single Hive public key + * @end + */ +message HivePublicKey { + optional string public_key = 1; // STM-prefixed base58check public key + optional bytes raw_public_key = 2; // 33-byte compressed secp256k1 public key +} + +/** + * Request: Ask device for all four Hive role keys in one interaction. + * Derives owner/active/memo/posting keys for the given account index. + * Paths: + * owner: m/48'/13'/0'/account_index'/0' + * active: m/48'/13'/1'/account_index'/0' + * memo: m/48'/13'/3'/account_index'/0' + * posting: m/48'/13'/4'/account_index'/0' + * @start + * @next HivePublicKeys + * @next Failure + */ +message HiveGetPublicKeys { + optional uint32 account_index = 1 [default = 0]; // Hive account slot (0 = first account) + optional bool show_display = 2; // Confirm on device before returning +} + +/** + * Response: All four Hive role public keys + * @end + */ +message HivePublicKeys { + optional string owner_key = 1; // STM... owner public key + optional string active_key = 2; // STM... active public key + optional string memo_key = 3; // STM... memo public key + optional string posting_key = 4; // STM... posting public key +} + +/** + * Request: Sign a Hive transfer transaction (op type 2). + * Signing key should be the active key: m/48'/13'/1'/account'/0' + * @start + * @next HiveSignedTx + * @next Failure + */ +message HiveSignTx { + repeated uint32 address_n = 1; // Full SLIP-0048 path of signing key + optional bytes chain_id = 2; // 32-byte chain ID (mainnet = beeab0de...) + optional uint32 ref_block_num = 3; // Reference block number (uint16) + optional uint32 ref_block_prefix = 4; // Reference block prefix (uint32) + optional uint32 expiration = 5; // Expiration Unix timestamp (uint32) + optional string from = 6; // Sender account name + optional string to = 7; // Recipient account name + optional uint64 amount = 8; // Amount in milliHIVE (1000 = 1.000 HIVE) + optional uint32 decimals = 9; // Decimal places (3 for HIVE/HBD) + optional string asset_symbol = 10; // "HIVE" or "HBD" + optional string memo = 11; // Optional transfer memo +} + +/** + * Response: Signed Hive transfer transaction + * @end + */ +message HiveSignedTx { + optional bytes signature = 1; // 65-byte recoverable secp256k1 signature + optional bytes serialized_tx = 2; // Serialized Graphene transaction bytes +} + +/** + * Request: Sign a Hive account_create operation (op type 9). + * All four role public keys become the account authorities at genesis. + * No software keys are generated. KeepKey is sole root of trust from block 1. + * Signing key: owner key at m/48'/13'/0'/account_index'/0' + * @start + * @next HiveSignedAccountCreate + * @next Failure + */ +message HiveSignAccountCreate { + repeated uint32 address_n = 1; // Owner key path (m/48'/13'/0'/account'/0') + optional bytes chain_id = 2; // 32-byte chain ID + optional uint32 ref_block_num = 3; + optional uint32 ref_block_prefix = 4; + optional uint32 expiration = 5; + optional string creator = 6; // Pioneer sponsor account name + optional string new_account_name = 7; // Desired Hive username + optional string owner_key = 8; // STM... owner public key (from device) + optional string active_key = 9; // STM... active public key (from device) + optional string posting_key = 10; // STM... posting public key (from device) + optional string memo_key = 11; // STM... memo public key (from device) + optional uint64 fee_amount = 12; // Creation fee in milliHIVE (3000 = 3.000 HIVE) +} + +/** + * Response: Signed Hive account_create transaction + * @end + */ +message HiveSignedAccountCreate { + optional bytes signature = 1; // 65-byte recoverable signature + optional bytes serialized_tx = 2; // Serialized Graphene transaction bytes +} + +/** + * Request: Sign a Hive account_update operation (op type 10). + * Replaces all account authorities with KeepKey-derived keys. + * Used to secure an existing Hive account. + * Signing key: owner key at m/48'/13'/0'/account_index'/0' + * @start + * @next HiveSignedAccountUpdate + * @next Failure + */ +message HiveSignAccountUpdate { + repeated uint32 address_n = 1; // Owner key path (must match account's current owner) + optional bytes chain_id = 2; // 32-byte chain ID + optional uint32 ref_block_num = 3; + optional uint32 ref_block_prefix = 4; + optional uint32 expiration = 5; + optional string account = 6; // Hive account name to update + optional string new_owner_key = 7; // STM... new owner public key + optional string new_active_key = 8; // STM... new active public key + optional string new_posting_key = 9; // STM... new posting public key + optional string new_memo_key = 10; // STM... new memo public key +} + +/** + * Response: Signed Hive account_update transaction + * @end + */ +message HiveSignedAccountUpdate { + optional bytes signature = 1; // 65-byte recoverable signature + optional bytes serialized_tx = 2; // Serialized Graphene transaction bytes +} diff --git a/messages-solana.proto b/messages-solana.proto index f0ca4484..a8f4eea2 100644 --- a/messages-solana.proto +++ b/messages-solana.proto @@ -91,10 +91,9 @@ message SolanaMessageSignature { * domain separation that plain SolanaSignMessage lacks. Firmware constructs * the envelope from the components below; host supplies version/format/message. * - * Format values: + * Format values (KeepKey supports formats 0 and 1 only; max message size 1212 bytes): * 0 = Restricted ASCII (printable, max 1212 bytes) — renderable on display * 1 = UTF-8 (max 1212 bytes) — renderable, may need policy gate - * 2 = UTF-8 (max 65515 bytes) — Ledger-only mode, blind-sign * * @next SolanaOffchainMessageSignature * @next Failure @@ -103,7 +102,7 @@ message SolanaSignOffchainMessage { repeated uint32 address_n = 1; // BIP-32/BIP-44 path to signing key optional string coin_name = 2 [default = "Solana"]; optional uint32 version = 3 [default = 0]; // Off-chain message spec version (0 = current) - optional uint32 message_format = 4; // 0=ASCII, 1=UTF8 limited, 2=UTF8 extended + optional uint32 message_format = 4; // 0=ASCII, 1=UTF8 (format 2 not supported) optional bytes message = 5; // Raw message payload (firmware wraps with envelope) optional bool show_display = 6; // Show message on device display } diff --git a/messages-tron.proto b/messages-tron.proto index f091c785..ffe9bbba 100644 --- a/messages-tron.proto +++ b/messages-tron.proto @@ -141,6 +141,7 @@ message TronSignTypedHash { /** * Response: Signed typed data * @prev TronSignTypedHash + * @end */ message TronTypedDataSignature { required string address = 1; // Base58Check TRON address that signed diff --git a/messages-zcash.options b/messages-zcash.options index 9e9f9992..31426964 100644 --- a/messages-zcash.options +++ b/messages-zcash.options @@ -35,10 +35,6 @@ ZcashTransparentInput.address_n max_count:8 ZcashTransparentSig.signature max_size:73 ZcashDisplayAddress.address_n max_count:8 -ZcashDisplayAddress.address max_size:256 -ZcashDisplayAddress.ak max_size:32 -ZcashDisplayAddress.nk max_size:32 -ZcashDisplayAddress.rivk max_size:32 ZcashDisplayAddress.expected_seed_fingerprint max_size:32 ZcashAddress.address max_size:256 diff --git a/messages-zcash.proto b/messages-zcash.proto index 10c13a2a..ac65c1c5 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -161,38 +161,30 @@ message ZcashTransparentSig { } /** - * Request: Display a Zcash unified address on the device screen. + * Request: Display the device-derived Orchard unified address on screen. * - * The host provides the unified address string and the FVK components - * (ak, nk, rivk) used to derive it. The device independently derives - * the FVK from the seed and verifies it matches the provided components - * before displaying the address with a QR code. + * The device derives the Orchard-only Unified Address (Sinsemilla + SWU + * hash-to-curve, default diversifier index 0) from its own seed at the + * requested account and shows it on the OLED with a QR code. What appears + * on screen is bound to this device — there is no host-supplied address + * to validate. * - * VERIFICATION SCOPE: The device only verifies that the Orchard FVK - * (ak, nk, rivk) in this request matches what it derives from the seed - * at the given account. A Unified Address may bundle receivers from - * multiple pools (transparent, Sapling, Orchard). The device CANNOT - * verify non-Orchard receivers — the guarantee is limited to: - * "This UA contains an Orchard receiver from this account." - * It does NOT guarantee that transparent or Sapling receivers (if - * present) are also controlled by this device. + * Either account or a complete address_n path (m/32'/133'/account', all + * hardened) is REQUIRED. The device rejects requests that omit both. * - * Full on-device UA derivation (Sinsemilla + SWU hash-to-curve) - * is planned for a future firmware release. - * - * Either account or a complete address_n path is REQUIRED. - * The device will reject requests that omit both. + * Fields 3–6 (host-supplied address, ak, nk, rivk) were removed when the + * device gained on-device UA derivation: FVK-match attestation against a + * host-built UA is strictly weaker than device-derived display and was + * dropped. Field numbers are reserved to prevent reuse. * * @next ZcashAddress * @next Failure */ message ZcashDisplayAddress { + reserved 3, 4, 5, 6; + reserved "address", "ak", "nk", "rivk"; repeated uint32 address_n = 1; // ZIP-32 path [32', 133', account'] — required if account omitted optional uint32 account = 2; // Account index — required if address_n omitted - optional string address = 3; // Host-computed unified address ("u1...") - optional bytes ak = 4; // 32-byte ak for Orchard FVK verification - optional bytes nk = 5; // 32-byte nk for Orchard FVK verification - optional bytes rivk = 6; // 32-byte rivk for Orchard FVK verification optional bytes expected_seed_fingerprint = 7; // 32-byte ZIP-32 §6.1 seed fingerprint. // If present, device verifies match against its own // seed fingerprint and rejects with Failure on mismatch. diff --git a/messages.proto b/messages.proto index ca35898c..6a9fcc66 100644 --- a/messages.proto +++ b/messages.proto @@ -239,6 +239,18 @@ enum MessageType { MessageType_TonSignedTx = 1503 [ (wire_out) = true ]; MessageType_TonSignMessage = 1504 [ (wire_in) = true ]; MessageType_TonMessageSignature = 1505 [ (wire_out) = true ]; + + // Hive + MessageType_HiveGetPublicKey = 1600 [ (wire_in) = true ]; + MessageType_HivePublicKey = 1601 [ (wire_out) = true ]; + MessageType_HiveSignTx = 1602 [ (wire_in) = true ]; + MessageType_HiveSignedTx = 1603 [ (wire_out) = true ]; + MessageType_HiveGetPublicKeys = 1604 [ (wire_in) = true ]; + MessageType_HivePublicKeys = 1605 [ (wire_out) = true ]; + MessageType_HiveSignAccountCreate = 1606 [ (wire_in) = true ]; + MessageType_HiveSignedAccountCreate = 1607 [ (wire_out) = true ]; + MessageType_HiveSignAccountUpdate = 1608 [ (wire_in) = true ]; + MessageType_HiveSignedAccountUpdate = 1609 [ (wire_out) = true ]; } //////////////////// diff --git a/package-lock.json b/package-lock.json index a709fae2..11708cdd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,140 +1,164 @@ { "name": "@keepkey/device-protocol", - "version": "7.2.4", - "lockfileVersion": 1, + "version": "7.14.1", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@protobufjs/aspromise": { + "packages": { + "": { + "name": "@keepkey/device-protocol", + "version": "7.14.1", + "license": "ISC", + "dependencies": { + "google-protobuf": "^3.7.0-rc.2", + "pbjs": "^0.0.5" + }, + "devDependencies": { + "protobufjs": "^6.8.8", + "ts-protoc-gen": "^0.10.0" + } + }, + "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", "dev": true }, - "@protobufjs/base64": { + "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "dev": true }, - "@protobufjs/codegen": { + "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", "dev": true }, - "@protobufjs/eventemitter": { + "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", "dev": true }, - "@protobufjs/fetch": { + "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", "dev": true, - "requires": { + "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, - "@protobufjs/float": { + "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", "dev": true }, - "@protobufjs/inquire": { + "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", "dev": true }, - "@protobufjs/path": { + "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", "dev": true }, - "@protobufjs/pool": { + "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", "dev": true }, - "@protobufjs/utf8": { + "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", "dev": true }, - "@types/long": { + "node_modules/@types/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==", "dev": true }, - "@types/node": { + "node_modules/@types/node": { "version": "10.14.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz", "integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==", "dev": true }, - "bytebuffer": { + "node_modules/bytebuffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", - "requires": { + "dependencies": { "long": "~3" }, - "dependencies": { - "long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" - } + "engines": { + "node": ">=0.8" } }, - "commander": { + "node_modules/bytebuffer/node_modules/long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/commander": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "requires": { + "dependencies": { "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" } }, - "google-protobuf": { + "node_modules/google-protobuf": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.7.1.tgz", "integrity": "sha512-6fvlUey6cNKtWSEn1bt4CT4wc2EID1fVluHS1dOnqIlxyIu3cBid2BvWE8Rwl6wN+hRTgiAKhfyydAGV/weZYQ==" }, - "graceful-readlink": { + "node_modules/graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, - "long": { + "node_modules/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", "dev": true }, - "pbjs": { + "node_modules/pbjs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/pbjs/-/pbjs-0.0.5.tgz", "integrity": "sha1-tMiOFarEVSygkiqmTNUzjv00R78=", - "requires": { + "dependencies": { "bytebuffer": "5.0.1", "commander": "2.9.0", "protocol-buffers-schema": "3.1.0" + }, + "bin": { + "pbjs": "cli.js" } }, - "protobufjs": { + "node_modules/protobufjs": { "version": "6.8.8", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", "dev": true, - "requires": { + "hasInstallScript": true, + "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", @@ -148,18 +172,29 @@ "@types/long": "^4.0.0", "@types/node": "^10.1.0", "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" } }, - "protocol-buffers-schema": { + "node_modules/protocol-buffers-schema": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.1.0.tgz", "integrity": "sha1-2KgZVJ6tPmvRievp5Q6WY2u8XMc=" }, - "ts-protoc-gen": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.9.0.tgz", - "integrity": "sha512-cFEUTY9U9o6C4DPPfMHk2ZUdIAKL91hZN1fyx5Stz3g56BDVOC7hk+r5fEMCAGaaIgi2akkT1a2hrxu1wo2Phg==", - "dev": true + "node_modules/ts-protoc-gen": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.10.0.tgz", + "integrity": "sha512-EEbgDWNHK3CvcNhmib94I4HMO23qLddjLRdXW8EUE11VJxbi3n5J0l2DiX/L1pijOaPTkbEoRK+zQinKgKGqsw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "google-protobuf": "^3.6.1" + }, + "bin": { + "protoc-gen-ts": "bin/protoc-gen-ts" + } } } } diff --git a/package.json b/package.json index e004b2d8..ab77a719 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "@keepkey/device-protocol", - "version": "7.13.4", + "version": "7.14.1", "publishConfig": { "access": "public" }, "description": "The proto buffer files from the KeepKey device, packed for consumption by the client.", "scripts": { "clean": "rm -rf ./lib/*.js ./lib/*.ts", - "build": "npm run build:js && npm run build:json && npm run build:postprocess", - "build:js": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-solana.proto messages-tron.proto messages-ton.proto messages-zcash.proto", - "build:json": "pbjs --keep-case -t json ./types.proto ./messages.proto ./messages-ethereum.proto ./messages-eos.proto ./messages-nano.proto ./messages-cosmos.proto ./messages-binance.proto ./messages-ripple.proto ./messages-tendermint.proto ./messages-thorchain.proto ./messages-osmosis.proto ./messages-mayachain.proto ./messages-solana.proto ./messages-tron.proto ./messages-ton.proto ./messages-zcash.proto > ./lib/proto.json", + "build": "npm run build:js && npm run build:postprocess", + "build:js": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-solana.proto messages-tron.proto messages-ton.proto messages-zcash.proto messages-hive.proto", + "build:json": "pbjs --keep-case -t json ./types.proto ./messages.proto ./messages-ethereum.proto ./messages-eos.proto ./messages-nano.proto ./messages-cosmos.proto ./messages-binance.proto ./messages-ripple.proto ./messages-tendermint.proto ./messages-thorchain.proto ./messages-osmosis.proto ./messages-mayachain.proto ./messages-solana.proto ./messages-tron.proto ./messages-ton.proto > ./lib/proto.json", "build:postprocess": "find ./lib -name \"*.js\" -exec sed -i '' -e \"s/var global = Function(\\'return this\\')();/var global = (function(){ return this }).call(null);/g\" {} \\;", "prepublishOnly": "npm run build", "test": "echo \"Error: no test specified\" && exit 1" diff --git a/yarn.lock b/yarn.lock index c4f17406..aaaab881 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,90 +4,105 @@ "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= "@protobufjs/base64@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== "@protobufjs/codegen@^2.0.4": version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== "@protobufjs/eventemitter@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= "@protobufjs/fetch@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= dependencies: "@protobufjs/aspromise" "^1.1.1" "@protobufjs/inquire" "^1.1.0" "@protobufjs/float@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= "@protobufjs/inquire@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= "@protobufjs/path@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= "@protobufjs/pool@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= "@protobufjs/utf8@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= "@types/long@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef" + resolved "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz" + integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q== "@types/node@^10.1.0": - version "10.12.24" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.24.tgz#b13564af612a22a20b5d95ca40f1bffb3af315cf" + version "10.14.6" + resolved "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz" + integrity sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg== bytebuffer@5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" + resolved "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz" + integrity sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0= dependencies: long "~3" commander@2.9.0: version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + resolved "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz" + integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= dependencies: graceful-readlink ">= 1.0.0" -google-protobuf@^3.6.1: - version "3.9.0" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.9.0.tgz#1f33e51e7993ea51e758a82650ad4347273b9bc6" - -google-protobuf@^3.7.0-rc.2: - version "3.7.0-rc.2" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.7.0-rc.2.tgz#a65e9216825065099c4ff243eee9e16e764cc2c9" +google-protobuf@^3.6.1, google-protobuf@^3.7.0-rc.2: + version "3.7.1" + resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.7.1.tgz" + integrity sha512-6fvlUey6cNKtWSEn1bt4CT4wc2EID1fVluHS1dOnqIlxyIu3cBid2BvWE8Rwl6wN+hRTgiAKhfyydAGV/weZYQ== "graceful-readlink@>= 1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + resolved "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= long@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== long@~3: version "3.2.0" - resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" + resolved "https://registry.npmjs.org/long/-/long-3.2.0.tgz" + integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s= pbjs@^0.0.5: version "0.0.5" - resolved "https://registry.yarnpkg.com/pbjs/-/pbjs-0.0.5.tgz#b4c88e15aac4552ca0922aa64cd5338efd3447bf" + resolved "https://registry.npmjs.org/pbjs/-/pbjs-0.0.5.tgz" + integrity sha1-tMiOFarEVSygkiqmTNUzjv00R78= dependencies: bytebuffer "5.0.1" commander "2.9.0" @@ -95,7 +110,8 @@ pbjs@^0.0.5: protobufjs@^6.8.8: version "6.8.8" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz" + integrity sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -113,10 +129,12 @@ protobufjs@^6.8.8: protocol-buffers-schema@3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.1.0.tgz#d8a819549ead3e6bd189ebe9e50e96636bbc5cc7" + resolved "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.1.0.tgz" + integrity sha1-2KgZVJ6tPmvRievp5Q6WY2u8XMc= ts-protoc-gen@^0.10.0: version "0.10.0" - resolved "https://registry.yarnpkg.com/ts-protoc-gen/-/ts-protoc-gen-0.10.0.tgz#f708d99be59ad0be6bdce6f4fe893ec41757d2c9" + resolved "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.10.0.tgz" + integrity sha512-EEbgDWNHK3CvcNhmib94I4HMO23qLddjLRdXW8EUE11VJxbi3n5J0l2DiX/L1pijOaPTkbEoRK+zQinKgKGqsw== dependencies: google-protobuf "^3.6.1" From 8778b6e4bd228d69bc8c6b6ecf10a74277a40bc1 Mon Sep 17 00:00:00 2001 From: highlander Date: Thu, 21 May 2026 13:05:54 -0300 Subject: [PATCH 16/45] feat(near): add NEAR Protocol proto definitions (MessageType 1610-1613) NearGetAddress, NearAddress, NearSignTx, NearSignedTx. Ed25519 derivation m/44'/397'/0'. Implicit account = lowercase hex of the 32-byte Ed25519 pubkey. Numbered 1610-1613 to avoid colliding with Hive (1600-1609), which landed on alpha after the original NEAR spike. --- messages-near.proto | 60 +++++++++++++++++++++++++++++++++++++++++++++ messages.proto | 6 +++++ package.json | 2 +- 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 messages-near.proto diff --git a/messages-near.proto b/messages-near.proto new file mode 100644 index 00000000..c44b04dc --- /dev/null +++ b/messages-near.proto @@ -0,0 +1,60 @@ +/* + * Messages (NEAR Protocol) for KeepKey Communication + * + * NEAR native account support via Ed25519 on the device. + * Derivation path: m/44'/397'/0' (SLIP-0010 hardened-only) + * Address: lowercase hex of 32-byte Ed25519 public key (implicit account) + * Signing: Ed25519(SHA256(borsh_serialized_transaction)) + */ + +syntax = "proto2"; + +option java_package = "com.keepkey.deviceprotocol"; +option java_outer_classname = "KeepKeyMessageNear"; + +/** + * Request: Derive NEAR implicit account address at the given path + * @next NearAddress + * @next Failure + */ +message NearGetAddress { + repeated uint32 address_n = 1; // BIP-44 path (e.g. m/44'/397'/0') + optional string coin_name = 2 [default = "NEAR"]; + optional bool show_display = 3; +} + +/** + * Response: NEAR implicit account address (lowercase hex of Ed25519 pubkey) + * @prev NearGetAddress + */ +message NearAddress { + optional string address = 1; // lowercase hex of 32-byte Ed25519 pubkey + optional bytes public_key = 2; // Raw 32-byte Ed25519 pubkey +} + +/** + * Request: Sign a NEAR transaction + * + * The host Borsh-serializes the full NearTransaction and sends raw bytes. + * Firmware SHA256-hashes the bytes, signs with Ed25519, returns 64-byte signature. + * receiver_id and action_display are provided separately for on-screen confirmation. + * + * @next NearSignedTx + * @next Failure + */ +message NearSignTx { + repeated uint32 address_n = 1; // BIP-44 path to signing key + optional string coin_name = 2 [default = "NEAR"]; + optional bytes raw_tx = 3; // Borsh-serialized NearTransaction (max 1024 bytes) + optional string receiver_id = 4; // Destination account for display (max 64 chars) + optional string action_display = 5; // Human-readable action summary for display +} + +/** + * Response: Ed25519 signature over SHA256(raw_tx) + * @prev NearSignTx + */ +message NearSignedTx { + optional bytes signature = 1; // 64-byte Ed25519 signature + optional bytes public_key = 2; // 32-byte Ed25519 pubkey (for verification) +} diff --git a/messages.proto b/messages.proto index 1a5466af..4697f0f2 100644 --- a/messages.proto +++ b/messages.proto @@ -253,6 +253,12 @@ enum MessageType { MessageType_HiveSignedAccountCreate = 1607 [ (wire_out) = true ]; MessageType_HiveSignAccountUpdate = 1608 [ (wire_in) = true ]; MessageType_HiveSignedAccountUpdate = 1609 [ (wire_out) = true ]; + + // NEAR + MessageType_NearGetAddress = 1610 [ (wire_in) = true ]; + MessageType_NearAddress = 1611 [ (wire_out) = true ]; + MessageType_NearSignTx = 1612 [ (wire_in) = true ]; + MessageType_NearSignedTx = 1613 [ (wire_out) = true ]; } //////////////////// diff --git a/package.json b/package.json index ab77a719..aba79303 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "scripts": { "clean": "rm -rf ./lib/*.js ./lib/*.ts", "build": "npm run build:js && npm run build:postprocess", - "build:js": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-solana.proto messages-tron.proto messages-ton.proto messages-zcash.proto messages-hive.proto", + "build:js": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-solana.proto messages-tron.proto messages-ton.proto messages-zcash.proto messages-hive.proto messages-near.proto", "build:json": "pbjs --keep-case -t json ./types.proto ./messages.proto ./messages-ethereum.proto ./messages-eos.proto ./messages-nano.proto ./messages-cosmos.proto ./messages-binance.proto ./messages-ripple.proto ./messages-tendermint.proto ./messages-thorchain.proto ./messages-osmosis.proto ./messages-mayachain.proto ./messages-solana.proto ./messages-tron.proto ./messages-ton.proto > ./lib/proto.json", "build:postprocess": "find ./lib -name \"*.js\" -exec sed -i '' -e \"s/var global = Function(\\'return this\\')();/var global = (function(){ return this }).call(null);/g\" {} \\;", "prepublishOnly": "npm run build", From 2dc51d5dc8cbf584924f09f905d483ddfe99f3c6 Mon Sep 17 00:00:00 2001 From: Highlander Date: Mon, 29 Jun 2026 17:56:53 -0500 Subject: [PATCH 17/45] docs(hive): clarify HiveSignAccountCreate is an attestation, not a broadcast (#35) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit account_create is authorized on-chain by the creator (sponsor) account's active authority, not by the new account. The device's owner-key signature here is a proof-of-control attestation; the sponsor recovers the owner pubkey to verify control, then rebuilds and signs the real account_create with the creator's active key server-side. Comment-only — no wire or generated-code change. Addresses review P1 on the (closed) feature/hive PR. --- messages-hive.proto | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/messages-hive.proto b/messages-hive.proto index 7612ec47..2141f2e9 100644 --- a/messages-hive.proto +++ b/messages-hive.proto @@ -85,10 +85,20 @@ message HiveSignedTx { } /** - * Request: Sign a Hive account_create operation (op type 9). - * All four role public keys become the account authorities at genesis. - * No software keys are generated. KeepKey is sole root of trust from block 1. - * Signing key: owner key at m/48'/13'/0'/account_index'/0' + * Request: ATTEST to a Hive account_create operation (op type 9). + * + * NOT a broadcast signature. In Hive, account_create is authorized by the + * CREATOR (sponsor) account's ACTIVE authority — which the new account holder + * does not have. The device instead signs the account_create bytes with the new + * account's OWNER key as a proof-of-control attestation (confirmed on-device). + * The sponsor recovers the owner pubkey from this signature to verify control, + * then rebuilds and signs the real account_create with the creator's active key + * server-side and broadcasts that. The serialized_tx/signature returned here are + * attestation material, not a broadcastable transaction. + * + * All four role public keys become the account authorities at genesis; no + * software keys are generated (KeepKey is sole root of trust from block 1). + * Attestation signing key: owner key at m/48'/13'/0'/account_index'/0' * @start * @next HiveSignedAccountCreate * @next Failure From f9e608196cd18d9649554ff4c9374364b966f5e5 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 30 Jun 2026 00:47:24 -0500 Subject: [PATCH 18/45] feat: protocol additions for the firmware 7.x release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the device-protocol messages required by the upcoming firmware release: - thorchain: ThorchainMsgSend.denom (field 11) — non-RUNE assets (TCY, RUJI, IBC) - ripple: RippleSignTx.memo (field 7) — XRP->THORChain swap routing - hive: full Hive support — HiveGetPublicKey(s), HiveSignTx, HiveSignAccountCreate/Update (MessageType 1600-1609) - zcash: clear-signing + Orchard shielded protocol (transparent in/out/ack, PCZT, FVK, display-address) All additions are new optional fields / new message types — additive and backward-compatible. lib/ bindings are gitignored build artifacts; package.json build:js/json updated to include messages-hive.proto. --- messages-hive.options | 46 ++++++++++++ messages-hive.proto | 149 +++++++++++++++++++++++++++++++++++++++ messages-ripple.proto | 1 + messages-thorchain.proto | 1 + messages-zcash.options | 16 +++-- messages-zcash.proto | 120 +++++++++++++++++++++++-------- messages.proto | 16 ++++- package.json | 4 +- 8 files changed, 317 insertions(+), 36 deletions(-) create mode 100644 messages-hive.options create mode 100644 messages-hive.proto diff --git a/messages-hive.options b/messages-hive.options new file mode 100644 index 00000000..e878f3e3 --- /dev/null +++ b/messages-hive.options @@ -0,0 +1,46 @@ +HiveGetPublicKey.address_n max_count:8 + +HivePublicKey.public_key max_size:64 +HivePublicKey.raw_public_key max_size:33 + +HiveGetPublicKeys.account_index int_size:IS_32 + +HivePublicKeys.owner_key max_size:64 +HivePublicKeys.active_key max_size:64 +HivePublicKeys.memo_key max_size:64 +HivePublicKeys.posting_key max_size:64 + +HiveSignTx.address_n max_count:8 +HiveSignTx.chain_id max_size:32 +HiveSignTx.from max_size:16 +HiveSignTx.to max_size:16 +HiveSignTx.amount int_size:IS_64 +HiveSignTx.asset_symbol max_size:10 +HiveSignTx.memo max_size:2048 + +HiveSignedTx.signature max_size:65 +HiveSignedTx.serialized_tx max_size:512 + +HiveSignAccountCreate.address_n max_count:8 +HiveSignAccountCreate.chain_id max_size:32 +HiveSignAccountCreate.creator max_size:16 +HiveSignAccountCreate.new_account_name max_size:16 +HiveSignAccountCreate.owner_key max_size:64 +HiveSignAccountCreate.active_key max_size:64 +HiveSignAccountCreate.posting_key max_size:64 +HiveSignAccountCreate.memo_key max_size:64 +HiveSignAccountCreate.fee_amount int_size:IS_64 + +HiveSignedAccountCreate.signature max_size:65 +HiveSignedAccountCreate.serialized_tx max_size:512 + +HiveSignAccountUpdate.address_n max_count:8 +HiveSignAccountUpdate.chain_id max_size:32 +HiveSignAccountUpdate.account max_size:16 +HiveSignAccountUpdate.new_owner_key max_size:64 +HiveSignAccountUpdate.new_active_key max_size:64 +HiveSignAccountUpdate.new_posting_key max_size:64 +HiveSignAccountUpdate.new_memo_key max_size:64 + +HiveSignedAccountUpdate.signature max_size:65 +HiveSignedAccountUpdate.serialized_tx max_size:512 diff --git a/messages-hive.proto b/messages-hive.proto new file mode 100644 index 00000000..7612ec47 --- /dev/null +++ b/messages-hive.proto @@ -0,0 +1,149 @@ +syntax = "proto2"; + +option java_package = "com.shapeshift.keepkey.lib.protobuf"; +option java_outer_classname = "KeepKeyMessageHive"; + +/** + * Request: Ask device for a single Hive public key at a given SLIP-0048 path. + * Path format: m/48'/13'/role'/account'/0' + * role: 0'=owner 1'=active 3'=memo 4'=posting + * @start + * @next HivePublicKey + * @next Failure + */ +message HiveGetPublicKey { + repeated uint32 address_n = 1; // Full SLIP-0048 path (all 5 components hardened) + optional bool show_display = 2; // Confirm on device before returning + optional uint32 role = 3; // 0=owner 1=active 3=memo 4=posting (for display label only) +} + +/** + * Response: Single Hive public key + * @end + */ +message HivePublicKey { + optional string public_key = 1; // STM-prefixed base58check public key + optional bytes raw_public_key = 2; // 33-byte compressed secp256k1 public key +} + +/** + * Request: Ask device for all four Hive role keys in one interaction. + * Derives owner/active/memo/posting keys for the given account index. + * Paths: + * owner: m/48'/13'/0'/account_index'/0' + * active: m/48'/13'/1'/account_index'/0' + * memo: m/48'/13'/3'/account_index'/0' + * posting: m/48'/13'/4'/account_index'/0' + * @start + * @next HivePublicKeys + * @next Failure + */ +message HiveGetPublicKeys { + optional uint32 account_index = 1 [default = 0]; // Hive account slot (0 = first account) + optional bool show_display = 2; // Confirm on device before returning +} + +/** + * Response: All four Hive role public keys + * @end + */ +message HivePublicKeys { + optional string owner_key = 1; // STM... owner public key + optional string active_key = 2; // STM... active public key + optional string memo_key = 3; // STM... memo public key + optional string posting_key = 4; // STM... posting public key +} + +/** + * Request: Sign a Hive transfer transaction (op type 2). + * Signing key should be the active key: m/48'/13'/1'/account'/0' + * @start + * @next HiveSignedTx + * @next Failure + */ +message HiveSignTx { + repeated uint32 address_n = 1; // Full SLIP-0048 path of signing key + optional bytes chain_id = 2; // 32-byte chain ID (mainnet = beeab0de...) + optional uint32 ref_block_num = 3; // Reference block number (uint16) + optional uint32 ref_block_prefix = 4; // Reference block prefix (uint32) + optional uint32 expiration = 5; // Expiration Unix timestamp (uint32) + optional string from = 6; // Sender account name + optional string to = 7; // Recipient account name + optional uint64 amount = 8; // Amount in milliHIVE (1000 = 1.000 HIVE) + optional uint32 decimals = 9; // Decimal places (3 for HIVE/HBD) + optional string asset_symbol = 10; // "HIVE" or "HBD" + optional string memo = 11; // Optional transfer memo +} + +/** + * Response: Signed Hive transfer transaction + * @end + */ +message HiveSignedTx { + optional bytes signature = 1; // 65-byte recoverable secp256k1 signature + optional bytes serialized_tx = 2; // Serialized Graphene transaction bytes +} + +/** + * Request: Sign a Hive account_create operation (op type 9). + * All four role public keys become the account authorities at genesis. + * No software keys are generated. KeepKey is sole root of trust from block 1. + * Signing key: owner key at m/48'/13'/0'/account_index'/0' + * @start + * @next HiveSignedAccountCreate + * @next Failure + */ +message HiveSignAccountCreate { + repeated uint32 address_n = 1; // Owner key path (m/48'/13'/0'/account'/0') + optional bytes chain_id = 2; // 32-byte chain ID + optional uint32 ref_block_num = 3; + optional uint32 ref_block_prefix = 4; + optional uint32 expiration = 5; + optional string creator = 6; // Pioneer sponsor account name + optional string new_account_name = 7; // Desired Hive username + optional string owner_key = 8; // STM... owner public key (from device) + optional string active_key = 9; // STM... active public key (from device) + optional string posting_key = 10; // STM... posting public key (from device) + optional string memo_key = 11; // STM... memo public key (from device) + optional uint64 fee_amount = 12; // Creation fee in milliHIVE (3000 = 3.000 HIVE) +} + +/** + * Response: Signed Hive account_create transaction + * @end + */ +message HiveSignedAccountCreate { + optional bytes signature = 1; // 65-byte recoverable signature + optional bytes serialized_tx = 2; // Serialized Graphene transaction bytes +} + +/** + * Request: Sign a Hive account_update operation (op type 10). + * Replaces all account authorities with KeepKey-derived keys. + * Used to secure an existing Hive account. + * Signing key: owner key at m/48'/13'/0'/account_index'/0' + * @start + * @next HiveSignedAccountUpdate + * @next Failure + */ +message HiveSignAccountUpdate { + repeated uint32 address_n = 1; // Owner key path (must match account's current owner) + optional bytes chain_id = 2; // 32-byte chain ID + optional uint32 ref_block_num = 3; + optional uint32 ref_block_prefix = 4; + optional uint32 expiration = 5; + optional string account = 6; // Hive account name to update + optional string new_owner_key = 7; // STM... new owner public key + optional string new_active_key = 8; // STM... new active public key + optional string new_posting_key = 9; // STM... new posting public key + optional string new_memo_key = 10; // STM... new memo public key +} + +/** + * Response: Signed Hive account_update transaction + * @end + */ +message HiveSignedAccountUpdate { + optional bytes signature = 1; // 65-byte recoverable signature + optional bytes serialized_tx = 2; // Serialized Graphene transaction bytes +} diff --git a/messages-ripple.proto b/messages-ripple.proto index 0fc012d2..bf526ef7 100644 --- a/messages-ripple.proto +++ b/messages-ripple.proto @@ -34,6 +34,7 @@ message RippleSignTx { optional uint32 sequence = 4; // transaction sequence number optional uint32 last_ledger_sequence = 5; // see https://developers.ripple.com/reliable-transaction-submission.html#lastledgersequence optional RipplePayment payment = 6; // Payment transaction type + optional string memo = 7; // transaction memo (e.g. THORChain swap routing memo) } /** diff --git a/messages-thorchain.proto b/messages-thorchain.proto index acde357a..00183623 100644 --- a/messages-thorchain.proto +++ b/messages-thorchain.proto @@ -60,6 +60,7 @@ message ThorchainMsgSend { optional uint64 amount = 8 [jstype = JS_STRING]; optional OutputAddressType address_type = 9; reserved 10; + optional string denom = 11; // asset denom, e.g. "rune" or IBC denom } message ThorchainMsgDeposit { diff --git a/messages-zcash.options b/messages-zcash.options index 89fcdc36..2ebcb224 100644 --- a/messages-zcash.options +++ b/messages-zcash.options @@ -5,6 +5,7 @@ ZcashSignPCZT.transparent_digest max_size:32 ZcashSignPCZT.sapling_digest max_size:32 ZcashSignPCZT.orchard_digest max_size:32 ZcashSignPCZT.orchard_anchor max_size:32 +ZcashSignPCZT.expected_seed_fingerprint max_size:32 ZcashPCZTAction.alpha max_size:32 ZcashPCZTAction.sighash max_size:32 @@ -17,6 +18,8 @@ ZcashPCZTAction.enc_memo max_size:512 ZcashPCZTAction.enc_noncompact max_size:612 ZcashPCZTAction.rk max_size:32 ZcashPCZTAction.out_ciphertext max_size:80 +ZcashPCZTAction.recipient max_size:43 +ZcashPCZTAction.rseed max_size:32 ZcashSignedPCZT.signatures max_count:64 max_size:64 ZcashSignedPCZT.txid max_size:32 @@ -26,16 +29,19 @@ ZcashGetOrchardFVK.address_n max_count:8 ZcashOrchardFVK.ak max_size:32 ZcashOrchardFVK.nk max_size:32 ZcashOrchardFVK.rivk max_size:32 +ZcashOrchardFVK.seed_fingerprint max_size:32 + +ZcashTransparentOutput.script_pubkey max_size:128 ZcashTransparentInput.sighash max_size:32 ZcashTransparentInput.address_n max_count:8 +ZcashTransparentInput.prevout_txid max_size:32 +ZcashTransparentInput.script_pubkey max_size:128 -ZcashTransparentSig.signature max_size:73 +ZcashTransparentSigned.signatures max_count:8 max_size:73 ZcashDisplayAddress.address_n max_count:8 -ZcashDisplayAddress.address max_size:256 -ZcashDisplayAddress.ak max_size:32 -ZcashDisplayAddress.nk max_size:32 -ZcashDisplayAddress.rivk max_size:32 +ZcashDisplayAddress.expected_seed_fingerprint max_size:32 ZcashAddress.address max_size:256 +ZcashAddress.seed_fingerprint max_size:32 diff --git a/messages-zcash.proto b/messages-zcash.proto index e6e14445..c9659dd3 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -18,6 +18,7 @@ option java_outer_classname = "KeepKeyMessageZcash"; * The device derives spend authorization keys, computes the sighash, * and returns RedPallas signatures for each Orchard action. * + * @next ZcashTransparentAck * @next ZcashPCZTActionAck * @next Failure */ @@ -32,14 +33,26 @@ message ZcashSignPCZT { // Phase 2a: sub-digests for on-device sighash computation optional bytes header_digest = 8; // 32-byte pre-computed header digest optional bytes transparent_digest = 9; // 32-byte transparent digest (or empty) - optional bytes sapling_digest = 10; // 32-byte sapling digest (or empty) + optional bytes sapling_digest = 10; // Reserved for future Sapling support; currently rejected optional bytes orchard_digest = 11; // 32-byte orchard digest // Phase 2b: bundle metadata for orchard digest verification optional uint32 orchard_flags = 12; // Orchard bundle flags byte optional int64 orchard_value_balance = 13; // Orchard value balance (LE i64) optional bytes orchard_anchor = 14; // 32-byte orchard anchor + // Phase 4: plaintext header fields for on-device header digest verification + optional uint32 tx_version = 15; // Transaction version (without overwinter bit) + optional uint32 version_group_id = 16; // Version group ID + optional uint32 lock_time = 17; // Transaction lock time + optional uint32 expiry_height = 18; // Transaction expiry height // Phase 3: transparent shielding support + optional uint32 n_transparent_outputs = 29; // 0 for shielded-only (default) optional uint32 n_transparent_inputs = 30; // 0 for shielded-only (default), >0 for hybrid shielding tx + // Seed identity binding (ZIP-32 §6.1) + optional bytes expected_seed_fingerprint = 31; // 32-byte ZIP-32 §6.1 seed fingerprint: + // BLAKE2b-256("Zcash_HD_Seed_FP", + // I2LEBSP_8(len(seed)) || seed) + // If present, device verifies match against its own + // seed fingerprint and rejects with Failure on mismatch. } /** @@ -66,6 +79,11 @@ message ZcashPCZTAction { optional bytes enc_noncompact = 12; // Remaining encrypted note bytes optional bytes rk = 13; // 32-byte randomized verification key optional bytes out_ciphertext = 14; // 80-byte output ciphertext + // Phase 4: plaintext Orchard output metadata for trusted display. + // Firmware recomputes cmx from recipient/value/rseed and nullifier before + // displaying the receiver/value and before emitting any signature. + optional bytes recipient = 15; // 43-byte Orchard receiver: d || pk_d + optional bytes rseed = 16; // 32-byte output note rseed } /** @@ -116,64 +134,110 @@ message ZcashOrchardFVK { optional bytes ak = 1; // 32-byte authorizing key (Pallas point) optional bytes nk = 2; // 32-byte nullifier deriving key optional bytes rivk = 3; // 32-byte commitment randomness key + optional bytes seed_fingerprint = 4; // 32-byte ZIP-32 §6.1 seed fingerprint: + // BLAKE2b-256("Zcash_HD_Seed_FP", + // I2LEBSP_8(len(seed)) || seed) + // Stable identity of the device's seed; lets a host pin an FVK + // to a specific seed across sessions. +} + +/** + * Request: Transparent output data for hybrid transactions. + * Sent before transparent inputs so the device can review standard + * transparent recipients before any signature is emitted. + * + * @next ZcashTransparentAck + * @next ZcashPCZTActionAck + * @next Failure + */ +message ZcashTransparentOutput { + required uint32 index = 1; // Output index within the transaction + optional uint64 amount = 2; // Output value in zatoshis + optional bytes script_pubkey = 3; // Standard P2PKH/P2SH scriptPubKey } /** * Request: Transparent input data for hybrid shielding transactions. - * Sent one per transparent input during the transparent signing phase. - * The device ECDSA-signs the per-input sighash with the secp256k1 key - * at the provided BIP44 path. + * Sent after all transparent outputs have been streamed. * - * Flow: after ZcashSignPCZT with n_transparent_inputs > 0, the device - * responds with ZcashPCZTActionAck. For each transparent input, the host - * sends ZcashTransparentInput and receives ZcashTransparentSig. After - * all transparent inputs, the device transitions to the Orchard phase. + * The device stores every input first because ZIP-244 per-input transparent + * sighashes commit to all transparent prevouts, values, scripts, sequences, + * and outputs. Host-provided sighash is legacy and rejected when present. * - * @next ZcashTransparentSig + * @next ZcashTransparentAck + * @next ZcashTransparentSigned * @next Failure */ message ZcashTransparentInput { required uint32 index = 1; // Input index within the transaction - required bytes sighash = 2; // 32-byte per-input sighash (host-computed, ZIP-244) + optional bytes sighash = 2; // Legacy host-computed sighash; rejected when present repeated uint32 address_n = 3; // BIP44 path [44', 133', 0', 0, 0] - optional uint64 amount = 4; // Input value in zatoshis (for display verification) + optional uint64 amount = 4; // Input value in zatoshis + optional bytes prevout_txid = 5; // Previous transaction ID + optional uint32 prevout_index = 6; // Previous output index + optional uint32 sequence = 7; // Input sequence + optional bytes script_pubkey = 8; // Previous output scriptPubKey +} + +/** + * Response: Acknowledgment requesting the next transparent item. + * + * @prev ZcashSignPCZT + * @prev ZcashTransparentOutput + * @prev ZcashTransparentInput + */ +message ZcashTransparentAck { + optional uint32 next_output_index = 1; // Next transparent output index + optional uint32 next_input_index = 2; // Next transparent input index } /** - * Response: ECDSA signature for a transparent input. + * Response: ECDSA signatures for transparent inputs. * * @prev ZcashTransparentInput */ -message ZcashTransparentSig { - required bytes signature = 1; // DER ECDSA signature (72-73 bytes) - optional uint32 next_index = 2; // Next transparent input index, or 0xFF = done +message ZcashTransparentSigned { + repeated bytes signatures = 1; // DER ECDSA signatures, one per transparent input } /** - * Request: Display and verify a Zcash unified address on device. + * Request: Display the device-derived Orchard unified address on screen. * - * The host provides the unified address string and the FVK components - * (ak, nk, rivk). The device re-derives its own Orchard keys from seed - * and compares them against the provided FVK to verify the Orchard - * receiver belongs to this device. + * The device derives the Orchard-only Unified Address (Sinsemilla + SWU + * hash-to-curve, default diversifier index 0) from its own seed at the + * requested account and shows it on the OLED with a QR code. What appears + * on screen is bound to this device — there is no host-supplied address + * to validate. + * + * Either account or a complete address_n path (m/32'/133'/account', all + * hardened) is REQUIRED. The device rejects requests that omit both. + * + * Fields 3–6 (host-supplied address, ak, nk, rivk) were removed when the + * device gained on-device UA derivation: FVK-match attestation against a + * host-built UA is strictly weaker than device-derived display and was + * dropped. Field numbers are reserved to prevent reuse. * * @next ZcashAddress * @next Failure */ message ZcashDisplayAddress { - repeated uint32 address_n = 1; // ZIP-32 derivation path [32', 133', account'] - optional uint32 account = 2; // Account index (alternative to full path) - optional string address = 3; // Unified address string (u1...) - optional bytes ak = 4; // 32-byte authorizing key for verification - optional bytes nk = 5; // 32-byte nullifier deriving key for verification - optional bytes rivk = 6; // 32-byte commitment randomness key for verification + reserved 3, 4, 5, 6; + reserved "address", "ak", "nk", "rivk"; + repeated uint32 address_n = 1; // ZIP-32 path [32', 133', account'] — required if account omitted + optional uint32 account = 2; // Account index — required if address_n omitted + optional bytes expected_seed_fingerprint = 7; // 32-byte ZIP-32 §6.1 seed fingerprint. + // If present, device verifies match against its own + // seed fingerprint and rejects with Failure on mismatch. } /** - * Response: Verified Zcash address. + * Response: Confirmed Zcash address after user approval on device. * * @prev ZcashDisplayAddress */ message ZcashAddress { - optional string address = 1; // Verified unified address string + optional string address = 1; // Confirmed unified address + optional bytes seed_fingerprint = 2; // 32-byte ZIP-32 §6.1 seed fingerprint of the attesting + // device. Returned alongside the confirmed address so a host + // can record that this UA is bound to this device's seed. } diff --git a/messages.proto b/messages.proto index ca35898c..1a5466af 100644 --- a/messages.proto +++ b/messages.proto @@ -217,9 +217,11 @@ enum MessageType { MessageType_ZcashGetOrchardFVK = 1304 [ (wire_in) = true ]; MessageType_ZcashOrchardFVK = 1305 [ (wire_out) = true ]; MessageType_ZcashTransparentInput = 1306 [ (wire_in) = true ]; - MessageType_ZcashTransparentSig = 1307 [ (wire_out) = true ]; + MessageType_ZcashTransparentSigned = 1307 [ (wire_out) = true ]; MessageType_ZcashDisplayAddress = 1308 [ (wire_in) = true ]; MessageType_ZcashAddress = 1309 [ (wire_out) = true ]; + MessageType_ZcashTransparentOutput = 1310 [ (wire_in) = true ]; + MessageType_ZcashTransparentAck = 1311 [ (wire_out) = true ]; // TRON MessageType_TronGetAddress = 1400 [ (wire_in) = true ]; @@ -239,6 +241,18 @@ enum MessageType { MessageType_TonSignedTx = 1503 [ (wire_out) = true ]; MessageType_TonSignMessage = 1504 [ (wire_in) = true ]; MessageType_TonMessageSignature = 1505 [ (wire_out) = true ]; + + // Hive + MessageType_HiveGetPublicKey = 1600 [ (wire_in) = true ]; + MessageType_HivePublicKey = 1601 [ (wire_out) = true ]; + MessageType_HiveSignTx = 1602 [ (wire_in) = true ]; + MessageType_HiveSignedTx = 1603 [ (wire_out) = true ]; + MessageType_HiveGetPublicKeys = 1604 [ (wire_in) = true ]; + MessageType_HivePublicKeys = 1605 [ (wire_out) = true ]; + MessageType_HiveSignAccountCreate = 1606 [ (wire_in) = true ]; + MessageType_HiveSignedAccountCreate = 1607 [ (wire_out) = true ]; + MessageType_HiveSignAccountUpdate = 1608 [ (wire_in) = true ]; + MessageType_HiveSignedAccountUpdate = 1609 [ (wire_out) = true ]; } //////////////////// diff --git a/package.json b/package.json index 51f6d535..9e82e419 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "scripts": { "clean": "rm -rf ./lib/*.js ./lib/*.ts", "build": "npm run build:js && npm run build:json && npm run build:postprocess", - "build:js": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-solana.proto messages-tron.proto messages-ton.proto messages-zcash.proto", - "build:json": "pbjs --keep-case -t json ./types.proto ./messages.proto ./messages-ethereum.proto ./messages-eos.proto ./messages-nano.proto ./messages-cosmos.proto ./messages-binance.proto ./messages-ripple.proto ./messages-tendermint.proto ./messages-thorchain.proto ./messages-osmosis.proto ./messages-mayachain.proto ./messages-solana.proto ./messages-tron.proto ./messages-ton.proto ./messages-zcash.proto > ./lib/proto.json", + "build:js": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-solana.proto messages-tron.proto messages-ton.proto messages-zcash.proto messages-hive.proto", + "build:json": "pbjs --keep-case -t json ./types.proto ./messages.proto ./messages-ethereum.proto ./messages-eos.proto ./messages-nano.proto ./messages-cosmos.proto ./messages-binance.proto ./messages-ripple.proto ./messages-tendermint.proto ./messages-thorchain.proto ./messages-osmosis.proto ./messages-mayachain.proto ./messages-solana.proto ./messages-tron.proto ./messages-ton.proto ./messages-zcash.proto ./messages-hive.proto > ./lib/proto.json", "build:postprocess": "find ./lib -name \"*.js\" -exec sed -i '' -e \"s/var global = Function(\\'return this\\')();/var global = (function(){ return this }).call(null);/g\" {} \\;", "prepublishOnly": "npm run build", "test": "echo \"Error: no test specified\" && exit 1" From 2ec999a9b2e5174da5981e85f66845a97cdaa877 Mon Sep 17 00:00:00 2001 From: highlander Date: Thu, 2 Jul 2026 02:10:27 -0500 Subject: [PATCH 19/45] =?UTF-8?q?feat:=20LoadClearsignSigner=20(117)=20?= =?UTF-8?q?=E2=80=94=20runtime=20clearsign=20signer=20with=20alias,=20user?= =?UTF-8?q?-confirmed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- messages-ethereum.options | 2 ++ messages-ethereum.proto | 16 ++++++++++++++++ messages.proto | 1 + 3 files changed, 19 insertions(+) diff --git a/messages-ethereum.options b/messages-ethereum.options index 1759d0ac..9cd03b4f 100644 --- a/messages-ethereum.options +++ b/messages-ethereum.options @@ -1,2 +1,4 @@ EthereumTxMetadata.signed_payload max_size:1024 EthereumMetadataAck.display_summary max_size:32 +LoadClearsignSigner.pubkey max_size:33 +LoadClearsignSigner.alias max_size:32 diff --git a/messages-ethereum.proto b/messages-ethereum.proto index b8719d34..40e7925e 100644 --- a/messages-ethereum.proto +++ b/messages-ethereum.proto @@ -113,6 +113,22 @@ message EthereumMetadataAck { optional string display_summary = 2; // Brief result for host logging } +/** + * Request: Load a clearsign signer public key + alias into a runtime key slot. + * The device shows a mandatory confirmation (alias + key fingerprint) before + * accepting; there is no way to load a signer without user consent. Loaded + * signers live in RAM only and are cleared on reboot. Every transaction whose + * metadata was verified by a loaded (non built-in) signer is preceded by a + * warning screen naming the alias during transaction confirmation. + * @next Success + * @next Failure + */ +message LoadClearsignSigner { + optional uint32 key_id = 1; // target key slot (0-3); must not hold a built-in key + optional bytes pubkey = 2; // 33-byte compressed secp256k1 public key + optional string alias = 3; // short display name shown on load confirm + per-tx warning +} + //////////////////////////////////////// // Ethereum: Message signing messages // //////////////////////////////////////// diff --git a/messages.proto b/messages.proto index 1a5466af..2f8e5b30 100644 --- a/messages.proto +++ b/messages.proto @@ -100,6 +100,7 @@ enum MessageType { // Ethereum Clear Signing MessageType_EthereumTxMetadata = 115 [ (wire_in) = true ]; MessageType_EthereumMetadataAck = 116 [ (wire_out) = true ]; + MessageType_LoadClearsignSigner = 117 [ (wire_in) = true ]; // BIP-85 MessageType_GetBip85Mnemonic = 120 [ (wire_in) = true ]; From 33521a8fd6f012c8bbca3a8902eea6c1f5aa3389 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 7 Jul 2026 14:50:04 -0300 Subject: [PATCH 20/45] feat(clearsign): identity icon + persist fields on LoadClearsignSigner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends msg 117 for the persistent-identity model ("KeepKey + identity"): - icon (bytes, <=384): 1bpp mono identity logo, shown on load-confirm, at the start of every clearsign it vouches for, and on boot. - icon_width / icon_height: icon dims (<=64). - persist (bool): store the identity in flash so it survives reboot; default RAM-only (backward compatible with current behavior). Proto + nanopb options only; the firmware handler + flash storage consume these in a follow-up. Fork only — never PR to keepkey/device-protocol. --- messages-ethereum.options | 1 + messages-ethereum.proto | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/messages-ethereum.options b/messages-ethereum.options index 9cd03b4f..8a1b3ff3 100644 --- a/messages-ethereum.options +++ b/messages-ethereum.options @@ -2,3 +2,4 @@ EthereumTxMetadata.signed_payload max_size:1024 EthereumMetadataAck.display_summary max_size:32 LoadClearsignSigner.pubkey max_size:33 LoadClearsignSigner.alias max_size:32 +LoadClearsignSigner.icon max_size:384 diff --git a/messages-ethereum.proto b/messages-ethereum.proto index 40e7925e..790fdc27 100644 --- a/messages-ethereum.proto +++ b/messages-ethereum.proto @@ -127,6 +127,10 @@ message LoadClearsignSigner { optional uint32 key_id = 1; // target key slot (0-3); must not hold a built-in key optional bytes pubkey = 2; // 33-byte compressed secp256k1 public key optional string alias = 3; // short display name shown on load confirm + per-tx warning + optional bytes icon = 4; // optional identity logo: 1bpp mono row-major bitmap, <= 384 bytes + optional uint32 icon_width = 5; // icon pixel width (<= 64; icon+dims omitted => text-only identity) + optional uint32 icon_height = 6; // icon pixel height (<= 64) + optional bool persist = 7; // store in flash so the identity survives reboot (shown on boot); default RAM-only } //////////////////////////////////////// From 9e46aeb6de95c3d12272bb6b48dcc06d40f1b436 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 14 Jul 2026 23:56:38 -0300 Subject: [PATCH 21/45] =?UTF-8?q?feat(hive):=20HiveSignMessage/HiveSignedM?= =?UTF-8?q?essage=20(1614/1615)=20=E2=80=94=20Keychain=20signBuffer=20cont?= =?UTF-8?q?ract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signature over SHA256(raw message bytes) only: no chain_id prepend, no message prefix. 1610-1613 skipped: NEAR holds them on master. --- messages-hive.options | 6 ++++++ messages-hive.proto | 27 +++++++++++++++++++++++++++ messages.proto | 3 +++ 3 files changed, 36 insertions(+) diff --git a/messages-hive.options b/messages-hive.options index e878f3e3..04ba85d6 100644 --- a/messages-hive.options +++ b/messages-hive.options @@ -44,3 +44,9 @@ HiveSignAccountUpdate.new_memo_key max_size:64 HiveSignedAccountUpdate.signature max_size:65 HiveSignedAccountUpdate.serialized_tx max_size:512 + +HiveSignMessage.address_n max_count:8 +HiveSignMessage.message max_size:1024 + +HiveSignedMessage.signature max_size:65 +HiveSignedMessage.public_key max_size:33 diff --git a/messages-hive.proto b/messages-hive.proto index 7612ec47..c08e2385 100644 --- a/messages-hive.proto +++ b/messages-hive.proto @@ -147,3 +147,30 @@ message HiveSignedAccountUpdate { optional bytes signature = 1; // 65-byte recoverable signature optional bytes serialized_tx = 2; // Serialized Graphene transaction bytes } + +/** + * Request: Sign an arbitrary message with a Hive role key. + * Implements the Hive Keychain requestSignBuffer contract (hive-js + * Signature.signBuffer): signature over SHA256(message bytes) — a single + * hash of the raw bytes only, NO chain_id prepend (unlike transactions) + * and NO Bitcoin/Solana-style message prefix. This is the Hive dApp login + * primitive (Aioha / Keychain SDK). + * Signing key: any SLIP-0048 role; dApp login uses posting + * (m/48'/13'/4'/account'/0'). + * @start + * @next HiveSignedMessage + * @next Failure + */ +message HiveSignMessage { + repeated uint32 address_n = 1; // Full SLIP-0048 path of signing key + optional bytes message = 2; // Raw message bytes (max 1024) +} + +/** + * Response: Signed Hive message + * @end + */ +message HiveSignedMessage { + optional bytes signature = 1; // 65-byte recoverable secp256k1 signature (27+recid+4, r, s) + optional bytes public_key = 2; // 33-byte compressed public key of the signing key +} diff --git a/messages.proto b/messages.proto index 2f8e5b30..f7ad68ad 100644 --- a/messages.proto +++ b/messages.proto @@ -254,6 +254,9 @@ enum MessageType { MessageType_HiveSignedAccountCreate = 1607 [ (wire_out) = true ]; MessageType_HiveSignAccountUpdate = 1608 [ (wire_in) = true ]; MessageType_HiveSignedAccountUpdate = 1609 [ (wire_out) = true ]; + // 1610-1613 reserved: NEAR (NearGetAddress..NearSignedTx) on master + MessageType_HiveSignMessage = 1614 [ (wire_in) = true ]; + MessageType_HiveSignedMessage = 1615 [ (wire_out) = true ]; } //////////////////// From a793934d09a883c9944ba3b9da15d23969906343 Mon Sep 17 00:00:00 2001 From: highlander Date: Wed, 15 Jul 2026 00:46:17 -0300 Subject: [PATCH 22/45] =?UTF-8?q?docs(hive):=20HiveSignMessage=20roles=20a?= =?UTF-8?q?re=20posting/active/memo=20=E2=80=94=20owner'=20rejected?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- messages-hive.proto | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messages-hive.proto b/messages-hive.proto index c08e2385..853134e8 100644 --- a/messages-hive.proto +++ b/messages-hive.proto @@ -155,7 +155,8 @@ message HiveSignedAccountUpdate { * hash of the raw bytes only, NO chain_id prepend (unlike transactions) * and NO Bitcoin/Solana-style message prefix. This is the Hive dApp login * primitive (Aioha / Keychain SDK). - * Signing key: any SLIP-0048 role; dApp login uses posting + * Signing key: posting/active/memo role (Keychain's requestSignBuffer + * surface — owner' is rejected); dApp login uses posting * (m/48'/13'/4'/account'/0'). * @start * @next HiveSignedMessage From f0b454981e093ac4f7c157281ed5cc1bfa395f73 Mon Sep 17 00:00:00 2001 From: highlander Date: Wed, 15 Jul 2026 12:42:47 -0300 Subject: [PATCH 23/45] =?UTF-8?q?feat(hive):=20HiveSignOperations/HiveSign?= =?UTF-8?q?edOperations=20(1616/1617)=20=E2=80=94=20parsed=20generic=20op?= =?UTF-8?q?=20signing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Host serializes; firmware parses the Graphene bytes and clear-signs the phase-1 op table (vote 0, comment 1, custom_json 18). Op types 2/9/10 are permanently excluded — dedicated messages keep their stronger invariants. --- messages-hive.options | 6 ++++++ messages-hive.proto | 29 +++++++++++++++++++++++++++++ messages.proto | 2 ++ 3 files changed, 37 insertions(+) diff --git a/messages-hive.options b/messages-hive.options index 04ba85d6..d39d5921 100644 --- a/messages-hive.options +++ b/messages-hive.options @@ -50,3 +50,9 @@ HiveSignMessage.message max_size:1024 HiveSignedMessage.signature max_size:65 HiveSignedMessage.public_key max_size:33 + +HiveSignOperations.address_n max_count:8 +HiveSignOperations.chain_id max_size:32 +HiveSignOperations.serialized_tx max_size:2048 + +HiveSignedOperations.signature max_size:65 diff --git a/messages-hive.proto b/messages-hive.proto index 853134e8..1566d5fb 100644 --- a/messages-hive.proto +++ b/messages-hive.proto @@ -175,3 +175,32 @@ message HiveSignedMessage { optional bytes signature = 1; // 65-byte recoverable secp256k1 signature (27+recid+4, r, s) optional bytes public_key = 2; // 33-byte compressed public key of the signing key } + +/** + * Request: Sign a host-serialized Graphene transaction after parsing and + * clear-signing every operation in it. The firmware re-derives everything it + * displays from the bytes themselves; transactions containing operations + * outside the supported table are refused (no blind-sign fallback). + * Phase-1 table: vote (0), comment (1), custom_json (18). + * Op types 2/9/10 (transfer, account_create, account_update) are PERMANENTLY + * excluded — they keep their dedicated message types and invariants. + * Signing key: posting (m/48'/13'/4'/account'/0') for posting-tier txs, + * active (m/48'/13'/1'/account'/0') when custom_json carries required_auths. + * Digest: SHA256(chain_id || serialized_tx), same as HiveSignTx. + * @start + * @next HiveSignedOperations + * @next Failure + */ +message HiveSignOperations { + repeated uint32 address_n = 1; // Full SLIP-0048 path of signing key + optional bytes chain_id = 2; // 32-byte chain ID (mainnet = beeab0de...) + optional bytes serialized_tx = 3; // Graphene tx bytes: header..extensions, NO chain_id prefix (max 2048) +} + +/** + * Response: Signed Hive operations transaction + * @end + */ +message HiveSignedOperations { + optional bytes signature = 1; // 65-byte recoverable secp256k1 signature (27+recid+4, r, s) +} diff --git a/messages.proto b/messages.proto index f7ad68ad..93b318e6 100644 --- a/messages.proto +++ b/messages.proto @@ -257,6 +257,8 @@ enum MessageType { // 1610-1613 reserved: NEAR (NearGetAddress..NearSignedTx) on master MessageType_HiveSignMessage = 1614 [ (wire_in) = true ]; MessageType_HiveSignedMessage = 1615 [ (wire_out) = true ]; + MessageType_HiveSignOperations = 1616 [ (wire_in) = true ]; + MessageType_HiveSignedOperations = 1617 [ (wire_out) = true ]; } //////////////////// From f7b458078cf9249ac706bd1089f109a5a2ea8696 Mon Sep 17 00:00:00 2001 From: highlander Date: Thu, 16 Jul 2026 17:47:54 -0300 Subject: [PATCH 24/45] =?UTF-8?q?fix(clearsign):=20specify=20the=20icon=20?= =?UTF-8?q?RLE=20wire=20grammar=20=E2=80=94=20the=20packed-bitmap=20doc=20?= =?UTF-8?q?was=20wrong?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LoadClearsignSigner.icon was documented as a "1bpp mono row-major bitmap", but firmware feeds it to draw_bitmap_mono_rle() (lib/board/draw.c), which reads signed int8 RLE packets and byte-valued pixels. A client following the protobuf comment would ship packed bits into an RLE decoder and render a garbled or missing identity logo on a trust screen. The RLE is intentional, not the doc: firmware permits icon_width/icon_height up to 64, but a packed 1bpp 64x64 needs 512 bytes and the cap is 384 — the documented format is arithmetically impossible at the dimensions the firmware itself accepts. So specify the grammar rather than change the decoder. Documents the exact packet grammar (RUN [n][v] for n>0, LITERAL [n][v1..v-n] for n<0, n==0 invalid, row-major fill, int8 bounds), states that pixels are byte-valued intensity rendered as value*color/100 (not a 1-bit mask), names draw_bitmap_mono_rle() as the decoder of record, and adds a golden vector (03 FF FF 00, w=2 h=2 -> FF FF FF 00) verified against that decoder. Comment-only: no field, number, or wire behaviour changes. --- messages-ethereum.proto | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/messages-ethereum.proto b/messages-ethereum.proto index 790fdc27..db64824d 100644 --- a/messages-ethereum.proto +++ b/messages-ethereum.proto @@ -127,7 +127,28 @@ message LoadClearsignSigner { optional uint32 key_id = 1; // target key slot (0-3); must not hold a built-in key optional bytes pubkey = 2; // 33-byte compressed secp256k1 public key optional string alias = 3; // short display name shown on load confirm + per-tx warning - optional bytes icon = 4; // optional identity logo: 1bpp mono row-major bitmap, <= 384 bytes + /* + * Optional identity logo, <= 384 bytes, run-length encoded (NOT a packed + * bitmap — a packed 1bpp 64x64 would need 512 bytes and cannot fit the cap). + * Pixels are BYTE-VALUED intensity, one byte per pixel after decoding; the + * device renders each as (value * color / 100). Decoder of record: + * keepkey-firmware lib/board/draw.c: draw_bitmap_mono_rle(). + * + * Grammar — the stream is a sequence of packets. Read n = (int8)data[i++]: + * n > 0 RUN : one value byte follows; emit it n times. [n][v] + * n < 0 LITERAL : (-n) value bytes follow; emit each once. [n][v1]..[v-n] + * n == 0 : invalid. + * Runs may not straddle the end of the image. Packets are decoded until + * exactly icon_width*icon_height pixels have been emitted, filling row-major + * (left->right, top->bottom). n is a signed 8-bit value, so a RUN emits at + * most 127 pixels and a LITERAL at most 128. + * + * Golden vector (2x2, w=2 h=2): bytes 03 FF FF 00 + * 03 -> RUN of 3, value FF => pixels [FF, FF, FF] + * FF -> n = -1, LITERAL of 1 => next byte 00 => pixel [00] + * decoded = FF FF FF 00 (row0 = FF FF, row1 = FF 00) + */ + optional bytes icon = 4; optional uint32 icon_width = 5; // icon pixel width (<= 64; icon+dims omitted => text-only identity) optional uint32 icon_height = 6; // icon pixel height (<= 64) optional bool persist = 7; // store in flash so the identity survives reboot (shown on boot); default RAM-only From 7182973919e88ac49cc219f30f17ec17488f9fde Mon Sep 17 00:00:00 2001 From: highlander Date: Thu, 16 Jul 2026 18:10:15 -0300 Subject: [PATCH 25/45] =?UTF-8?q?fix(clearsign):=20icon=20spec=20was=20uns?= =?UTF-8?q?afe=20=E2=80=94=200x80=20literal=20undecodable,=20width=20cap?= =?UTF-8?q?=20allows=20text=20overwrite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two contract errors found in review of the RLE grammar added in f7b4580. 1) The doc claimed a LITERAL may carry up to 128 bytes, i.e. n = -128 (0x80). The decoder's counter is `int8_t nonsequence` and it computes `nonsequence = -sequence`, so -(-128) = 128 does not fit and wraps back to -128 — failing the `nonsequence > 0` invariant. Under NDEBUG (release) the assert is compiled out and decoding proceeds with a negative counter (signed-overflow UB); on debug/emulator builds it asserts. The load handler validates only length and dimensions, so a spec-valid icon could fault the device during its own mandatory confirmation. Restrict to [-127,-1] and mark 0x80 explicitly invalid; encoders split a 128-byte literal in two. 2) icon_width was documented (and enforced) as <= 64, but the confirm screen's icon column is LEFT_MARGIN_WITH_ICON = 40 and title/body text starts at x=40. stage_runtime_icon() places any icon wider than the column at x=0 ("would clip the title/body" — its own comment), and confirm_sm draws the icon AFTER the text. So a host-supplied 64px icon overwrites 24 columns of the alias, fingerprint and the "NOT verified by KeepKey" warning — on the screen whose entire purpose is that warning. Cap width at 40. Firmware must enforce both independently — a hostile host is not bound by this comment. Companion firmware change rejects 0x80 in the decoder and caps icon_width at 40 in fsm_msgLoadClearsignSigner. Comment-only: no field, number, or wire behaviour changes. --- messages-ethereum.proto | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/messages-ethereum.proto b/messages-ethereum.proto index db64824d..530a5284 100644 --- a/messages-ethereum.proto +++ b/messages-ethereum.proto @@ -135,13 +135,18 @@ message LoadClearsignSigner { * keepkey-firmware lib/board/draw.c: draw_bitmap_mono_rle(). * * Grammar — the stream is a sequence of packets. Read n = (int8)data[i++]: - * n > 0 RUN : one value byte follows; emit it n times. [n][v] - * n < 0 LITERAL : (-n) value bytes follow; emit each once. [n][v1]..[v-n] - * n == 0 : invalid. + * n in [1, 127] RUN : one value byte follows; emit it n times. [n][v] + * n in [-127, -1] LITERAL : (-n) value bytes follow; emit each once. [n][v1]..[v-n] + * n == 0 : invalid. + * n == -128 (0x80) : INVALID. The decoder's run counter is int8_t, + * so it cannot represent -(-128) = 128; a 0x80 + * packet is undecodable. Encoders MUST split a + * 128-byte literal into two packets. Firmware + * rejects 0x80 rather than rendering. * Runs may not straddle the end of the image. Packets are decoded until * exactly icon_width*icon_height pixels have been emitted, filling row-major - * (left->right, top->bottom). n is a signed 8-bit value, so a RUN emits at - * most 127 pixels and a LITERAL at most 128. + * (left->right, top->bottom). So a RUN emits at most 127 pixels and a + * LITERAL at most 127. * * Golden vector (2x2, w=2 h=2): bytes 03 FF FF 00 * 03 -> RUN of 3, value FF => pixels [FF, FF, FF] @@ -149,8 +154,15 @@ message LoadClearsignSigner { * decoded = FF FF FF 00 (row0 = FF FF, row1 = FF 00) */ optional bytes icon = 4; - optional uint32 icon_width = 5; // icon pixel width (<= 64; icon+dims omitted => text-only identity) - optional uint32 icon_height = 6; // icon pixel height (<= 64) + /* + * Icon pixel width, 1..40. The cap is the confirm screen's left icon column + * (LEFT_MARGIN_WITH_ICON = 40); title/body text begins at x=40 and the icon + * is drawn AFTER the text, so a wider icon would paint over the alias, + * fingerprint and the "NOT verified by KeepKey" warning on the trust screen. + * Icon + both dimensions omitted => text-only identity. + */ + optional uint32 icon_width = 5; + optional uint32 icon_height = 6; // icon pixel height (1..64; the icon column is 64px tall) optional bool persist = 7; // store in flash so the identity survives reboot (shown on boot); default RAM-only } From 4eb7d5e4f30ab75930df59cb19e40500217a68e8 Mon Sep 17 00:00:00 2001 From: highlander Date: Thu, 16 Jul 2026 19:21:30 -0300 Subject: [PATCH 26/45] fix(clearsign): drop the obsolete packed-size rationale; correct the RAM-only claim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two contract errors found in review. 1) The "a packed 1bpp 64x64 would need 512 bytes and cannot fit the cap" rationale is obsolete and misleading. Once icon_width was capped at 40, that arithmetic stopped applying: a packed icon at the LEGAL maximum geometry (40x64) is 320 bytes and WOULD fit the 384-byte cap. RLE is the format because draw_bitmap_mono_rle() is the decoder of record and every bundled image already uses it — not because packed wouldn't fit. Say that instead. 2) "Loaded signers live in RAM only and are cleared on reboot" contradicts persist=7 in the same message, which writes the identity to flash to survive reboot. State the actual behaviour: RAM-only by default, durable with persist=true. Also documents the exactness rule the firmware now enforces (no run straddling the image, entire input consumed), so encoders learn it from the spec rather than from a silently-missing logo. Comment-only: no field, number, or wire behaviour changes. --- messages-ethereum.proto | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/messages-ethereum.proto b/messages-ethereum.proto index 530a5284..6d8144d5 100644 --- a/messages-ethereum.proto +++ b/messages-ethereum.proto @@ -114,12 +114,14 @@ message EthereumMetadataAck { } /** - * Request: Load a clearsign signer public key + alias into a runtime key slot. + * Request: Load a clearsign signer public key + alias into a key slot. * The device shows a mandatory confirmation (alias + key fingerprint) before - * accepting; there is no way to load a signer without user consent. Loaded - * signers live in RAM only and are cleared on reboot. Every transaction whose - * metadata was verified by a loaded (non built-in) signer is preceded by a - * warning screen naming the alias during transaction confirmation. + * accepting; there is no way to load a signer without user consent. + * A signer is RAM-only by default and is cleared on reboot; set persist=true + * to also write it to flash, where it survives reboot and is reloaded + * automatically. Every transaction whose metadata was verified by a loaded + * (non built-in) signer is preceded by a warning screen naming the alias + * during transaction confirmation. * @next Success * @next Failure */ @@ -128,8 +130,11 @@ message LoadClearsignSigner { optional bytes pubkey = 2; // 33-byte compressed secp256k1 public key optional string alias = 3; // short display name shown on load confirm + per-tx warning /* - * Optional identity logo, <= 384 bytes, run-length encoded (NOT a packed - * bitmap — a packed 1bpp 64x64 would need 512 bytes and cannot fit the cap). + * Optional identity logo, <= 384 bytes, run-length encoded — NOT a packed + * bitmap. (This is the format because draw_bitmap_mono_rle() is the decoder + * of record and every bundled image already uses it; it is NOT a size + * workaround — a packed 1bpp icon at the legal maximum geometry, 40x64, + * would be 320 bytes and would fit the cap.) * Pixels are BYTE-VALUED intensity, one byte per pixel after decoding; the * device renders each as (value * color / 100). Decoder of record: * keepkey-firmware lib/board/draw.c: draw_bitmap_mono_rle(). @@ -143,10 +148,12 @@ message LoadClearsignSigner { * packet is undecodable. Encoders MUST split a * 128-byte literal into two packets. Firmware * rejects 0x80 rather than rendering. - * Runs may not straddle the end of the image. Packets are decoded until - * exactly icon_width*icon_height pixels have been emitted, filling row-major - * (left->right, top->bottom). So a RUN emits at most 127 pixels and a - * LITERAL at most 127. + * The stream must decode EXACTLY, and the device validates this before the + * icon is shown or stored: no run may straddle the end of the image, + * exactly icon_width*icon_height pixels are emitted (row-major, + * left->right, top->bottom), and the ENTIRE input must be consumed — + * trailing packets after the final pixel are rejected. So a RUN emits at + * most 127 pixels and a LITERAL at most 127. * * Golden vector (2x2, w=2 h=2): bytes 03 FF FF 00 * 03 -> RUN of 3, value FF => pixels [FF, FF, FF] From 47e19d8b0816db20e15d9b1e27da83c70d5ed88d Mon Sep 17 00:00:00 2001 From: highlander Date: Fri, 17 Jul 2026 17:57:22 -0300 Subject: [PATCH 27/45] feat(solana): optional signed token-definition fields on SolanaTokenInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add optional signature + signer_key_id so a host can attest a token's mint->symbol mapping with a clear-sign signer key (LoadClearsignSigner). The firmware verifies; producing these signatures is a follow-up. Backward compatible — existing hosts set neither field. --- messages-solana.proto | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/messages-solana.proto b/messages-solana.proto index a8f4eea2..c169817e 100644 --- a/messages-solana.proto +++ b/messages-solana.proto @@ -37,6 +37,13 @@ message SolanaTokenInfo { optional bytes mint = 1; // 32-byte mint public key optional string symbol = 2; // Token symbol e.g. "USDC" (max 12) optional uint32 decimals = 3; // Token decimals e.g. 6 + // Optional attestation: ECDSA(secp256k1) signature over a domain-separated + // digest of (mint, decimals, symbol), signed by a clear-sign signer the + // user loaded via LoadClearsignSigner. When present and valid, the device + // trusts the symbol; when absent it falls back to displaying the raw mint. + // (Firmware verifies; the host/SDK signing side is a follow-up.) + optional bytes signature = 4; // 64-byte compact ECDSA signature + optional uint32 signer_key_id = 5; // which loaded clear-sign signer } /** From e31cddfe7f5c72c983d06a889ac7db649b9811df Mon Sep 17 00:00:00 2001 From: highlander Date: Sat, 25 Jul 2026 16:15:16 -0300 Subject: [PATCH 28/45] docs(clearsign): reserve persistence pending authenticated storage --- messages-ethereum.proto | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/messages-ethereum.proto b/messages-ethereum.proto index 6d8144d5..e69010a3 100644 --- a/messages-ethereum.proto +++ b/messages-ethereum.proto @@ -117,9 +117,9 @@ message EthereumMetadataAck { * Request: Load a clearsign signer public key + alias into a key slot. * The device shows a mandatory confirmation (alias + key fingerprint) before * accepting; there is no way to load a signer without user consent. - * A signer is RAM-only by default and is cleared on reboot; set persist=true - * to also write it to flash, where it survives reboot and is reloaded - * automatically. Every transaction whose metadata was verified by a loaded + * A signer is RAM-only and is cleared on reboot. The persist field is retained + * for wire compatibility and future authenticated storage; firmware 7.15 + * rejects persist=true. Every transaction whose metadata was verified by a loaded * (non built-in) signer is preceded by a warning screen naming the alias * during transaction confirmation. * @next Success @@ -170,7 +170,7 @@ message LoadClearsignSigner { */ optional uint32 icon_width = 5; optional uint32 icon_height = 6; // icon pixel height (1..64; the icon column is 64px tall) - optional bool persist = 7; // store in flash so the identity survives reboot (shown on boot); default RAM-only + optional bool persist = 7; // reserved for future authenticated persistence; firmware 7.15 rejects true } //////////////////////////////////////// From 6d0ae670e287a75338244fe82c4bef33a920a2ee Mon Sep 17 00:00:00 2001 From: highlander Date: Sun, 26 Jul 2026 14:47:27 -0300 Subject: [PATCH 29/45] test(zcash): pin RC18 compact-signature contract --- .github/workflows/ci.yml | 35 ++++++++++++++++++ messages-zcash.proto | 14 ++++--- tools/check_zcash_contract.py | 70 +++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 tools/check_zcash_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..e99fc06f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: Protocol CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + + - name: Install protobuf compiler + run: sudo apt-get update && sudo apt-get install -y protobuf-compiler + + - name: Compile protocol descriptors + run: | + protoc --proto_path=. --include_imports \ + --descriptor_set_out=/tmp/keepkey-device-protocol.pb \ + types.proto messages.proto messages-binance.proto \ + messages-cosmos.proto messages-eos.proto messages-ethereum.proto \ + messages-hive.proto messages-mayachain.proto messages-nano.proto \ + messages-osmosis.proto messages-ripple.proto messages-solana.proto \ + messages-tendermint.proto messages-thorchain.proto messages-ton.proto \ + messages-tron.proto messages-zcash.proto + + - name: Check RC18 Zcash wire contract + run: python3 tools/check_zcash_contract.py diff --git a/messages-zcash.proto b/messages-zcash.proto index c9659dd3..0be76f47 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -15,8 +15,9 @@ option java_outer_classname = "KeepKeyMessageZcash"; * Request: Sign a Zcash shielded transaction (PCZT format) * * The PCZT contains pre-constructed transaction data with proofs. - * The device derives spend authorization keys, computes the sighash, - * and returns RedPallas signatures for each Orchard action. + * The device derives spend authorization keys, computes the sighash, validates + * every Orchard action, and returns compact RedPallas signatures only for + * actions explicitly marked is_spend=true. * * @next ZcashTransparentAck * @next ZcashPCZTActionAck @@ -26,7 +27,7 @@ message ZcashSignPCZT { repeated uint32 address_n = 1; // ZIP-32 derivation path [32', 133', account'] optional uint32 account = 2; // Account index (alternative to full path) optional bytes pczt_data = 3; // Serialized PCZT data (may be chunked) - optional uint32 n_actions = 4; // Number of Orchard actions to sign + optional uint32 n_actions = 4; // Number of Orchard actions to stream and validate optional uint64 total_amount = 5; // Total ZEC amount (zatoshis) for user confirmation optional uint64 fee = 6; // Transaction fee (zatoshis) optional uint32 branch_id = 7; // Consensus branch ID @@ -69,7 +70,8 @@ message ZcashPCZTAction { optional bytes sighash = 3; // 32-byte transaction sighash (ZIP 244) - legacy mode optional bytes cv_net = 4; // 32-byte value commitment optional uint64 value = 5; // Action value in zatoshis (for display) - optional bool is_spend = 6; // True if this action spends a note + optional bool is_spend = 6; // REQUIRED by firmware 7.15: true only for a real spend; + // false for dummy spends/output-only actions // Phase 2b: action fields for incremental orchard digest verification optional bytes nullifier = 7; // 32-byte nullifier optional bytes cmx = 8; // 32-byte note commitment @@ -102,7 +104,9 @@ message ZcashPCZTActionAck { * @prev ZcashPCZTAction */ message ZcashSignedPCZT { - repeated bytes signatures = 1; // 64-byte RedPallas signatures, one per action + repeated bytes signatures = 1; // Compact 64-byte RedPallas signatures: one per + // is_spend=true action, in ascending action-index order; + // an all-dummy shield transaction returns zero optional bytes txid = 2; // 32-byte computed transaction ID } diff --git a/tools/check_zcash_contract.py b/tools/check_zcash_contract.py new file mode 100644 index 00000000..bc64d57c --- /dev/null +++ b/tools/check_zcash_contract.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +"""Pin the RC18 Zcash wire identifiers and compact-signature contract.""" + +import re +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +ZCASH = (ROOT / "messages-zcash.proto").read_text() +MESSAGES = (ROOT / "messages.proto").read_text() + + +def message_body(name): + match = re.search(r"message\s+%s\s*\{(.*?)\n\}" % name, ZCASH, re.S) + if not match: + raise AssertionError("missing message %s" % name) + return match.group(1) + + +def require_field(message, declaration): + body = message_body(message) + if not re.search(r"^\s*%s\s*(?://.*)?$" % declaration, body, re.M): + raise AssertionError("%s is missing field contract: %s" % (message, declaration)) + + +FIELDS = [ + ("ZcashSignPCZT", r"optional uint32 n_actions = 4;"), + ("ZcashSignPCZT", r"optional uint32 n_transparent_outputs = 29;"), + ("ZcashSignPCZT", r"optional uint32 n_transparent_inputs = 30;"), + ("ZcashSignPCZT", r"optional bytes expected_seed_fingerprint = 31;"), + ("ZcashPCZTAction", r"optional bool is_spend = 6;"), + ("ZcashPCZTAction", r"optional bytes recipient = 15;"), + ("ZcashPCZTAction", r"optional bytes rseed = 16;"), + ("ZcashSignedPCZT", r"repeated bytes signatures = 1;"), + ("ZcashTransparentOutput", r"required uint32 index = 1;"), + ("ZcashTransparentInput", r"required uint32 index = 1;"), +] + +for field in FIELDS: + require_field(*field) + + +MESSAGE_IDS = { + "ZcashSignPCZT": (1300, "wire_in"), + "ZcashPCZTAction": (1301, "wire_in"), + "ZcashPCZTActionAck": (1302, "wire_out"), + "ZcashSignedPCZT": (1303, "wire_out"), + "ZcashGetOrchardFVK": (1304, "wire_in"), + "ZcashOrchardFVK": (1305, "wire_out"), + "ZcashTransparentInput": (1306, "wire_in"), + "ZcashTransparentSigned": (1307, "wire_out"), + "ZcashDisplayAddress": (1308, "wire_in"), + "ZcashAddress": (1309, "wire_out"), + "ZcashTransparentOutput": (1310, "wire_in"), + "ZcashTransparentAck": (1311, "wire_out"), +} + +for name, (number, direction) in MESSAGE_IDS.items(): + pattern = ( + r"MessageType_%s\s*=\s*%d\s*\[\s*\(%s\)\s*=\s*true\s*\]\s*;" + % (name, number, direction) + ) + if not re.search(pattern, MESSAGES): + raise AssertionError("wrong message ID or wire direction for %s" % name) + + +if not re.search(r"one per\s*// is_spend=true action", ZCASH): + raise AssertionError("ZcashSignedPCZT must document compact real-spend signatures") + +print("RC18 Zcash protocol contract: ok") From 41c59abb9393f06d9199392f2c081b346e5b3e35 Mon Sep 17 00:00:00 2001 From: highlander Date: Mon, 27 Jul 2026 01:16:00 -0300 Subject: [PATCH 30/45] feat(solana): reusable instruction-schema fields on SolanaSignTx --- messages-solana.options | 2 ++ messages-solana.proto | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/messages-solana.options b/messages-solana.options index ee812da1..510df1cf 100644 --- a/messages-solana.options +++ b/messages-solana.options @@ -4,6 +4,8 @@ SolanaSignTx.address_n max_count:8 SolanaSignTx.coin_name max_size:21 SolanaSignTx.raw_tx max_size:1232 SolanaSignTx.token_info max_count:4 +SolanaSignTx.schema_payload max_size:256 +SolanaSignTx.schema_signature max_size:64 SolanaTokenInfo.mint max_size:32 SolanaTokenInfo.symbol max_size:13 SolanaAddress.address max_size:45 diff --git a/messages-solana.proto b/messages-solana.proto index c169817e..5f404e8f 100644 --- a/messages-solana.proto +++ b/messages-solana.proto @@ -56,6 +56,22 @@ message SolanaSignTx { optional string coin_name = 2 [default = "Solana"]; optional bytes raw_tx = 3; // Serialized Solana transaction bytes repeated SolanaTokenInfo token_info = 4; // Token metadata for display (max 4) + /* + * KKSOLSC1 instruction schema (see solana.h). A schema describes how to + * read ONE program instruction: program id, discriminator, and the + * labelled args/accounts to display. It carries no amounts and no + * transaction hash, so a signer attests it ONCE per program+instruction + * and every later transaction reuses it — the device decodes the values + * out of the raw_tx bytes it is about to sign. + * + * Safety comes from structural completeness rather than binding to one + * transaction: firmware requires the schema to account for the + * instruction data exactly, and every other instruction in the + * transaction to be a program it already recognises. + */ + optional bytes schema_payload = 5; + optional bytes schema_signature = 6; // 64-byte compact secp256k1 over SHA256(payload) + optional uint32 schema_signer_key_id = 7; // trusted clearsign signer slot (0-3) } /** From 844a9b970e334fc556caa1ad4f1f225280d36801 Mon Sep 17 00:00:00 2001 From: highlander Date: Sun, 26 Jul 2026 21:47:52 -0300 Subject: [PATCH 31/45] feat(clearsign): attestor messages (1700-1703) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClearsignAttestorGetPublicKey/PublicKey provision the seed-derived attestation key; ClearsignAttestorSign/Signature validate a canonical descriptor payload on-device before attesting it with plain secp256k1 ECDSA over SHA256(payload) — the format signed_metadata_verify_attestation checks on verifying devices. --- messages.proto | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/messages.proto b/messages.proto index 93b318e6..f8a7bafe 100644 --- a/messages.proto +++ b/messages.proto @@ -259,6 +259,12 @@ enum MessageType { MessageType_HiveSignedMessage = 1615 [ (wire_out) = true ]; MessageType_HiveSignOperations = 1616 [ (wire_in) = true ]; MessageType_HiveSignedOperations = 1617 [ (wire_out) = true ]; + + // Clearsign attestor (server/emulator attestor tier) + MessageType_ClearsignAttestorGetPublicKey = 1700 [ (wire_in) = true ]; + MessageType_ClearsignAttestorPublicKey = 1701 [ (wire_out) = true ]; + MessageType_ClearsignAttestorSign = 1702 [ (wire_in) = true ]; + MessageType_ClearsignAttestorSignature = 1703 [ (wire_out) = true ]; } //////////////////// @@ -1040,3 +1046,48 @@ message DebugLinkFillConfig {} message ChangeWipeCode { optional bool remove = 1; // is wipe code removal requested? } + +////////////////////////// +// Clearsign attestor // +////////////////////////// + +/** + * Request: derive and return the clearsign attestation public key. + * The attestation key is derived from the seed at a dedicated hardened path + * and is unrelated to any coin key. Used to provision verifying devices + * (METADATA_PUBKEYS baking, or LoadClearsignSigner for test slots) and to + * let hosts confirm which attestor they are talking to. + * @next ClearsignAttestorPublicKey + * @next Failure + */ +message ClearsignAttestorGetPublicKey {} + +/** + * Response: compressed attestation public key + * @prev ClearsignAttestorGetPublicKey + */ +message ClearsignAttestorPublicKey { + optional bytes public_key = 1; // 33-byte compressed secp256k1 +} + +/** + * Request: validate a clearsign descriptor payload and attest it. + * The device parses the payload with the SAME validator verifying devices + * run (currently KKSOLSW1 cross-chain swap descriptors) and refuses + * anything malformed — this message can never sign arbitrary bytes. + * Requires an unlocked session and an on-device confirmation. + * @next ClearsignAttestorSignature + * @next Failure + */ +message ClearsignAttestorSign { + optional bytes payload = 1; // canonical descriptor payload (magic-prefixed) +} + +/** + * Response: attestation over the validated payload + * @prev ClearsignAttestorSign + */ +message ClearsignAttestorSignature { + optional bytes signature = 1; // 64-byte compact secp256k1 ECDSA over SHA256(payload) + optional bytes public_key = 2; // 33-byte compressed attestation pubkey +} From 81c398d868781971daf5443796546d4226501c98 Mon Sep 17 00:00:00 2001 From: highlander Date: Wed, 29 Jul 2026 02:02:04 -0300 Subject: [PATCH 32/45] docs(clearsign): attestor validates KKSOLSC1, not the abandoned KKSOLSW1 --- messages.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages.proto b/messages.proto index f8a7bafe..9b688a8e 100644 --- a/messages.proto +++ b/messages.proto @@ -1073,7 +1073,7 @@ message ClearsignAttestorPublicKey { /** * Request: validate a clearsign descriptor payload and attest it. * The device parses the payload with the SAME validator verifying devices - * run (currently KKSOLSW1 cross-chain swap descriptors) and refuses + * run (currently KKSOLSC1 reusable Solana instruction schemas) and refuses * anything malformed — this message can never sign arbitrary bytes. * Requires an unlocked session and an on-device confirmation. * @next ClearsignAttestorSignature From 8856334eaad4cb8af67fab5ba62fc146543f79b1 Mon Sep 17 00:00:00 2001 From: highlander Date: Wed, 29 Jul 2026 18:37:34 -0300 Subject: [PATCH 33/45] fix(clearsign): reserve additive schema wire fields --- messages-ethereum.proto | 2 +- messages-solana.proto | 10 ++++++---- messages.proto | 7 +++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/messages-ethereum.proto b/messages-ethereum.proto index e69010a3..f04424f5 100644 --- a/messages-ethereum.proto +++ b/messages-ethereum.proto @@ -170,7 +170,7 @@ message LoadClearsignSigner { */ optional uint32 icon_width = 5; optional uint32 icon_height = 6; // icon pixel height (1..64; the icon column is 64px tall) - optional bool persist = 7; // reserved for future authenticated persistence; firmware 7.15 rejects true + optional bool persist = 7; // reserved compatibility field; firmware rejects true (RAM-only) } //////////////////////////////////////// diff --git a/messages-solana.proto b/messages-solana.proto index 5f404e8f..15b7647f 100644 --- a/messages-solana.proto +++ b/messages-solana.proto @@ -67,11 +67,13 @@ message SolanaSignTx { * Safety comes from structural completeness rather than binding to one * transaction: firmware requires the schema to account for the * instruction data exactly, and every other instruction in the - * transaction to be a program it already recognises. + * transaction to be a program it already recognises. Runtime-loaded + * signers are annotation-only, so the normal Advanced-mode unverified + * transaction warning remains additive. */ - optional bytes schema_payload = 5; - optional bytes schema_signature = 6; // 64-byte compact secp256k1 over SHA256(payload) - optional uint32 schema_signer_key_id = 7; // trusted clearsign signer slot (0-3) + optional bytes schema_payload = 9; + optional bytes schema_signature = 10; // 64-byte compact secp256k1 over SHA256(payload) + optional uint32 schema_signer_key_id = 11; // trusted clearsign signer slot (0-3) } /** diff --git a/messages.proto b/messages.proto index 9b688a8e..1cf8bbb4 100644 --- a/messages.proto +++ b/messages.proto @@ -260,7 +260,7 @@ enum MessageType { MessageType_HiveSignOperations = 1616 [ (wire_in) = true ]; MessageType_HiveSignedOperations = 1617 [ (wire_out) = true ]; - // Clearsign attestor (server/emulator attestor tier) + // Advanced-mode ClearSign studio / schema attestation MessageType_ClearsignAttestorGetPublicKey = 1700 [ (wire_in) = true ]; MessageType_ClearsignAttestorPublicKey = 1701 [ (wire_out) = true ]; MessageType_ClearsignAttestorSign = 1702 [ (wire_in) = true ]; @@ -1054,9 +1054,8 @@ message ChangeWipeCode { /** * Request: derive and return the clearsign attestation public key. * The attestation key is derived from the seed at a dedicated hardened path - * and is unrelated to any coin key. Used to provision verifying devices - * (METADATA_PUBKEYS baking, or LoadClearsignSigner for test slots) and to - * let hosts confirm which attestor they are talking to. + * and is unrelated to any coin key. Available in regular firmware only while + * AdvancedMode is enabled. * @next ClearsignAttestorPublicKey * @next Failure */ From 4cc8b717517c79ee3ac436161141dd033db286fd Mon Sep 17 00:00:00 2001 From: highlander Date: Wed, 29 Jul 2026 23:53:50 -0300 Subject: [PATCH 34/45] fix(clearsign): reserve Solana transaction metadata tags --- messages-solana.proto | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/messages-solana.proto b/messages-solana.proto index 15b7647f..865b5cfb 100644 --- a/messages-solana.proto +++ b/messages-solana.proto @@ -56,6 +56,10 @@ message SolanaSignTx { optional string coin_name = 2 [default = "Solana"]; optional bytes raw_tx = 3; // Serialized Solana transaction bytes repeated SolanaTokenInfo token_info = 4; // Token metadata for display (max 4) + // Reserved for the transaction-bound KKSOLSW1 descriptor and one-request + // opaque-signing consent. Keeping this reservation in the canonical + // protocol makes reusing a planned tag an explicit review decision. + reserved 5 to 8; /* * KKSOLSC1 instruction schema (see solana.h). A schema describes how to * read ONE program instruction: program id, discriminator, and the From f2246cebea8f96fcd7ec2883588a784a60b430ae Mon Sep 17 00:00:00 2001 From: highlander Date: Thu, 30 Jul 2026 15:29:27 -0300 Subject: [PATCH 35/45] feat(zcash): add Ironwood signing metadata --- messages-zcash.options | 1 + messages-zcash.proto | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/messages-zcash.options b/messages-zcash.options index 2ebcb224..4ab6a997 100644 --- a/messages-zcash.options +++ b/messages-zcash.options @@ -5,6 +5,7 @@ ZcashSignPCZT.transparent_digest max_size:32 ZcashSignPCZT.sapling_digest max_size:32 ZcashSignPCZT.orchard_digest max_size:32 ZcashSignPCZT.orchard_anchor max_size:32 +ZcashSignPCZT.ironwood_digest max_size:32 ZcashSignPCZT.expected_seed_fingerprint max_size:32 ZcashPCZTAction.alpha max_size:32 diff --git a/messages-zcash.proto b/messages-zcash.proto index 0be76f47..4dd54d38 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -7,6 +7,11 @@ syntax = "proto2"; +enum ZcashShieldedPool { + ZCASH_SHIELDED_POOL_ORCHARD = 0; + ZCASH_SHIELDED_POOL_IRONWOOD = 1; +} + // Sugar for easier handling in Java option java_package = "com.keepkey.deviceprotocol"; option java_outer_classname = "KeepKeyMessageZcash"; @@ -45,6 +50,11 @@ message ZcashSignPCZT { optional uint32 version_group_id = 16; // Version group ID optional uint32 lock_time = 17; // Transaction lock time optional uint32 expiry_height = 18; // Transaction expiry height + // NU6.3 / transaction-v6 Orchard-family pool selection. The existing + // orchard_* metadata fields describe the selected action bundle for wire + // compatibility; ironwood_digest is the fifth v6 transaction component. + optional ZcashShieldedPool shielded_pool = 19 [default = ZCASH_SHIELDED_POOL_ORCHARD]; + optional bytes ironwood_digest = 20; // 32-byte Ironwood component digest (v6) // Phase 3: transparent shielding support optional uint32 n_transparent_outputs = 29; // 0 for shielded-only (default) optional uint32 n_transparent_inputs = 30; // 0 for shielded-only (default), >0 for hybrid shielding tx From cc858ef3db390b57dd291f6d966b21b107a516a1 Mon Sep 17 00:00:00 2001 From: highlander Date: Fri, 31 Jul 2026 14:41:53 -0300 Subject: [PATCH 36/45] feat(solana): add verified token recipient owner hints --- messages-solana.options | 2 ++ messages-solana.proto | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/messages-solana.options b/messages-solana.options index 510df1cf..fd945238 100644 --- a/messages-solana.options +++ b/messages-solana.options @@ -6,6 +6,8 @@ SolanaSignTx.raw_tx max_size:1232 SolanaSignTx.token_info max_count:4 SolanaSignTx.schema_payload max_size:256 SolanaSignTx.schema_signature max_size:64 +SolanaSignTx.token_recipient_owner max_count:4 +SolanaSignTx.token_recipient_owner max_size:32 SolanaTokenInfo.mint max_size:32 SolanaTokenInfo.symbol max_size:13 SolanaAddress.address max_size:45 diff --git a/messages-solana.proto b/messages-solana.proto index 865b5cfb..d377a6aa 100644 --- a/messages-solana.proto +++ b/messages-solana.proto @@ -78,6 +78,15 @@ message SolanaSignTx { optional bytes schema_payload = 9; optional bytes schema_signature = 10; // 64-byte compact secp256k1 over SHA256(payload) optional uint32 schema_signer_key_id = 11; // trusted clearsign signer slot (0-3) + /* + * Candidate owners for SPL associated-token-account destinations. For a + * TransferChecked instruction, firmware may display an owner only after + * independently deriving ATA(owner, token_program, mint) and matching it + * to the signed destination account. An unmatched candidate is never + * treated as a recipient. This lets payment protocols such as x402 show + * their payTo address without trusting host-side decoding or chain RPC. + */ + repeated bytes token_recipient_owner = 12; // 32-byte Solana public keys (max 4) } /** From dafb567241f57dcf24a4a15b53afecd5177c2d8f Mon Sep 17 00:00:00 2001 From: highlander Date: Fri, 31 Jul 2026 20:15:23 -0300 Subject: [PATCH 37/45] fix(ci): make protobuf JavaScript codegen portable --- .github/workflows/ci.yml | 16 ++- package-lock.json | 183 +++++++++++++++++++++++++++++++-- package.json | 5 +- yarn.lock | 211 +++++++++++++++++++++++++++++++++------ 4 files changed, 377 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e99fc06f..cffbdf46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: Protocol CI on: push: - branches: [master] + branches: [master, up/release-protocol] pull_request: - branches: [master] + branches: [master, up/release-protocol] permissions: contents: read @@ -20,6 +20,18 @@ jobs: - name: Install protobuf compiler run: sudo apt-get update && sudo apt-get install -y protobuf-compiler + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install JavaScript generator dependencies + run: npm ci + + - name: Generate JavaScript bindings + run: npm run build:js + - name: Compile protocol descriptors run: | protoc --proto_path=. --include_imports \ diff --git a/package-lock.json b/package-lock.json index 462ec2a5..7c35fddc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,30 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "requires": { + "minipass": "^7.0.4" + } + }, + "@mapbox/node-pre-gyp": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", + "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", + "dev": true, + "requires": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + } + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -80,6 +104,18 @@ "integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==", "dev": true }, + "abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true + }, + "agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true + }, "bytebuffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", @@ -95,6 +131,12 @@ } } }, + "chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true + }, "commander": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", @@ -103,22 +145,101 @@ "graceful-readlink": ">= 1.0.0" } }, + "consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true + }, + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true + }, "google-protobuf": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.7.1.tgz", - "integrity": "sha512-6fvlUey6cNKtWSEn1bt4CT4wc2EID1fVluHS1dOnqIlxyIu3cBid2BvWE8Rwl6wN+hRTgiAKhfyydAGV/weZYQ==" + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", + "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==" }, "graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, + "grpc-tools": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.13.1.tgz", + "integrity": "sha512-0sttMUxThNIkCTJq5qI0xXMz5zWqV2u3yG1kR3Sj9OokGIoyRBFjoInK9NyW7x5fH7knj48Roh1gq5xbl0VoDQ==", + "dev": true, + "requires": { + "@mapbox/node-pre-gyp": "^2.0.0" + } + }, + "https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "requires": { + "agent-base": "^7.1.2", + "debug": "4" + } + }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", "dev": true }, + "minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true + }, + "minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "requires": { + "minipass": "^7.1.2" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "requires": { + "abbrev": "^3.0.0" + } + }, "pbjs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/pbjs/-/pbjs-0.0.5.tgz", @@ -155,10 +276,60 @@ "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.1.0.tgz", "integrity": "sha1-2KgZVJ6tPmvRievp5Q6WY2u8XMc=" }, + "semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true + }, + "tar": { + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", + "dev": true, + "requires": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, "ts-protoc-gen": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.9.0.tgz", - "integrity": "sha512-cFEUTY9U9o6C4DPPfMHk2ZUdIAKL91hZN1fyx5Stz3g56BDVOC7hk+r5fEMCAGaaIgi2akkT1a2hrxu1wo2Phg==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.10.0.tgz", + "integrity": "sha512-EEbgDWNHK3CvcNhmib94I4HMO23qLddjLRdXW8EUE11VJxbi3n5J0l2DiX/L1pijOaPTkbEoRK+zQinKgKGqsw==", + "dev": true, + "requires": { + "google-protobuf": "^3.6.1" + } + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "dev": true } } diff --git a/package.json b/package.json index 9e82e419..369dd38a 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "scripts": { "clean": "rm -rf ./lib/*.js ./lib/*.ts", "build": "npm run build:js && npm run build:json && npm run build:postprocess", - "build:js": "protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-solana.proto messages-tron.proto messages-ton.proto messages-zcash.proto messages-hive.proto", + "build:js": "mkdir -p ./lib && ./node_modules/.bin/grpc_tools_node_protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-solana.proto messages-tron.proto messages-ton.proto messages-zcash.proto messages-hive.proto", "build:json": "pbjs --keep-case -t json ./types.proto ./messages.proto ./messages-ethereum.proto ./messages-eos.proto ./messages-nano.proto ./messages-cosmos.proto ./messages-binance.proto ./messages-ripple.proto ./messages-tendermint.proto ./messages-thorchain.proto ./messages-osmosis.proto ./messages-mayachain.proto ./messages-solana.proto ./messages-tron.proto ./messages-ton.proto ./messages-zcash.proto ./messages-hive.proto > ./lib/proto.json", "build:postprocess": "find ./lib -name \"*.js\" -exec sed -i '' -e \"s/var global = Function(\\'return this\\')();/var global = (function(){ return this }).call(null);/g\" {} \\;", "prepublishOnly": "npm run build", @@ -21,11 +21,12 @@ "author": "", "license": "ISC", "devDependencies": { + "grpc-tools": "1.13.1", "protobufjs": "^6.8.8", "ts-protoc-gen": "^0.10.0" }, "dependencies": { - "google-protobuf": "^3.7.0-rc.2", + "google-protobuf": "3.21.4", "pbjs": "^0.0.5" } } diff --git a/yarn.lock b/yarn.lock index c4f17406..ce3c2145 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,92 +2,205 @@ # yarn lockfile v1 +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + +"@mapbox/node-pre-gyp@^2.0.0": + version "2.0.3" + resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz" + integrity sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg== + dependencies: + consola "^3.2.3" + detect-libc "^2.0.0" + https-proxy-agent "^7.0.5" + node-fetch "^2.6.7" + nopt "^8.0.0" + semver "^7.5.3" + tar "^7.4.0" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= "@protobufjs/base64@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== "@protobufjs/codegen@^2.0.4": version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== "@protobufjs/eventemitter@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= "@protobufjs/fetch@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= dependencies: "@protobufjs/aspromise" "^1.1.1" "@protobufjs/inquire" "^1.1.0" "@protobufjs/float@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= "@protobufjs/inquire@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= "@protobufjs/path@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= "@protobufjs/pool@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= "@protobufjs/utf8@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= "@types/long@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef" + resolved "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz" + integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q== "@types/node@^10.1.0": - version "10.12.24" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.24.tgz#b13564af612a22a20b5d95ca40f1bffb3af315cf" + version "10.14.6" + resolved "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz" + integrity sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg== + +abbrev@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz" + integrity sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg== + +agent-base@^7.1.2: + version "7.1.4" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== bytebuffer@5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" + resolved "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz" + integrity sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0= dependencies: long "~3" +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== + commander@2.9.0: version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + resolved "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz" + integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= dependencies: graceful-readlink ">= 1.0.0" -google-protobuf@^3.6.1: - version "3.9.0" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.9.0.tgz#1f33e51e7993ea51e758a82650ad4347273b9bc6" +consola@^3.2.3: + version "3.4.2" + resolved "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz" + integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== + +debug@4: + version "4.4.3" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +detect-libc@^2.0.0: + version "2.1.2" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== -google-protobuf@^3.7.0-rc.2: - version "3.7.0-rc.2" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.7.0-rc.2.tgz#a65e9216825065099c4ff243eee9e16e764cc2c9" +google-protobuf@^3.6.1, google-protobuf@3.21.4: + version "3.21.4" + resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz" + integrity sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ== "graceful-readlink@>= 1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + resolved "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= + +grpc-tools@1.13.1: + version "1.13.1" + resolved "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.13.1.tgz" + integrity sha512-0sttMUxThNIkCTJq5qI0xXMz5zWqV2u3yG1kR3Sj9OokGIoyRBFjoInK9NyW7x5fH7knj48Roh1gq5xbl0VoDQ== + dependencies: + "@mapbox/node-pre-gyp" "^2.0.0" + +https-proxy-agent@^7.0.5: + version "7.0.6" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" long@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + resolved "https://registry.npmjs.org/long/-/long-4.0.0.tgz" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== long@~3: version "3.2.0" - resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" + resolved "https://registry.npmjs.org/long/-/long-3.2.0.tgz" + integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s= + +minipass@^7.0.4, minipass@^7.1.2: + version "7.1.3" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + +minizlib@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz" + integrity sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw== + dependencies: + minipass "^7.1.2" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +nopt@^8.0.0: + version "8.1.0" + resolved "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz" + integrity sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A== + dependencies: + abbrev "^3.0.0" pbjs@^0.0.5: version "0.0.5" - resolved "https://registry.yarnpkg.com/pbjs/-/pbjs-0.0.5.tgz#b4c88e15aac4552ca0922aa64cd5338efd3447bf" + resolved "https://registry.npmjs.org/pbjs/-/pbjs-0.0.5.tgz" + integrity sha1-tMiOFarEVSygkiqmTNUzjv00R78= dependencies: bytebuffer "5.0.1" commander "2.9.0" @@ -95,7 +208,8 @@ pbjs@^0.0.5: protobufjs@^6.8.8: version "6.8.8" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz" + integrity sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -113,10 +227,51 @@ protobufjs@^6.8.8: protocol-buffers-schema@3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.1.0.tgz#d8a819549ead3e6bd189ebe9e50e96636bbc5cc7" + resolved "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.1.0.tgz" + integrity sha1-2KgZVJ6tPmvRievp5Q6WY2u8XMc= + +semver@^7.5.3: + version "7.8.5" + resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" + integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== + +tar@^7.4.0: + version "7.5.22" + resolved "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz" + integrity sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA== + dependencies: + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.1.0" + yallist "^5.0.0" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== ts-protoc-gen@^0.10.0: version "0.10.0" - resolved "https://registry.yarnpkg.com/ts-protoc-gen/-/ts-protoc-gen-0.10.0.tgz#f708d99be59ad0be6bdce6f4fe893ec41757d2c9" + resolved "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.10.0.tgz" + integrity sha512-EEbgDWNHK3CvcNhmib94I4HMO23qLddjLRdXW8EUE11VJxbi3n5J0l2DiX/L1pijOaPTkbEoRK+zQinKgKGqsw== dependencies: google-protobuf "^3.6.1" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== From 4b41d1194284781eb5239a8a71315460968e2584 Mon Sep 17 00:00:00 2001 From: highlander Date: Fri, 31 Jul 2026 20:40:49 -0300 Subject: [PATCH 38/45] fix(build): generate bindings for git dependencies --- .github/workflows/ci.yml | 7 +++++-- package.json | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cffbdf46..2e901a1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,11 @@ jobs: - name: Install JavaScript generator dependencies run: npm ci - - name: Generate JavaScript bindings - run: npm run build:js + - name: Verify install generated JavaScript bindings + run: | + test -s lib/messages_pb.js + test -s lib/messages-solana_pb.js + test -s lib/messages-zcash_pb.js - name: Compile protocol descriptors run: | diff --git a/package.json b/package.json index 369dd38a..997305f1 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "build:js": "mkdir -p ./lib && ./node_modules/.bin/grpc_tools_node_protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --js_out=import_style=commonjs,binary:./lib --ts_out=./lib types.proto messages.proto messages-ethereum.proto messages-eos.proto messages-nano.proto messages-cosmos.proto messages-binance.proto messages-ripple.proto messages-tendermint.proto messages-thorchain.proto messages-osmosis.proto messages-mayachain.proto messages-solana.proto messages-tron.proto messages-ton.proto messages-zcash.proto messages-hive.proto", "build:json": "pbjs --keep-case -t json ./types.proto ./messages.proto ./messages-ethereum.proto ./messages-eos.proto ./messages-nano.proto ./messages-cosmos.proto ./messages-binance.proto ./messages-ripple.proto ./messages-tendermint.proto ./messages-thorchain.proto ./messages-osmosis.proto ./messages-mayachain.proto ./messages-solana.proto ./messages-tron.proto ./messages-ton.proto ./messages-zcash.proto ./messages-hive.proto > ./lib/proto.json", "build:postprocess": "find ./lib -name \"*.js\" -exec sed -i '' -e \"s/var global = Function(\\'return this\\')();/var global = (function(){ return this }).call(null);/g\" {} \\;", + "prepare": "npm run build:js", "prepublishOnly": "npm run build", "test": "echo \"Error: no test specified\" && exit 1" }, From be2854903f8795daf42ca7b96d93fc348b886b58 Mon Sep 17 00:00:00 2001 From: highlander Date: Sat, 1 Aug 2026 18:36:22 -0300 Subject: [PATCH 39/45] feat(features): add supports_taproot capability bit Lets a host ask the device whether it can derive and spend P2TR, instead of inferring it from a firmware version. Version inference breaks the moment the feature is retargeted to a different release, and it forces every client to carry a version table. Field 27: 19 and 20 are gaps with no reserved markers, so they are not safe to reuse against historical wire data. Additive and optional -- older hosts ignore it, older firmware simply does not set it. --- messages.proto | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/messages.proto b/messages.proto index 1cf8bbb4..26e7290e 100644 --- a/messages.proto +++ b/messages.proto @@ -319,6 +319,11 @@ message Features { optional bool wipe_code_protection = 25; optional uint32 auto_lock_delay_ms = 26; // Current auto lock delay (in milliseconds) + optional bool supports_taproot = + 27; // Firmware can derive and spend P2TR (BIP-86 / BIP-340 / BIP-341). + // Lets a host detect taproot support directly instead of inferring + // it from a firmware version, which breaks whenever the feature is + // retargeted to a different release. } /** From 635571b00b1318280d71a53ce3d886dd3bc1c11f Mon Sep 17 00:00:00 2001 From: highlander Date: Sun, 2 Aug 2026 18:35:02 -0300 Subject: [PATCH 40/45] fix(ci): request reviews safely for fork PRs --- .github/workflows/copilot-review.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/copilot-review.yml b/.github/workflows/copilot-review.yml index 54db1498..8afdb03e 100644 --- a/.github/workflows/copilot-review.yml +++ b/.github/workflows/copilot-review.yml @@ -1,11 +1,15 @@ name: Request Copilot Review on: - pull_request: + # This workflow never checks out or executes pull-request code. Using the + # base-repository context is therefore safe and is required for cross-fork + # PRs, whose pull_request GITHUB_TOKEN is always downgraded to read-only. + pull_request_target: types: [opened, reopened, ready_for_review, synchronize] jobs: request-copilot-review: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest permissions: pull-requests: write From 342174d93c32209fa7ea3e0553f214c5214a4d59 Mon Sep 17 00:00:00 2001 From: highlander Date: Tue, 4 Aug 2026 14:45:42 -0300 Subject: [PATCH 41/45] feat(reset): dice-entropy fields for on-device roll collection ResetDevice.dice_entropy=10 asks the device to collect dice rolls with the single button and mix them into the internal entropy before it is displayed or committed. DebugLinkDecision.input=2 lets debug builds inject synthetic roll input (kept short so the decoded struct fits the firmware tiny-message buffer). DebugLinkState.dice_digest=15 exposes SHA-256 of the collected ASCII roll string so tests can prove the device received exactly the injected rolls. ButtonRequest_DiceRoll=39 announces the entry screen to the host. --- messages.proto | 10 ++++++++++ types.proto | 1 + 2 files changed, 11 insertions(+) diff --git a/messages.proto b/messages.proto index 26e7290e..a35d67c6 100644 --- a/messages.proto +++ b/messages.proto @@ -576,6 +576,9 @@ message ResetDevice { 7; // Initialize without ever showing the recovery sentence. optional uint32 auto_lock_delay_ms = 8; // Screensaver Timeout optional uint32 u2f_counter = 9; // U2F Counter + optional bool dice_entropy = + 10; // collect dice rolls on the device and mix them into the internal + // entropy before it is displayed or committed } /** @@ -986,6 +989,10 @@ message FirmwareUpload { */ message DebugLinkDecision { required bool yes_no = 1; // true for "Confirm", false for "Cancel" + optional string input = + 2; // synthetic keyboard input for on-device entry flows (e.g. dice + // rolls '1'-'6', 'u' for undo); kept short so the decoded struct + // fits the tiny-message buffer } /** @@ -1020,6 +1027,9 @@ message DebugLinkState { 12; // last auto completed recovery word optional bytes firmware_hash = 13; // hash of the application and meta header optional bytes storage_hash = 14; // hash of storage + optional bytes dice_digest = + 15; // SHA-256 of the ASCII dice-roll string collected during a + // dice_entropy ResetDevice workflow } /** diff --git a/types.proto b/types.proto index 18cf8fab..9ceaf72c 100644 --- a/types.proto +++ b/types.proto @@ -134,6 +134,7 @@ enum ButtonRequestType { ButtonRequest_RemoveWipeCode = 36; ButtonRequest_ChangeWipeCode = 37; ButtonRequest_CreateWipeCode = 38; + ButtonRequest_DiceRoll = 39; } /** From e9067a032b998e2560395ee6f2d76d3cdbeea19c Mon Sep 17 00:00:00 2001 From: highlander Date: Wed, 5 Aug 2026 18:10:24 -0300 Subject: [PATCH 42/45] =?UTF-8?q?ci:=20pin=20cimg/node:20.11=20=E2=80=94?= =?UTF-8?q?=20the=20legacy=20circleci/node=20image=20ships=20node=2017?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit yarn install --frozen-lockfile fails on this branch with: @mapbox/node-pre-gyp@2.0.3: The engine "node" is incompatible with this module. Expected version ">=18". Got "17.2.0" The unpinned legacy circleci/node image resolves to node 17.2.0. master passes only because its older lockfile predates that transitive requirement; any lockfile refresh trips it. Pin a maintained image. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b2d2f49f..034b0e8d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 defaults: &defaults working_directory: ~/device-protocol docker: - - image: circleci/node + - image: cimg/node:20.11 jobs: build: From fbb483f1cadc6394d51566ca4be0cfe6daa40000 Mon Sep 17 00:00:00 2001 From: highlander Date: Wed, 5 Aug 2026 18:13:22 -0300 Subject: [PATCH 43/45] fix(zcash): move file options into the preamble so pbjs can parse The Ironwood enum was inserted above the file-level java_package / java_outer_classname options, leaving them after a top-level definition. protobufjs's parser rejects that outright: Error: illegal token 'option' (messages-zcash.proto, line 16) so 'npm run build' (build:json -> lib/proto.json) fails on this branch. protoc accepts either order, so the generated descriptors are unchanged; every other proto in the repo already declares options directly after syntax. Verified: pbjs now emits proto.json cleanly. --- messages-zcash.proto | 8 +- package-lock.json | 255 ++++++++++++++++++++++++++++++------------- 2 files changed, 185 insertions(+), 78 deletions(-) diff --git a/messages-zcash.proto b/messages-zcash.proto index 4dd54d38..f03848b7 100644 --- a/messages-zcash.proto +++ b/messages-zcash.proto @@ -7,15 +7,15 @@ syntax = "proto2"; +// Sugar for easier handling in Java +option java_package = "com.keepkey.deviceprotocol"; +option java_outer_classname = "KeepKeyMessageZcash"; + enum ZcashShieldedPool { ZCASH_SHIELDED_POOL_ORCHARD = 0; ZCASH_SHIELDED_POOL_IRONWOOD = 1; } -// Sugar for easier handling in Java -option java_package = "com.keepkey.deviceprotocol"; -option java_outer_classname = "KeepKeyMessageZcash"; - /** * Request: Sign a Zcash shielded transaction (PCZT format) * diff --git a/package-lock.json b/package-lock.json index 7c35fddc..60628403 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,41 @@ { "name": "@keepkey/device-protocol", "version": "7.14.1", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@isaacs/fs-minipass": { + "packages": { + "": { + "name": "@keepkey/device-protocol", + "version": "7.14.1", + "license": "ISC", + "dependencies": { + "google-protobuf": "3.21.4", + "pbjs": "^0.0.5" + }, + "devDependencies": { + "grpc-tools": "1.13.1", + "protobufjs": "^6.8.8", + "ts-protoc-gen": "^0.10.0" + } + }, + "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "dev": true, - "requires": { + "dependencies": { "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" } }, - "@mapbox/node-pre-gyp": { + "node_modules/@mapbox/node-pre-gyp": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", "dev": true, - "requires": { + "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", @@ -26,236 +43,307 @@ "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" } }, - "@protobufjs/aspromise": { + "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", "dev": true }, - "@protobufjs/base64": { + "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "dev": true }, - "@protobufjs/codegen": { + "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", "dev": true }, - "@protobufjs/eventemitter": { + "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", "dev": true }, - "@protobufjs/fetch": { + "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", "dev": true, - "requires": { + "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, - "@protobufjs/float": { + "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", "dev": true }, - "@protobufjs/inquire": { + "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", "dev": true }, - "@protobufjs/path": { + "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", "dev": true }, - "@protobufjs/pool": { + "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", "dev": true }, - "@protobufjs/utf8": { + "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", "dev": true }, - "@types/long": { + "node_modules/@types/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==", "dev": true }, - "@types/node": { + "node_modules/@types/node": { "version": "10.14.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz", "integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==", "dev": true }, - "abbrev": { + "node_modules/abbrev": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", - "dev": true + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } }, - "agent-base": { + "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true + "dev": true, + "engines": { + "node": ">= 14" + } }, - "bytebuffer": { + "node_modules/bytebuffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", - "requires": { + "dependencies": { "long": "~3" }, - "dependencies": { - "long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" - } + "engines": { + "node": ">=0.8" + } + }, + "node_modules/bytebuffer/node_modules/long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "engines": { + "node": ">=0.6" } }, - "chownr": { + "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true + "dev": true, + "engines": { + "node": ">=18" + } }, - "commander": { + "node_modules/commander": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "requires": { + "dependencies": { "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" } }, - "consola": { + "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } }, - "debug": { + "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, - "requires": { + "dependencies": { "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "detect-libc": { + "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "google-protobuf": { + "node_modules/google-protobuf": { "version": "3.21.4", "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==" }, - "graceful-readlink": { + "node_modules/graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, - "grpc-tools": { + "node_modules/grpc-tools": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/grpc-tools/-/grpc-tools-1.13.1.tgz", "integrity": "sha512-0sttMUxThNIkCTJq5qI0xXMz5zWqV2u3yG1kR3Sj9OokGIoyRBFjoInK9NyW7x5fH7knj48Roh1gq5xbl0VoDQ==", "dev": true, - "requires": { + "hasInstallScript": true, + "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0" + }, + "bin": { + "grpc_tools_node_protoc": "bin/protoc.js", + "grpc_tools_node_protoc_plugin": "bin/protoc_plugin.js" } }, - "https-proxy-agent": { + "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, - "requires": { + "dependencies": { "agent-base": "^7.1.2", "debug": "4" + }, + "engines": { + "node": ">= 14" } }, - "long": { + "node_modules/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", "dev": true }, - "minipass": { + "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } }, - "minizlib": { + "node_modules/minizlib": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, - "requires": { + "dependencies": { "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" } }, - "ms": { + "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node-fetch": { + "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, - "requires": { + "dependencies": { "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "nopt": { + "node_modules/nopt": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", "dev": true, - "requires": { + "dependencies": { "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "pbjs": { + "node_modules/pbjs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/pbjs/-/pbjs-0.0.5.tgz", "integrity": "sha1-tMiOFarEVSygkiqmTNUzjv00R78=", - "requires": { + "dependencies": { "bytebuffer": "5.0.1", "commander": "2.9.0", "protocol-buffers-schema": "3.1.0" + }, + "bin": { + "pbjs": "cli.js" } }, - "protobufjs": { + "node_modules/protobufjs": { "version": "6.8.8", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", "dev": true, - "requires": { + "hasInstallScript": true, + "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", @@ -269,68 +357,87 @@ "@types/long": "^4.0.0", "@types/node": "^10.1.0", "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" } }, - "protocol-buffers-schema": { + "node_modules/protocol-buffers-schema": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.1.0.tgz", "integrity": "sha1-2KgZVJ6tPmvRievp5Q6WY2u8XMc=" }, - "semver": { + "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "tar": { + "node_modules/tar": { "version": "7.5.22", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", "dev": true, - "requires": { + "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" } }, - "tr46": { + "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, - "ts-protoc-gen": { + "node_modules/ts-protoc-gen": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.10.0.tgz", "integrity": "sha512-EEbgDWNHK3CvcNhmib94I4HMO23qLddjLRdXW8EUE11VJxbi3n5J0l2DiX/L1pijOaPTkbEoRK+zQinKgKGqsw==", "dev": true, - "requires": { + "dependencies": { "google-protobuf": "^3.6.1" + }, + "bin": { + "protoc-gen-ts": "bin/protoc-gen-ts" } }, - "webidl-conversions": { + "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, - "whatwg-url": { + "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, - "requires": { + "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, - "yallist": { + "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true + "dev": true, + "engines": { + "node": ">=18" + } } } } From 2190c152c66a139bdfca5b9fd127853065f687c5 Mon Sep 17 00:00:00 2001 From: highlander Date: Fri, 21 Aug 2026 13:30:06 -0500 Subject: [PATCH 44/45] feat(solana): KKSOLSW1 transaction-bound account attestation on tags 5-7 A Solana v0 transaction may source instruction accounts from an Address Lookup Table. Those accounts are NOT in the bytes being signed, so the device cannot derive them and refuses to guess: the transaction is forced to SOL_TX_REVIEW_OPAQUE, which fsm_msg_solana.h refuses outright unless the user has enabled AdvancedMode, and which is then an explicit BLIND SIGN. The instruction's meaning is never shown. So this does not rescue a blank screen -- nothing is signed silently. It upgrades a BLIND SIGN into a provider-attested CLEAR SIGN, which is the whole point of the tier. KKSOLSC1 schemas cannot close this: they are instruction-scoped and reusable, carry no transaction hash, and work by decoding values out of the bytes the device is signing. With a lookup table those bytes do not contain the accounts. So a provider attests the resolved account list for THIS transaction: preimage = "KeepKeySolanaTxAccounts/1" || message_hash(32) || count(le32) || account[i](32) ... Bound to the exact message hash, so it cannot be replayed onto another transaction. Domain-tagged, so a signature made for any other purpose -- an EVM metadata blob, a token definition -- cannot be replayed as one. Annotation, not authority: accounts render as PROVIDER-ATTESTED next to the provider alias, and the unverified-transaction review still runs. Uses tags 5-7 of the reservation that named this descriptor. Tag 8 stays reserved for one-request opaque-signing consent. --- messages-solana.proto | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/messages-solana.proto b/messages-solana.proto index d377a6aa..312b1481 100644 --- a/messages-solana.proto +++ b/messages-solana.proto @@ -56,10 +56,37 @@ message SolanaSignTx { optional string coin_name = 2 [default = "Solana"]; optional bytes raw_tx = 3; // Serialized Solana transaction bytes repeated SolanaTokenInfo token_info = 4; // Token metadata for display (max 4) - // Reserved for the transaction-bound KKSOLSW1 descriptor and one-request - // opaque-signing consent. Keeping this reservation in the canonical - // protocol makes reusing a planned tag an explicit review decision. - reserved 5 to 8; + /* + * KKSOLSW1 -- transaction-bound account attestation. + * + * A Solana v0 transaction may source instruction accounts from an Address + * Lookup Table. Those accounts are NOT in the bytes being signed, so the + * device cannot derive them and refuses to guess: such a transaction is + * forced to SOL_TX_REVIEW_OPAQUE, which is refused outright unless the user + * has enabled AdvancedMode and is then an explicit BLIND SIGN. The + * instruction's meaning is never shown. + * + * So this does not rescue a blank screen -- it upgrades a blind sign into a + * provider-attested clear sign, which is the whole point of the tier. + * + * A provider may attest the resolved account list for THIS transaction. + * The attestation is bound to the exact message hash, so it cannot be + * replayed onto another transaction, and it is domain-tagged so a + * signature made for any other purpose cannot be replayed as one. + * + * This is annotation, not authority: the accounts are displayed as + * PROVIDER-ATTESTED alongside the provider's alias, and the normal + * unverified-transaction review still runs. Rendering nothing while + * signing is the outcome this replaces. + * + * Preimage: "KeepKeySolanaTxAccounts/1" || message_hash(32) + * || count(le32) || account[0..count-1] (32 bytes each) + */ + repeated bytes lut_account = 5; // resolved 32-byte account keys (max 8) + optional bytes lut_signature = 6; // 64-byte compact secp256k1 over SHA256(preimage) + optional uint32 lut_signer_key_id = 7; // trusted clearsign signer slot (0-3) + // Still reserved: one-request opaque-signing consent. + reserved 8; /* * KKSOLSC1 instruction schema (see solana.h). A schema describes how to * read ONE program instruction: program id, discriminator, and the From be0e84d002a883d412ac8fd5e07fd05039bceb77 Mon Sep 17 00:00:00 2001 From: highlander Date: Fri, 21 Aug 2026 16:34:14 -0500 Subject: [PATCH 45/45] feat(eip712): device-driven field streaming for structured typed data Five messages (1704-1708) that let the device drive an EIP-712 walk instead of being handed the document. The device asks for one type definition, or one leaf VALUE, at a time, and hashes each value in the same pass that displays it. The host owns the document; the device holds only the digest stack for the containers currently open. A 10,000-element array costs the same RAM as a 2-element one, and there is no document-size limit to raise later. This replaces Ethereum712TypesValues, which shipped the whole thing as two 2048-byte JSON blobs and was withdrawn in 7.14.2 because its parser could not guarantee the value on screen was the value being hashed. Here that is structural rather than reviewed: a value is displayed and absorbed from the same buffer in the same call, and each member_path is requested exactly once. That last clause is not incidental. Trezor shipped this same protocol with a hole until 2.12.0 -- nothing bound repeated answers for one path to each other, so a host could answer the domain name one way for the summary screen and another for the hashing pass. Requesting each path once closes it by construction rather than by caching around it. ONE deliberate divergence from Trezor and OneKey: arrays. Both describe an array as a field whose entry_type is another EthereumFieldType -- a self-referential message. Trezor can, because core is Python. OneKey does it on nanopb by compiling that one field as a POINTER (PB_ENABLE_MALLOC) and then flattening the pointer chain into a fixed pool to sever the recursion. KeepKey's nanopb is static-allocation only, and a heap inside a signing device is not a liability worth taking on for one field. So array nesting is FLATTENED onto the wire the way Ledger describes it: data_type is always the LEAF type, and array_levels carries the dimensions in written order, 0 for dynamic: uint256 -> UINT, size=32, array_levels=[] address[] -> ADDRESS, array_levels=[0] Person[3] -> STRUCT, struct_name="Person", array_levels=[3] int16[2][][4] -> INT, size=2, array_levels=[2,0,4] Nothing EIP-712 permits is lost and the encoding is bounded, flat and statically sized. Enum values still match Trezor's so a shared host keeps its mapping; ARRAY is reserved and never sent. Values arrive as raw big-endian bytes of the declared width, not JSON. That deletes the whole decimal-parsing step from the device -- and with it the 64-bit integer ceiling that made the old path refuse an unlimited approval, which is the most common permit there is. Validated with protoc 3.5.1 in kktech/firmware:v8. --- messages-ethereum.options | 9 +++ messages-ethereum.proto | 129 ++++++++++++++++++++++++++++++++++++++ messages.proto | 10 +++ 3 files changed, 148 insertions(+) diff --git a/messages-ethereum.options b/messages-ethereum.options index 8a1b3ff3..fa1767f5 100644 --- a/messages-ethereum.options +++ b/messages-ethereum.options @@ -3,3 +3,12 @@ EthereumMetadataAck.display_summary max_size:32 LoadClearsignSigner.pubkey max_size:33 LoadClearsignSigner.alias max_size:32 LoadClearsignSigner.icon max_size:384 + +EthereumSignTypedData.primary_type max_size:80 +EthereumTypedDataStructRequest.name max_size:80 +EthereumTypedDataStructAck.members max_count:32 +EthereumTypedDataStructAck.EthereumStructMember.name max_size:64 +EthereumTypedDataStructAck.EthereumFieldType.struct_name max_size:80 +EthereumTypedDataStructAck.EthereumFieldType.array_levels max_count:4 +EthereumTypedDataValueRequest.member_path max_count:16 +EthereumTypedDataValueAck.value max_size:1024 diff --git a/messages-ethereum.proto b/messages-ethereum.proto index f04424f5..7a708b41 100644 --- a/messages-ethereum.proto +++ b/messages-ethereum.proto @@ -247,3 +247,132 @@ message Ethereum712TypesValues { required string eip712data = 4; // "domain" or "message" json string (up to 2048) required uint32 eip712typevals = 5; // device calculates hash for 1 = domain sep, 2 = message } + +// ── Structured EIP-712, device-driven field streaming ──────────────── +// +// The device drives. It asks for one type definition, or one leaf VALUE, at a +// time, and hashes each value in the same pass that displays it. The host owns +// the document; the device holds only the digest stack for the containers +// currently open, so a 10,000-element array costs the same RAM as a 2-element +// one and there is no document-size limit to raise later. +// +// This replaces Ethereum712TypesValues, which shipped the whole document in two +// 2048-byte JSON blobs and was withdrawn in 7.14.2: its parser could not +// guarantee that the value shown on screen was the value being hashed. Here +// that property is structural -- a value is displayed and absorbed from the +// same buffer in the same call, and each member_path is requested exactly once. +// +// Shapes follow Trezor's where they can, so a host that can drive a Trezor can +// drive a KeepKey with the same traversal logic. ONE deliberate divergence is +// described at EthereumFieldType. + +/** + * Request: Begin structured EIP-712 signing. The device replies with + * EthereumTypedDataStructRequest and drives from there. + * @start + * @next EthereumTypedDataStructRequest + * @next Failure + */ +message EthereumSignTypedData { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required string primary_type = 2; // primaryType of the message being signed + optional bool metamask_v4_compat = 3 [default = true]; // array-of-struct hashing follows MetaMask v4 +} + +/** + * Response: The device needs this struct's member list before it can hash. + * @next EthereumTypedDataStructAck + */ +message EthereumTypedDataStructRequest { + required string name = 1; // struct name, "EIP712Domain" for the domain +} + +/** + * Request: The member list for the struct the device just asked about, in + * declaration order. Order is part of the signature: it determines encodeType + * and the order encodeData concatenates members. + * @next EthereumTypedDataStructRequest + * @next EthereumTypedDataValueRequest + * @next Failure + */ +message EthereumTypedDataStructAck { + repeated EthereumStructMember members = 1; + + message EthereumStructMember { + required EthereumFieldType type = 1; + required string name = 2; + } + + /** + * DIVERGENCE FROM TREZOR, and the reason for it. + * + * Trezor and OneKey describe an array as a field whose `entry_type` is + * another EthereumFieldType -- a self-referential message. Trezor can do + * that because core is Python. OneKey does it on nanopb by compiling that + * one field as a POINTER, which needs PB_ENABLE_MALLOC, and then flattens + * the pointer chain into a fixed pool to sever the recursion. + * + * KeepKey's nanopb is static-allocation only, and a heap inside a signing + * device is a liability we are not taking on for one field. So the array + * nesting is FLATTENED onto the wire, the way Ledger describes it: + * data_type is always the LEAF type, and array_levels carries the + * dimensions. + * + * array_levels lists the bracket groups in the order they are written in + * the Solidity type string, left to right -- which is the order encodeType + * must reproduce. 0 means a dynamic dimension, N means a fixed one: + * + * uint256 -> data_type=UINT, size=32, array_levels=[] + * address[] -> data_type=ADDRESS, array_levels=[0] + * Person[3] -> data_type=STRUCT, struct_name="Person", + * array_levels=[3] + * int16[2][][4] -> data_type=INT, size=2, array_levels=[2,0,4] + * + * Nothing EIP-712 permits is lost, and the encoding is bounded, flat and + * statically sized. + */ + message EthereumFieldType { + required EthereumDataType data_type = 1; // the LEAF type; never ARRAY + optional uint32 size = 2; // bytesN: N. intN/uintN: N in BYTES, 1..32. + optional string struct_name = 3; // STRUCT: its name, to request in turn + repeated uint32 array_levels = 4; // see above. Empty for a non-array. + } + + // Values match Trezor's enum so a shared host implementation keeps its + // mapping. ARRAY is reserved and never sent -- see EthereumFieldType. + enum EthereumDataType { + UINT = 1; + INT = 2; + BYTES = 3; + STRING = 4; + BOOL = 5; + ADDRESS = 6; + ARRAY = 7; // reserved, unused + STRUCT = 8; + } +} + +/** + * Response: The device needs one leaf value. member_path addresses it: element + * 0 is 0 for the domain and 1 for the message, and the rest index members and + * array elements from there. A struct is never requested as a value -- the + * device walks into it. An array's LENGTH is requested as its own value, big + * endian uint16, before its elements. + * @next EthereumTypedDataValueAck + */ +message EthereumTypedDataValueRequest { + repeated uint32 member_path = 1; +} + +/** + * Request: The raw big-endian bytes of the requested leaf, already the exact + * declared width. Not JSON: the device does no number parsing, so there is no + * integer width ceiling and no decimal-to-binary step that could disagree with + * what the host meant. + * @next EthereumTypedDataValueRequest + * @next EthereumTypedDataSignature + * @next Failure + */ +message EthereumTypedDataValueAck { + required bytes value = 1; +} diff --git a/messages.proto b/messages.proto index 510ab111..9357211c 100644 --- a/messages.proto +++ b/messages.proto @@ -102,6 +102,16 @@ enum MessageType { MessageType_EthereumMetadataAck = 116 [ (wire_out) = true ]; MessageType_LoadClearsignSigner = 117 [ (wire_in) = true ]; + // Structured EIP-712, device-driven field streaming. Allocated in a fresh + // block rather than after the 108-117 Ethereum run: the low range is where + // upstream Trezor allocates too, and keeping clear of it means adopting more + // of their messages later never collides. + MessageType_EthereumSignTypedData = 1704 [ (wire_in) = true ]; + MessageType_EthereumTypedDataStructRequest = 1705 [ (wire_out) = true ]; + MessageType_EthereumTypedDataStructAck = 1706 [ (wire_in) = true ]; + MessageType_EthereumTypedDataValueRequest = 1707 [ (wire_out) = true ]; + MessageType_EthereumTypedDataValueAck = 1708 [ (wire_in) = true ]; + // BIP-85 MessageType_GetBip85Mnemonic = 120 [ (wire_in) = true ]; MessageType_Bip85Mnemonic = 121 [ (wire_out) = true ];