From 3f1f395b0614657de3379183be717cc1fc20df39 Mon Sep 17 00:00:00 2001 From: nol4lej Date: Fri, 10 Jul 2026 19:48:51 -0400 Subject: [PATCH 1/5] feat(shielded)!: carry circuit_version inside the encrypted memo --- Cargo.lock | 2 +- frame/shielded-pool/CHANGELOG.md | 9 ++++ frame/shielded-pool/Cargo.toml | 2 +- frame/shielded-pool/src/benchmarking.rs | 2 +- frame/shielded-pool/src/lib.rs | 6 +-- .../src/operations/private_transfer.rs | 2 +- .../shielded-pool/src/operations/unshield.rs | 6 +-- frame/shielded-pool/src/types.rs | 24 ++++----- primitives/encrypted-memo/CHANGELOG.md | 9 ++++ primitives/encrypted-memo/Cargo.lock | 2 +- primitives/encrypted-memo/Cargo.toml | 2 +- primitives/encrypted-memo/src/lib.rs | 4 +- primitives/encrypted-memo/src/memo.rs | 52 ++++++++++++------- 13 files changed, 76 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9281155c..e55fc31a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8173,7 +8173,7 @@ dependencies = [ [[package]] name = "pallet-shielded-pool" -version = "0.9.0" +version = "0.10.0" dependencies = [ "ark-bn254", "ark-ff 0.5.0", diff --git a/frame/shielded-pool/CHANGELOG.md b/frame/shielded-pool/CHANGELOG.md index 89e5810a..4c407126 100644 --- a/frame/shielded-pool/CHANGELOG.md +++ b/frame/shielded-pool/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to `pallet-shielded-pool` will be documented in this file. +## [0.10.0] - 2026-07-10 + +### Changed +- **`MAX_ENCRYPTED_MEMO_SIZE` 176 → 180** to accommodate the new `circuit_version` field carried in the memo plaintext (see `orbinum-encrypted-memo` 0.7.0). `EncryptedMemo` wire offsets updated: `ciphertext [12..132]`, `tag [132..148]`, `ephPk [148..180]`. The pallet still treats the memo as opaque bytes — only the length bound and slice offsets change. +- **Weights regenerated** — the larger memo grows `CommitmentMemos`' `MaxEncodedLen`, changing the `proof_size` of every extrinsic that reads/writes it (`shield`, `shield_batch`, `private_transfer`, `unshield`). Runtime `spec_version` 5 → 6. + +### Breaking +- `EncryptedMemo` now requires exactly 180 bytes; 176-byte memos are rejected. Consensus-affecting (covered by the `spec_version` bump). `transaction_version` is intentionally **not** bumped: the extrinsic SCALE codec is unchanged (the memo arg is still `BoundedVec` — only its max bound rose), so offline-signed extrinsics still decode identically. + ## [0.9.0] - 2026-07-09 ### Changed diff --git a/frame/shielded-pool/Cargo.toml b/frame/shielded-pool/Cargo.toml index 47d2261a..2b43fc80 100644 --- a/frame/shielded-pool/Cargo.toml +++ b/frame/shielded-pool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-shielded-pool" -version = "0.9.0" +version = "0.10.0" description = "Shielded pool pallet for private transactions using ZK proofs" authors = ["Orbinum Team"] license = "GPL-3.0-or-later" diff --git a/frame/shielded-pool/src/benchmarking.rs b/frame/shielded-pool/src/benchmarking.rs index b176704d..213abf10 100644 --- a/frame/shielded-pool/src/benchmarking.rs +++ b/frame/shielded-pool/src/benchmarking.rs @@ -69,7 +69,7 @@ mod benchmarks { let (caller, asset_id) = setup_benchmark_env::(); let amount: BalanceOf = T::MinShieldAmount::get() * 10u32.into(); let commitment = Commitment([1u8; 32]); - // Memo must be exactly 168 bytes (MAX_ENCRYPTED_MEMO_SIZE): nonce(12) + data(108) + MAC(16) + ephPk(32) + // Memo must be exactly 180 bytes (MAX_ENCRYPTED_MEMO_SIZE): nonce(12) + data(120) + MAC(16) + ephPk(32) let memo_bytes = vec![0u8; MAX_ENCRYPTED_MEMO_SIZE as usize]; let encrypted_memo = FrameEncryptedMemo(memo_bytes.try_into().unwrap()); diff --git a/frame/shielded-pool/src/lib.rs b/frame/shielded-pool/src/lib.rs index ecae68e3..6fe0a901 100644 --- a/frame/shielded-pool/src/lib.rs +++ b/frame/shielded-pool/src/lib.rs @@ -490,7 +490,7 @@ pub mod pallet { /// * `AmountTooSmall` - Amount is below minimum /// * `MerkleTreeFull` - No more space in the tree /// * `CommitmentAlreadyExists` - Duplicate commitment - /// * `InvalidMemoSize` - Encrypted memo is not exactly 168 bytes + /// * `InvalidMemoSize` - Encrypted memo is not exactly 180 bytes #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::shield())] pub fn shield( @@ -585,7 +585,7 @@ pub mod pallet { /// * `NullifierAlreadyUsed` - Double-spend attempt /// * `InvalidProof` - ZK proof verification failed /// * `FeeTooLow` - Fee is below `T::Relayer::min_relay_fee()` - /// * `InvalidMemoSize` - Any encrypted memo is not exactly 168 bytes + /// * `InvalidMemoSize` - Any encrypted memo is not exactly 180 bytes /// * `MemoCommitmentMismatch` - Number of memos does not match number of commitments #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::private_transfer(commitments.len() as u32))] @@ -600,8 +600,6 @@ pub mod pallet { asset_id: u32, fee: BalanceOf, relayer: Option, - // Circuit version the spent notes were created under; the proof is - // verified against this version's VK (not merely the active one). circuit_version: u32, ) -> DispatchResult { ensure_none(origin)?; diff --git a/frame/shielded-pool/src/operations/private_transfer.rs b/frame/shielded-pool/src/operations/private_transfer.rs index aa50c1b2..7bf1cb6d 100644 --- a/frame/shielded-pool/src/operations/private_transfer.rs +++ b/frame/shielded-pool/src/operations/private_transfer.rs @@ -178,7 +178,7 @@ mod tests { } fn short_memo() -> EncryptedMemo { - // 32 bytes — too short (not 168) + // 32 bytes — too short (not 180) EncryptedMemo::new(vec![0x01u8; 32]).unwrap() } diff --git a/frame/shielded-pool/src/operations/unshield.rs b/frame/shielded-pool/src/operations/unshield.rs index 2b66aa10..e0d52d12 100644 --- a/frame/shielded-pool/src/operations/unshield.rs +++ b/frame/shielded-pool/src/operations/unshield.rs @@ -71,7 +71,7 @@ impl UnshieldOperation { !CommitmentRepository::exists::(&change_comm), Error::::CommitmentAlreadyExists ); - // For partial unshield, memo must be valid size (176 bytes). + // For partial unshield, memo must be valid size (180 bytes). if !change_encrypted_memo.is_empty() { ensure!( change_encrypted_memo.is_valid_size(), @@ -899,7 +899,7 @@ mod tests { asset_id, 1000u128, Commitment::new([0x31u8; 32]), - FrameEncryptedMemo::from_bytes(&[0u8; 176]).unwrap(), + FrameEncryptedMemo::from_bytes(&[0u8; 180]).unwrap(), )); assert_eq!(tracked(asset_id), pool_physical()); assert_eq!(tracked(asset_id), 1000); @@ -933,7 +933,7 @@ mod tests { fee_note, 50u128, asset_id, - crate::types::EncryptedMemo::from_bytes(&[0u8; 176]).unwrap(), + crate::types::EncryptedMemo::from_bytes(&[0u8; 180]).unwrap(), vec![0x01u8; 128], signals, 1, diff --git a/frame/shielded-pool/src/types.rs b/frame/shielded-pool/src/types.rs index fcdac149..9a6e32f7 100644 --- a/frame/shielded-pool/src/types.rs +++ b/frame/shielded-pool/src/types.rs @@ -289,8 +289,8 @@ pub type DefaultMerklePath = MerklePath; // EncryptedMemo (concrete, FRAME-compatible — used in storage & extrinsics) // ════════════════════════════════════════════════════════════════════════════ -/// Max encrypted memo size: `nonce(12) + ciphertext(116) + MAC(16) + ephPk(32) = 176`. -pub const MAX_ENCRYPTED_MEMO_SIZE: u32 = 176; +/// Max encrypted memo size: `nonce(12) + ciphertext(120) + MAC(16) + ephPk(32) = 180`. +pub const MAX_ENCRYPTED_MEMO_SIZE: u32 = 180; /// Encrypted memo attached to a commitment (ChaCha20-Poly1305). #[derive( @@ -348,7 +348,7 @@ impl EncryptedMemo { } } pub fn ciphertext(&self) -> &[u8] { - // Invariant: see nonce(). ciphertext occupies bytes 12..128. + // Invariant: see nonce(). ciphertext occupies bytes 12..132. debug_assert_eq!( self.0.len(), MAX_ENCRYPTED_MEMO_SIZE as usize, @@ -356,14 +356,14 @@ impl EncryptedMemo { MAX_ENCRYPTED_MEMO_SIZE, self.0.len() ); - if self.0.len() >= 128 { - &self.0[12..128] + if self.0.len() >= 132 { + &self.0[12..132] } else { &[] } } pub fn tag(&self) -> &[u8] { - // Layout: nonce(0..12) | ciphertext(12..128) | tag/MAC(128..144) | ephPk(144..176) + // Layout: nonce(0..12) | ciphertext(12..132) | tag/MAC(132..148) | ephPk(148..180) debug_assert_eq!( self.0.len(), MAX_ENCRYPTED_MEMO_SIZE as usize, @@ -371,14 +371,14 @@ impl EncryptedMemo { MAX_ENCRYPTED_MEMO_SIZE, self.0.len() ); - if self.0.len() >= 144 { - &self.0[128..144] + if self.0.len() >= 148 { + &self.0[132..148] } else { &[] } } pub fn eph_pk(&self) -> &[u8] { - // Ephemeral BabyJubJub public key (packed, LE) occupies bytes 144..176. + // Ephemeral BabyJubJub public key (packed, LE) occupies bytes 148..180. debug_assert_eq!( self.0.len(), MAX_ENCRYPTED_MEMO_SIZE as usize, @@ -386,8 +386,8 @@ impl EncryptedMemo { MAX_ENCRYPTED_MEMO_SIZE, self.0.len() ); - if self.0.len() >= 176 { - &self.0[144..176] + if self.0.len() >= 180 { + &self.0[148..180] } else { &[] } @@ -663,7 +663,7 @@ mod tests { let bytes = [0x01u8; MAX_ENCRYPTED_MEMO_SIZE as usize]; let memo = EncryptedMemo::from_bytes(&bytes).unwrap(); assert_eq!(memo.nonce().len(), 12); - assert_eq!(memo.ciphertext().len(), 116); + assert_eq!(memo.ciphertext().len(), 120); assert_eq!(memo.tag().len(), 16); assert_eq!(memo.eph_pk().len(), 32); } diff --git a/primitives/encrypted-memo/CHANGELOG.md b/primitives/encrypted-memo/CHANGELOG.md index ec33cd39..53b359d0 100644 --- a/primitives/encrypted-memo/CHANGELOG.md +++ b/primitives/encrypted-memo/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to `orbinum-encrypted-memo` will be documented in this file. +## [0.7.0] - 2026-07-10 + +### Changed +- **`MemoData` now carries `circuit_version: u32`** as the last plaintext field, so a note's ZK circuit version travels inside the note itself (recovered on scan without an external lookup). Plaintext grows 116 → 120 bytes; encrypted wire format 176 → 180 bytes: `nonce(12) | ciphertext(120) | MAC(16) | ephPk(32)`. Layout: `circuit_version` at plaintext bytes `[116..120]` (u32 LE). +- `MemoData::new` / `new_without_counterparty` gain a trailing `circuit_version` argument. `MEMO_DATA_SIZE` 116 → 120; `MAX_ENCRYPTED_MEMO_SIZE` recomputes to 180. + +### Breaking +- Wire format changed: memos written by ≤ 0.6.0 (176 bytes) cannot be decoded by 0.7.0, and vice-versa. + ## [0.6.0] - 2026-05-14 ### Added diff --git a/primitives/encrypted-memo/Cargo.lock b/primitives/encrypted-memo/Cargo.lock index 22966035..7e623653 100644 --- a/primitives/encrypted-memo/Cargo.lock +++ b/primitives/encrypted-memo/Cargo.lock @@ -280,7 +280,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "orbinum-encrypted-memo" -version = "0.6.0" +version = "0.7.0" dependencies = [ "chacha20poly1305", "getrandom", diff --git a/primitives/encrypted-memo/Cargo.toml b/primitives/encrypted-memo/Cargo.toml index ed86612d..2d49ec59 100644 --- a/primitives/encrypted-memo/Cargo.toml +++ b/primitives/encrypted-memo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orbinum-encrypted-memo" -version = "0.6.0" +version = "0.7.0" authors = ["Orbinum Network "] edition = "2021" license = "Apache-2.0 OR GPL-3.0-or-later" diff --git a/primitives/encrypted-memo/src/lib.rs b/primitives/encrypted-memo/src/lib.rs index aa46c094..b85a64dd 100644 --- a/primitives/encrypted-memo/src/lib.rs +++ b/primitives/encrypted-memo/src/lib.rs @@ -13,12 +13,12 @@ //! //! // Symmetric mode: use viewing_key as shared_secret //! let keys = KeySet::from_spending_key(spending_key); -//! let memo = MemoData::new(1000, owner_pk, blinding, 0, [0u8; 32]); +//! let memo = MemoData::new(1000, owner_pk, blinding, 0, [0u8; 32], 1); //! let encrypted = encrypt_memo(&memo, &commitment, keys.viewing_key.as_bytes(), &nonce)?; //! let decrypted = decrypt_memo(&encrypted, &commitment, keys.viewing_key.as_bytes())?; //! //! // ECDH mode: derive shared_secret externally via BabyJubJub before calling decrypt_memo -//! // let eph_pk = &encrypted[144..176]; +//! // let eph_pk = &encrypted[148..180]; //! // let shared_secret = bjj_ecdh(ivsk_scalar, eph_pk); //! // let decrypted = decrypt_memo(&encrypted, &commitment, &shared_secret)?; //! ``` diff --git a/primitives/encrypted-memo/src/memo.rs b/primitives/encrypted-memo/src/memo.rs index aa3d7bfa..20f38dad 100644 --- a/primitives/encrypted-memo/src/memo.rs +++ b/primitives/encrypted-memo/src/memo.rs @@ -3,11 +3,13 @@ //! # Size layout //! //! ```text -//! Plaintext (MemoData): value_lo(8) | value_hi(8) | owner_pk(32) | blinding(32) | asset_id(4) | counterparty_pk(32) = 116 bytes -//! Encrypted wire format: nonce(12) | ciphertext(116) | MAC(16) | ephPk(32) = 176 bytes +//! Plaintext (MemoData): value_lo(8) | value_hi(8) | owner_pk(32) | blinding(32) | asset_id(4) | counterparty_pk(32) | circuit_version(4) = 120 bytes +//! Encrypted wire format: nonce(12) | ciphertext(120) | MAC(16) | ephPk(32) = 180 bytes //! ``` //! //! `value` is stored as a 128-bit LE unsigned integer (two u64 words: lo at [0..8), hi at [8..16)). +//! `circuit_version` is the ZK circuit version the note is spent under, carried inside the note +//! itself so a scan-recovered note stays spendable across a VK rotation without an external lookup. use alloc::vec::Vec; use chacha20poly1305::{ @@ -19,8 +21,8 @@ use crate::keys::derive_encryption_key; // ─── Constants ──────────────────────────────────────────────────────────────── -/// Plaintext memo size: `value_lo(8) + value_hi(8) + owner_pk(32) + blinding(32) + asset_id(4) + counterparty_pk(32)` -pub const MEMO_DATA_SIZE: usize = 116; +/// Plaintext memo size: `value_lo(8) + value_hi(8) + owner_pk(32) + blinding(32) + asset_id(4) + counterparty_pk(32) + circuit_version(4)` +pub const MEMO_DATA_SIZE: usize = 120; /// ChaCha20-Poly1305 nonce size. pub const NONCE_SIZE: usize = 12; @@ -31,7 +33,7 @@ pub const MAC_SIZE: usize = 16; /// Ephemeral BabyJubJub public key (packed LE) appended to the memo for ECDH recipient decryption. pub const EPH_PK_SIZE: usize = 32; -/// Maximum encrypted memo size: `nonce(12) + ciphertext(108) + MAC(16) + ephPk(32)` +/// Maximum encrypted memo size: `nonce(12) + ciphertext(120) + MAC(16) + ephPk(32)` = 180 pub const MAX_ENCRYPTED_MEMO_SIZE: usize = NONCE_SIZE + MEMO_DATA_SIZE + MAC_SIZE + EPH_PK_SIZE; /// Minimum encrypted memo size: `nonce(12) + MAC(16)` (zero-length plaintext) @@ -79,11 +81,12 @@ impl std::error::Error for MemoError {} /// Plaintext content of an encrypted note memo. /// -/// Serialized layout (116 bytes): `value_lo(8) | value_hi(8) | owner_pk(32) | blinding(32) | asset_id(4) | counterparty_pk(32)` +/// Serialized layout (120 bytes): `value_lo(8) | value_hi(8) | owner_pk(32) | blinding(32) | asset_id(4) | counterparty_pk(32) | circuit_version(4)` /// /// `value` is stored as a 128-bit LE integer (two u64 words: lo at [0..8), hi at [8..16)). /// `counterparty_pk` is the BabyJubJub Ax coordinate of the other party in the transaction /// (recipient's key in the change note; sender's key in the output note). Zero for shield/unshield. +/// `circuit_version` (u32 LE) is the ZK circuit version the note is spent under. #[derive(Clone, PartialEq, Eq, Debug)] #[cfg_attr( all(feature = "parity-scale-codec", feature = "scale-info"), @@ -104,6 +107,8 @@ pub struct MemoData { pub asset_id: u32, /// Counterparty's public key (32 bytes). Zero for shield/unshield notes. pub counterparty_pk: [u8; 32], + /// ZK circuit version the note is spent under (u32). Carried in the note itself. + pub circuit_version: u32, } impl MemoData { @@ -114,6 +119,7 @@ impl MemoData { blinding: [u8; 32], asset_id: u32, counterparty_pk: [u8; 32], + circuit_version: u32, ) -> Self { Self { value, @@ -121,6 +127,7 @@ impl MemoData { blinding, asset_id, counterparty_pk, + circuit_version, } } @@ -130,6 +137,7 @@ impl MemoData { owner_pk: [u8; 32], blinding: [u8; 32], asset_id: u32, + circuit_version: u32, ) -> Self { Self { value, @@ -137,10 +145,11 @@ impl MemoData { blinding, asset_id, counterparty_pk: [0u8; 32], + circuit_version, } } - /// Serializes to 116 bytes: value_lo(8) | value_hi(8) | owner_pk(32) | blinding(32) | asset_id(4) | counterparty_pk(32). + /// Serializes to 120 bytes: value_lo(8) | value_hi(8) | owner_pk(32) | blinding(32) | asset_id(4) | counterparty_pk(32) | circuit_version(4). pub fn to_bytes(&self) -> [u8; MEMO_DATA_SIZE] { let mut bytes = [0u8; MEMO_DATA_SIZE]; let value_lo = (self.value & 0xffff_ffff_ffff_ffff) as u64; @@ -151,10 +160,11 @@ impl MemoData { bytes[48..80].copy_from_slice(&self.blinding); bytes[80..84].copy_from_slice(&self.asset_id.to_le_bytes()); bytes[84..116].copy_from_slice(&self.counterparty_pk); + bytes[116..120].copy_from_slice(&self.circuit_version.to_le_bytes()); bytes } - /// Deserializes from exactly 116 bytes. + /// Deserializes from exactly 120 bytes. pub fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != MEMO_DATA_SIZE { return Err(MemoError::InvalidNoteData); @@ -175,12 +185,16 @@ impl MemoData { ); let mut counterparty_pk = [0u8; 32]; counterparty_pk.copy_from_slice(&bytes[84..116]); + let circuit_version = u32::from_le_bytes( + bytes[116..120].try_into().map_err(|_| MemoError::InvalidNoteData)?, + ); Ok(Self { value, owner_pk, blinding, asset_id, counterparty_pk, + circuit_version, }) } } @@ -194,7 +208,7 @@ pub fn is_valid_encrypted_memo(data: &[u8]) -> bool { /// Encrypts memo data using ChaCha20-Poly1305. /// -/// Returns `nonce(12) || ciphertext(116) || MAC(16) || ephPk(32)` = 176 bytes. +/// Returns `nonce(12) || ciphertext(120) || MAC(16) || ephPk(32)` = 180 bytes. /// The ephemeral public key is set to all-zeros (symmetric / public-note mode). /// For full ECDH, compute the shared secret externally with BabyJubJub and pass it as /// `shared_secret`. The caller is responsible for appending the real ephPk after the call @@ -238,10 +252,10 @@ pub fn encrypt_memo_random( /// Decrypts an encrypted memo. /// -/// Expects `nonce(12) || ciphertext + MAC(132) || ephPk(32)` (176 bytes). -/// The trailing `ephPk` bytes are stripped; the first 144 bytes are fed to ChaCha20-Poly1305. +/// Expects `nonce(12) || ciphertext + MAC(136) || ephPk(32)` (180 bytes). +/// The trailing `ephPk` bytes are stripped; the first 148 bytes are fed to ChaCha20-Poly1305. /// -/// **ECDH callers**: extract `ephPk = encrypted[144..176]`, derive the BabyJubJub ECDH +/// **ECDH callers**: extract `ephPk = encrypted[148..180]`, derive the BabyJubJub ECDH /// shared secret externally (x-coordinate of `BJJ_mul(ephPk_point, ivsk_scalar)`), then /// pass that 32-byte value as `shared_secret`. /// @@ -289,7 +303,7 @@ mod tests { use super::*; fn sample_memo() -> MemoData { - MemoData::new(1000, [1u8; 32], [2u8; 32], 5, [6u8; 32]) + MemoData::new(1000, [1u8; 32], [2u8; 32], 5, [6u8; 32], 1) } const COMMITMENT: [u8; 32] = [3u8; 32]; @@ -300,7 +314,7 @@ mod tests { #[test] fn layout_constants_are_coherent() { - assert_eq!(MEMO_DATA_SIZE, 116); + assert_eq!(MEMO_DATA_SIZE, 120); assert_eq!(NONCE_SIZE, 12); assert_eq!(MAC_SIZE, 16); assert_eq!(EPH_PK_SIZE, 32); @@ -309,7 +323,7 @@ mod tests { NONCE_SIZE + MEMO_DATA_SIZE + MAC_SIZE + EPH_PK_SIZE ); assert_eq!(MIN_ENCRYPTED_MEMO_SIZE, NONCE_SIZE + MAC_SIZE); - assert_eq!(MAX_ENCRYPTED_MEMO_SIZE, 176); + assert_eq!(MAX_ENCRYPTED_MEMO_SIZE, 180); assert_eq!(MIN_ENCRYPTED_MEMO_SIZE, 28); } @@ -352,7 +366,7 @@ mod tests { #[test] fn memo_data_field_layout() { let val: u128 = u64::MAX as u128; - let m = MemoData::new(val, [0xAAu8; 32], [0xBBu8; 32], u32::MAX, [0xCCu8; 32]); + let m = MemoData::new(val, [0xAAu8; 32], [0xBBu8; 32], u32::MAX, [0xCCu8; 32], 7); let b = m.to_bytes(); // value_lo at [0..8) = lower 64 bits of val assert_eq!(b[0..8], (val as u64).to_le_bytes()); @@ -368,14 +382,14 @@ mod tests { fn memo_data_u128_value_roundtrip() { // 50 * 10^18 — overflows u64 let big_val: u128 = 50_000_000_000_000_000_000u128; - let m = MemoData::new(big_val, [0x01u8; 32], [0x02u8; 32], 0, [0u8; 32]); + let m = MemoData::new(big_val, [0x01u8; 32], [0x02u8; 32], 0, [0u8; 32], 1); let dec = MemoData::from_bytes(&m.to_bytes()).unwrap(); assert_eq!(dec.value, big_val); } #[test] fn new_without_counterparty_zeroes_counterparty_pk() { - let m = MemoData::new_without_counterparty(42, [0x01u8; 32], [0x02u8; 32], 1); + let m = MemoData::new_without_counterparty(42, [0x01u8; 32], [0x02u8; 32], 1, 1); assert_eq!(m.counterparty_pk, [0u8; 32]); let b = m.to_bytes(); assert_eq!(b[84..116], [0u8; 32]); @@ -476,7 +490,7 @@ mod tests { #[test] fn counterparty_pk_nonzero_roundtrip() { let cpk = [0xABu8; 32]; - let m = MemoData::new(1_000, [0x01u8; 32], [0x02u8; 32], 7, cpk); + let m = MemoData::new(1_000, [0x01u8; 32], [0x02u8; 32], 7, cpk, 1); let enc = encrypt_memo(&m, &COMMITMENT, &VK, &NONCE).unwrap(); let dec = decrypt_memo(&enc, &COMMITMENT, &VK).unwrap(); assert_eq!(dec.counterparty_pk, cpk); From a51854f54a4d25a6a24e7d869e6362b51759bfd8 Mon Sep 17 00:00:00 2001 From: nol4lej Date: Sat, 11 Jul 2026 00:20:26 +0000 Subject: [PATCH 2/5] chore(weights): regenerate pallet-shielded-pool weights for 180-byte memo --- frame/shielded-pool/src/weights.rs | 114 ++++++++++++++--------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/frame/shielded-pool/src/weights.rs b/frame/shielded-pool/src/weights.rs index 5c8e783e..072ca688 100644 --- a/frame/shielded-pool/src/weights.rs +++ b/frame/shielded-pool/src/weights.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for pallet_shielded_pool //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 53.0.0 -//! DATE: 2026-07-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `ubuntu-32gb-fsn1-1`, CPU: `AMD EPYC-Genoa Processor` //! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -55,7 +55,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `ShieldedPool::Assets` (r:1 w:0) /// Proof: `ShieldedPool::Assets` (`max_values`: None, `max_size`: Some(166), added: 2641, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::CommitmentMemos` (r:1 w:1) - /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(230), added: 2705, mode: `MaxEncodedLen`) /// Storage: `Balances::TotalIssuance` (r:1 w:0) /// Proof: `Balances::TotalIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) @@ -90,15 +90,15 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1127` // Estimated: `4687` - // Minimum execution time: 773_840_000 picoseconds. - Weight::from_parts(785_138_000, 4687) + // Minimum execution time: 748_837_000 picoseconds. + Weight::from_parts(763_347_000, 4687) .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)) } /// Storage: `ShieldedPool::Assets` (r:1 w:0) /// Proof: `ShieldedPool::Assets` (`max_values`: None, `max_size`: Some(166), added: 2641, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::CommitmentMemos` (r:20 w:20) - /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(230), added: 2705, mode: `MaxEncodedLen`) /// Storage: `Balances::TotalIssuance` (r:1 w:0) /// Proof: `Balances::TotalIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) @@ -133,16 +133,16 @@ impl WeightInfo for SubstrateWeight { fn shield_batch(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1127` - // Estimated: `4687 + n * (2701 ±0)` - // Minimum execution time: 761_244_000 picoseconds. - Weight::from_parts(776_987_000, 4687) - // Standard Error: 1_644_747 - .saturating_add(Weight::from_parts(701_980_415, 0).saturating_mul(n.into())) + // Estimated: `4687 + n * (2705 ±0)` + // Minimum execution time: 770_687_000 picoseconds. + Weight::from_parts(7_926_673, 4687) + // Standard Error: 1_395_437 + .saturating_add(Weight::from_parts(760_833_028, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(9_u64)) .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2701).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2705).saturating_mul(n.into())) } /// Storage: `ShieldedPool::Assets` (r:1 w:0) /// Proof: `ShieldedPool::Assets` (`max_values`: None, `max_size`: Some(166), added: 2641, mode: `MaxEncodedLen`) @@ -159,7 +159,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `ShieldedPool::MerkleTreeSize` (r:1 w:1) /// Proof: `ShieldedPool::MerkleTreeSize` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::CommitmentMemos` (r:2 w:2) - /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(230), added: 2705, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::MerkleTreeFrontier` (r:1 w:1) /// Proof: `ShieldedPool::MerkleTreeFrontier` (`max_values`: Some(1), `max_size`: Some(640), added: 1135, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::PoseidonRoot` (r:1 w:1) @@ -186,16 +186,16 @@ impl WeightInfo for SubstrateWeight { fn private_transfer(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1182` - // Estimated: `4687 + n * (2701 ±0)` - // Minimum execution time: 702_703_000 picoseconds. - Weight::from_parts(733_295_000, 4687) - // Standard Error: 4_803_642 - .saturating_add(Weight::from_parts(43_395_069, 0).saturating_mul(n.into())) + // Estimated: `4687 + n * (2705 ±0)` + // Minimum execution time: 725_705_000 picoseconds. + Weight::from_parts(33_902_291, 4687) + // Standard Error: 2_472_578 + .saturating_add(Weight::from_parts(728_680_304, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(9_u64)) .saturating_add(T::DbWeight::get().writes((5_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2701).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2705).saturating_mul(n.into())) } /// Storage: `ShieldedPool::Assets` (r:1 w:0) /// Proof: `ShieldedPool::Assets` (`max_values`: None, `max_size`: Some(166), added: 2641, mode: `MaxEncodedLen`) @@ -229,8 +229,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `903` // Estimated: `6196` - // Minimum execution time: 105_837_000 picoseconds. - Weight::from_parts(123_090_000, 6196) + // Minimum execution time: 101_489_000 picoseconds. + Weight::from_parts(104_063_000, 6196) .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(8_u64)) } @@ -250,8 +250,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `239` // Estimated: `3631` - // Minimum execution time: 16_512_000 picoseconds. - Weight::from_parts(17_735_000, 3631) + // Minimum execution time: 16_063_000 picoseconds. + Weight::from_parts(17_194_000, 3631) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -269,8 +269,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `328` // Estimated: `3631` - // Minimum execution time: 15_602_000 picoseconds. - Weight::from_parts(16_614_000, 3631) + // Minimum execution time: 16_723_000 picoseconds. + Weight::from_parts(17_503_000, 3631) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -288,8 +288,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `328` // Estimated: `3631` - // Minimum execution time: 16_192_000 picoseconds. - Weight::from_parts(17_184_000, 3631) + // Minimum execution time: 16_253_000 picoseconds. + Weight::from_parts(16_853_000, 3631) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -308,7 +308,7 @@ impl WeightInfo for SubstrateWeight { /// Storage: `ShieldedPool::MerkleTreeSize` (r:1 w:1) /// Proof: `ShieldedPool::MerkleTreeSize` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::CommitmentMemos` (r:1 w:1) - /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(230), added: 2705, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::MerkleTreeFrontier` (r:1 w:1) /// Proof: `ShieldedPool::MerkleTreeFrontier` (`max_values`: Some(1), `max_size`: Some(640), added: 1135, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::PoseidonRoot` (r:1 w:1) @@ -327,8 +327,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1085` // Estimated: `4687` - // Minimum execution time: 714_679_000 picoseconds. - Weight::from_parts(915_558_000, 4687) + // Minimum execution time: 740_796_000 picoseconds. + Weight::from_parts(745_853_000, 4687) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(12_u64)) } @@ -339,7 +339,7 @@ impl WeightInfo for () { /// Storage: `ShieldedPool::Assets` (r:1 w:0) /// Proof: `ShieldedPool::Assets` (`max_values`: None, `max_size`: Some(166), added: 2641, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::CommitmentMemos` (r:1 w:1) - /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(230), added: 2705, mode: `MaxEncodedLen`) /// Storage: `Balances::TotalIssuance` (r:1 w:0) /// Proof: `Balances::TotalIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) @@ -374,15 +374,15 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1127` // Estimated: `4687` - // Minimum execution time: 773_840_000 picoseconds. - Weight::from_parts(785_138_000, 4687) + // Minimum execution time: 748_837_000 picoseconds. + Weight::from_parts(763_347_000, 4687) .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().writes(13_u64)) } /// Storage: `ShieldedPool::Assets` (r:1 w:0) /// Proof: `ShieldedPool::Assets` (`max_values`: None, `max_size`: Some(166), added: 2641, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::CommitmentMemos` (r:20 w:20) - /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(230), added: 2705, mode: `MaxEncodedLen`) /// Storage: `Balances::TotalIssuance` (r:1 w:0) /// Proof: `Balances::TotalIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) @@ -417,16 +417,16 @@ impl WeightInfo for () { fn shield_batch(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1127` - // Estimated: `4687 + n * (2701 ±0)` - // Minimum execution time: 761_244_000 picoseconds. - Weight::from_parts(776_987_000, 4687) - // Standard Error: 1_644_747 - .saturating_add(Weight::from_parts(701_980_415, 0).saturating_mul(n.into())) + // Estimated: `4687 + n * (2705 ±0)` + // Minimum execution time: 770_687_000 picoseconds. + Weight::from_parts(7_926_673, 4687) + // Standard Error: 1_395_437 + .saturating_add(Weight::from_parts(760_833_028, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(9_u64)) .saturating_add(RocksDbWeight::get().writes((4_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2701).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2705).saturating_mul(n.into())) } /// Storage: `ShieldedPool::Assets` (r:1 w:0) /// Proof: `ShieldedPool::Assets` (`max_values`: None, `max_size`: Some(166), added: 2641, mode: `MaxEncodedLen`) @@ -443,7 +443,7 @@ impl WeightInfo for () { /// Storage: `ShieldedPool::MerkleTreeSize` (r:1 w:1) /// Proof: `ShieldedPool::MerkleTreeSize` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::CommitmentMemos` (r:2 w:2) - /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(230), added: 2705, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::MerkleTreeFrontier` (r:1 w:1) /// Proof: `ShieldedPool::MerkleTreeFrontier` (`max_values`: Some(1), `max_size`: Some(640), added: 1135, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::PoseidonRoot` (r:1 w:1) @@ -470,16 +470,16 @@ impl WeightInfo for () { fn private_transfer(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1182` - // Estimated: `4687 + n * (2701 ±0)` - // Minimum execution time: 702_703_000 picoseconds. - Weight::from_parts(733_295_000, 4687) - // Standard Error: 4_803_642 - .saturating_add(Weight::from_parts(43_395_069, 0).saturating_mul(n.into())) + // Estimated: `4687 + n * (2705 ±0)` + // Minimum execution time: 725_705_000 picoseconds. + Weight::from_parts(33_902_291, 4687) + // Standard Error: 2_472_578 + .saturating_add(Weight::from_parts(728_680_304, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(9_u64)) .saturating_add(RocksDbWeight::get().writes((5_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2701).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2705).saturating_mul(n.into())) } /// Storage: `ShieldedPool::Assets` (r:1 w:0) /// Proof: `ShieldedPool::Assets` (`max_values`: None, `max_size`: Some(166), added: 2641, mode: `MaxEncodedLen`) @@ -513,8 +513,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `903` // Estimated: `6196` - // Minimum execution time: 105_837_000 picoseconds. - Weight::from_parts(123_090_000, 6196) + // Minimum execution time: 101_489_000 picoseconds. + Weight::from_parts(104_063_000, 6196) .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(8_u64)) } @@ -534,8 +534,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `239` // Estimated: `3631` - // Minimum execution time: 16_512_000 picoseconds. - Weight::from_parts(17_735_000, 3631) + // Minimum execution time: 16_063_000 picoseconds. + Weight::from_parts(17_194_000, 3631) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -553,8 +553,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `328` // Estimated: `3631` - // Minimum execution time: 15_602_000 picoseconds. - Weight::from_parts(16_614_000, 3631) + // Minimum execution time: 16_723_000 picoseconds. + Weight::from_parts(17_503_000, 3631) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -572,8 +572,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `328` // Estimated: `3631` - // Minimum execution time: 16_192_000 picoseconds. - Weight::from_parts(17_184_000, 3631) + // Minimum execution time: 16_253_000 picoseconds. + Weight::from_parts(16_853_000, 3631) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -592,7 +592,7 @@ impl WeightInfo for () { /// Storage: `ShieldedPool::MerkleTreeSize` (r:1 w:1) /// Proof: `ShieldedPool::MerkleTreeSize` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::CommitmentMemos` (r:1 w:1) - /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(226), added: 2701, mode: `MaxEncodedLen`) + /// Proof: `ShieldedPool::CommitmentMemos` (`max_values`: None, `max_size`: Some(230), added: 2705, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::MerkleTreeFrontier` (r:1 w:1) /// Proof: `ShieldedPool::MerkleTreeFrontier` (`max_values`: Some(1), `max_size`: Some(640), added: 1135, mode: `MaxEncodedLen`) /// Storage: `ShieldedPool::PoseidonRoot` (r:1 w:1) @@ -611,8 +611,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1085` // Estimated: `4687` - // Minimum execution time: 714_679_000 picoseconds. - Weight::from_parts(915_558_000, 4687) + // Minimum execution time: 740_796_000 picoseconds. + Weight::from_parts(745_853_000, 4687) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(12_u64)) } From 3ac128ba8f91909756e80af19c35252d3613874c Mon Sep 17 00:00:00 2001 From: nol4lej Date: Fri, 10 Jul 2026 20:27:52 -0400 Subject: [PATCH 3/5] chore(weights): regenerate pallet-shielded-pool weights for 180-byte memo --- frame/shielded-pool/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/shielded-pool/CHANGELOG.md b/frame/shielded-pool/CHANGELOG.md index 4c407126..e21f79ee 100644 --- a/frame/shielded-pool/CHANGELOG.md +++ b/frame/shielded-pool/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to `pallet-shielded-pool` will be documented in this file. ### Changed - **`MAX_ENCRYPTED_MEMO_SIZE` 176 → 180** to accommodate the new `circuit_version` field carried in the memo plaintext (see `orbinum-encrypted-memo` 0.7.0). `EncryptedMemo` wire offsets updated: `ciphertext [12..132]`, `tag [132..148]`, `ephPk [148..180]`. The pallet still treats the memo as opaque bytes — only the length bound and slice offsets change. -- **Weights regenerated** — the larger memo grows `CommitmentMemos`' `MaxEncodedLen`, changing the `proof_size` of every extrinsic that reads/writes it (`shield`, `shield_batch`, `private_transfer`, `unshield`). Runtime `spec_version` 5 → 6. +- **Weights regenerated** (`src/weights.rs`) — the larger memo raises `CommitmentMemos`' `MaxEncodedLen` (`max_size` 226 → 230), changing the `proof_size` of every extrinsic that reads/writes it (`shield`, `shield_batch`, `private_transfer`, `unshield`, `claim_shielded_fees`). Benchmarked on the reference host (`ubuntu-32gb-fsn1-1`, AMD EPYC-Genoa) with `--steps=50 --repeat=20 --chain=dev`. Runtime `spec_version` 5 → 6. ### Breaking - `EncryptedMemo` now requires exactly 180 bytes; 176-byte memos are rejected. Consensus-affecting (covered by the `spec_version` bump). `transaction_version` is intentionally **not** bumped: the extrinsic SCALE codec is unchanged (the memo arg is still `BoundedVec` — only its max bound rose), so offline-signed extrinsics still decode identically. From 40daa9e71fd178255544cf56a74bd769a77a0d4e Mon Sep 17 00:00:00 2001 From: nol4lej Date: Fri, 10 Jul 2026 20:29:48 -0400 Subject: [PATCH 4/5] feat(runtime): bump spec_version 5->6 for 180-byte encrypted memo --- template/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index 8d171e78..346a85d3 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -197,7 +197,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: Cow::Borrowed("orbinum"), impl_name: Cow::Borrowed("orbinum"), authoring_version: 1, - spec_version: 5, + spec_version: 6, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 3, From 5faf0e05bed8219ece577cd68a995bc5728debd5 Mon Sep 17 00:00:00 2001 From: nol4lej Date: Fri, 10 Jul 2026 21:52:10 -0400 Subject: [PATCH 5/5] test(shielded-pool-precompile): update memo literals to 180 bytes --- .../evm/precompile/shielded-pool/src/tests.rs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/frame/evm/precompile/shielded-pool/src/tests.rs b/frame/evm/precompile/shielded-pool/src/tests.rs index 871f7586..533f4704 100644 --- a/frame/evm/precompile/shielded-pool/src/tests.rs +++ b/frame/evm/precompile/shielded-pool/src/tests.rs @@ -187,7 +187,7 @@ fn encode_unshield( // ─── Convenience: shield then return the current Merkle root ───────────────── fn do_shield(commitment: [u8; 32], value: u128) { - let input = encode_shield(0, commitment, &[0xAB; 176]); + let input = encode_shield(0, commitment, &[0xAB; 180]); let mut h = MockHandle::with_value(input, value); assert_success(ShieldedPoolPrecompile::::execute(&mut h)); } @@ -379,7 +379,7 @@ fn shield_rejects_truncated_input() { fn shield_rejects_below_min_amount() { // MinShieldAmount = 100; sending value = 1 should be rejected by the pallet. new_test_ext().execute_with(|| { - let input = encode_shield(0, [0x11; 32], &[0xAB; 168]); + let input = encode_shield(0, [0x11; 32], &[0xAB; 180]); let mut h = MockHandle::with_value(input, 1); expect_error(ShieldedPoolPrecompile::::execute(&mut h)); }); @@ -389,7 +389,7 @@ fn shield_rejects_below_min_amount() { fn shield_stores_commitment_and_updates_balance() { new_test_ext().execute_with(|| { let commitment = [0x11; 32]; - let input = encode_shield(0, commitment, &[0xAB; 176]); + let input = encode_shield(0, commitment, &[0xAB; 180]); let mut h = MockHandle::with_value(input, 1_000); assert_success(ShieldedPoolPrecompile::::execute(&mut h)); @@ -433,7 +433,7 @@ fn shield_updates_merkle_root_after_each_insertion() { #[test] fn shield_with_zero_value_rejected() { new_test_ext().execute_with(|| { - let input = encode_shield(0, [0xAA; 32], &[0x00; 168]); + let input = encode_shield(0, [0xAA; 32], &[0x00; 180]); let mut h = MockHandle::with_value(input, 0); expect_error(ShieldedPoolPrecompile::::execute(&mut h)); }); @@ -462,7 +462,7 @@ fn private_transfer_rejects_empty_proof() { root, &[[0x11; 32], [0x22; 32]], &[[0x33; 32], [0x44; 32]], - &[vec![0xAA; 176], vec![0xBB; 176]], + &[vec![0xAA; 180], vec![0xBB; 180]], 0, 0, 1, @@ -505,7 +505,7 @@ fn private_transfer_rejects_mismatched_nullifier_commitment_count() { root, &[[0x11; 32], [0x22; 32]], // 2 nullifiers &[[0x33; 32]], // 1 commitment - &[vec![0xAA; 176]], // 1 memo + &[vec![0xAA; 180]], // 1 memo 0, 0, 1, @@ -526,7 +526,7 @@ fn private_transfer_rejects_mismatched_commitment_memo_count() { root, &[[0x11; 32], [0x22; 32]], // 2 nullifiers &[[0x33; 32], [0x44; 32]], // 2 commitments - &[vec![0xAA; 176]], // 1 memo — mismatch + &[vec![0xAA; 180]], // 1 memo — mismatch 0, 0, 1, @@ -551,7 +551,7 @@ fn private_transfer_happy_path() { root, &[nullifier_1, nullifier_2], &[commitment_1, commitment_2], - &[vec![0xAA; 176], vec![0xBB; 176]], + &[vec![0xAA; 180], vec![0xBB; 180]], 0, 0, 1, @@ -594,7 +594,7 @@ fn private_transfer_rejects_double_spend() { root, &[nullifier, [0x02; 32]], &[[0x03; 32], [0x04; 32]], - &[vec![0xAA; 176], vec![0xBB; 176]], + &[vec![0xAA; 180], vec![0xBB; 180]], 0, 0, 1, @@ -619,7 +619,7 @@ fn private_transfer_root_updates_after_outputs() { root_before, &[[0x11; 32], [0x22; 32]], &[[0x33; 32], [0x44; 32]], - &[vec![0xAA; 176], vec![0xBB; 176]], + &[vec![0xAA; 180], vec![0xBB; 180]], 0, 0, 1, @@ -825,7 +825,7 @@ fn full_lifecycle_shield_transfer_unshield() { root_1, &[nullifier_in, [0x00; 32]], &[commitment_out_1, commitment_out_2], - &[vec![0xAA; 176], vec![0xBB; 176]], + &[vec![0xAA; 180], vec![0xBB; 180]], 0, 0, 1,