- analyzed penumbra protocol specifications
- studied prax wallet airgap signer implementation
- examined ledger penumbra implementation (@zondax/ledger-penumbra@1.0.0)
- sourced full ledger-penumbra source code (~/rotko/ledger-penumbra)
- analyzed pcli source code (~/rotko/penumbra)
- documented qr code protocol format
- created comprehensive integration spec (PENUMBRA_INTEGRATION.md)
- documented complete signing algorithm from both ledger and pcli (PENUMBRA_SIGNING_DETAILS.md)
- added
Penumbravariant toEncryptionenum (rust/definitions/src/crypto.rs) - updated
Encryption::show()to handle penumbra - updated
Encryption::identicon_style()to handle penumbra - updated
Encryption::try_from()to parse "penumbra" string - added
Penumbra(H256)variant toNetworkSpecsKeyContentenum (rust/definitions/src/keyring.rs) - updated
NetworkSpecsKey::from_parts()to handle penumbra - updated
NetworkSpecsKey::genesis_hash_encryption()to handle penumbra - added penumbra placeholder to
get_multisigner()(rust/definitions/src/helpers.rs) - added penumbra placeholder to
base58_or_eth_pubkey_to_multisigner()(rust/definitions/src/helpers.rs) - tested compilation - all changes compile successfully ✓
- added penumbra feature flag to Cargo.toml with dependencies:
- decaf377 v0.10.1
- decaf377-rdsa v0.11
- blake2b_simd v1.0.2
- rand_chacha v0.3.1
- bip32 v0.5
- hmac v0.12, sha2 v0.10, pbkdf2 v0.12
- implemented
SpendKeyBytestype with zeroize on drop - implemented
from_seed_phrase()bip44 key derivation (m/44'/6532'/account') - implemented
expand_ff()prf function (blake2b with personalization) - implemented
derive_spend_auth_key()to get SigningKey - implemented
sign_spend()with randomized keys (matches ledger-penumbra) - implemented
PenumbraAuthorizationDatastruct with encode() for QR output - implemented
sign_transaction()to sign all spend/vote actions - implemented
EffectHashtype with:from_proto_effecting_data()for action hashescompute_transaction_effect_hash()for full tx hash
- added personalization constants for all action types
- added
PenumbraNotSubstrateerror to db_handling - all 6 penumbra tests pass ✓
- added dependencies: poseidon377 v1.2.0, bech32 v0.11.0
- implemented
NullifierKeyderivation from spend key bytes - implemented
expand_fq()for Fq field element expansion - implemented
FullViewingKeystruct with:derive_from()- derives FVK from SpendKeyBytesto_bytes()/from_bytes()- 64-byte serialization (ak || nk)wallet_id()- computes WalletId using poseidon377 hashto_bech32m()/from_bech32m()- "penumbrafullviewingkey1..." encoding
- implemented
WalletIdstruct with:to_bech32m()- "penumbrawalletid1..." encodingto_bytes()- 32-byte raw format
- implemented
FvkExportDatafor QR code export:from_spend_key()- creates export data from spend keyencode_qr()/decode_qr()- binary format with prelude53 03 01encode_qr_hex()/decode_qr_hex()- hex string format
- QR format:
[53][03][01][account:4][label_len:1][label][fvk:64][wallet_id:32] - all 15 penumbra tests pass ✓
chain: penumbra-1 (mainnet)
type: cosmos sdk chain (not substrate)
bip44 path: m/44'/6532'/0'
address format: bech32m with prefix "penumbra"
address length: 80 bytes
signatures: decaf377-rdsa (64 bytes each)
effect hash: blake2b-512 (64 bytes)
outgoing (hot → cold):
[0x53][0x03][0x10][metadata][transaction_plan]
0x53 = substrate format compat
0x03 = penumbra identifier
0x10 = transaction type
metadata = asset names (count + length-prefixed strings)
transaction_plan = protobuf bytes
return (cold → hot):
[0x53][0x03][0x10][effect_hash][signatures]
effect_hash = 64 bytes blake2b-512
signatures = spend_auth + delegator_vote
- spend_auth_count (2 bytes le)
- spend_auth_sigs (64 bytes each)
- vote_count (2 bytes le)
- vote_sigs (64 bytes each)
- add handler for transaction type
0x10inrust/transaction_parsing/src/lib.rs- modify
handle_scanner_input()match statement - add case:
"10" => process_penumbra_transaction(database, data_hex)(gated by penumbra feature)
- modify
- create
rust/transaction_parsing/src/penumbra.rs- parse prelude and validate format
- extract metadata (asset names)
- create
PenumbraTransactionPlanstruct - implement
parse_penumbra_transaction()handler - create transaction display cards (PenumbraSummaryCard)
- re-export signing types from transaction_signing::penumbra
- added
Penumbravariant toEncryptionenum in signer.udl - added penumbra card types to
Cardenum:- PenumbraSummaryCard, PenumbraSpendCard, PenumbraOutputCard
- PenumbraSwapCard, PenumbraDelegateCard, PenumbraVoteCard
- added corresponding dictionary definitions for penumbra types
- added Penumbra case to
make_message.rs(returns NotSupported) - added Penumbra case to
parser.rsinto_sufficient (returns NotSupported)
-
vendor penumbra protobufs or generate from buf.build
penumbra.core.transaction.v1.TransactionPlanpenumbra.core.transaction.v1.Actionpenumbra.core.transaction.v1.AuthorizationDatapenumbra.crypto.decaf377_rdsa.v1.SpendAuthSignature
-
add dependencies to rust/Cargo.toml:
prost = "0.12" # protobuf decoding blake2 = "0.10" # blake2b-512
- implement authorization qr generation in PenumbraAuthorizationData::encode()
- encode effect hash (64 bytes)
- encode spend auth signatures with count
- encode delegator vote signatures with count
- encode lqt vote signatures with count
- add QR prelude formatting: [0x53][0x03][0x10]
-
android (kotlin)
- transaction display screen
- show transaction details
- display effect hash
-
ios (swift)
- transaction display screen
- show transaction details
- display effect hash
- unit tests for qr parsing
- unit tests for effect hash computation
- unit tests for signature generation
- integration test with prax wallet
- end-to-end test: sign real transaction
we're NOT adding penumbra as a default network like polkadot/kusama because penumbra is not a substrate chain. instead, we're extending parity signer to support penumbra's signing protocol via qr codes.
the key is recognizing the 0x53 0x03 0x10 prelude and routing to penumbra-specific transaction handling.
currently using ed25519 multisigner as a placeholder for penumbra keys to avoid compilation errors in functions that expect substrate's multisigner type. proper penumbra key handling (decaf377) will be implemented in the crypto phase.
# rust/Cargo.toml
[dependencies]
# crypto primitives (use u64_backend for phones, u32 for ledger)
decaf377 = { version = "0.10.1", features = ["u64_backend"] }
decaf377-rdsa = { version = "0.11", features = ["u64_backend"] }
poseidon377 = "1.1.0"
# hashing
blake2b_simd = "1.0.2"
# address encoding
f4jumble = "0.1.0"
bech32 = "0.11.0"
# encryption
chacha20poly1305 = "0.10.1"
# rng
rand_chacha = "0.3.1"
# parsing
prost = "0.12" # protobuf (or nom for no_std)
# misc
zeroize = { version = "1.7.0", features = ["derive"] }implement signing with decaf377-rdsa✓ DONEadd transaction type handler✓ DONE0x10to routing logiccreate penumbra transaction parser module✓ DONEwire up signing module to transaction parser✓ DONE (re-exports in place)- implement full protobuf parsing for TransactionPlan (extract randomizers)
- add effect hash computation from parsed plan
- implement android/ios UI for penumbra cards
- test with prax wallet
- prax wallet:
../prax/packages/wallet/src/airgap-signer.ts - ledger-penumbra:
~/rotko/ledger-penumbra/app/rust/src/- signing:
ffi/sign.rs - key derivation:
keys/signing_key.rs,keys/spend_key.rs - effect hash:
parser/effect_hash.rs - constants:
constants.rs
- signing:
- pcli source:
~/rotko/penumbra/crates/core/- transaction auth:
transaction/src/plan/auth.rs - spend key:
keys/src/keys/spend.rs - bip44:
keys/src/keys/bip44.rs
- transaction auth:
- penumbra protocol: https://protocol.penumbra.zone/
- penumbra mainnet: chain-id
penumbra-1 - decaf377 curve: https://docs.rs/decaf377
- rdsa signatures: https://docs.rs/decaf377-rdsa