From d066cd398d6a8c99876fdd5550f5f2ff0e8ff0c6 Mon Sep 17 00:00:00 2001 From: illuzen Date: Wed, 27 May 2026 20:24:41 +0900 Subject: [PATCH 1/2] simplify poseidon usage --- Cargo.lock | 58 ++++------- Cargo.toml | 3 +- node/Cargo.toml | 1 - pallets/frame-system/src/mocking.rs | 6 +- pallets/mining-rewards/Cargo.toml | 3 - pallets/wormhole/Cargo.toml | 2 - pallets/wormhole/src/lib.rs | 16 ++- pallets/wormhole/src/mock.rs | 7 +- primitives/dilithium-crypto/Cargo.toml | 4 +- primitives/header/Cargo.toml | 3 +- primitives/header/src/lib.rs | 134 +++++++++---------------- primitives/wormhole/Cargo.toml | 2 - runtime/Cargo.toml | 2 - runtime/src/lib.rs | 20 ++-- 14 files changed, 88 insertions(+), 173 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41e77fc7..f502a4f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6616,7 +6616,6 @@ dependencies = [ "pallet-balances", "pallet-treasury", "parity-scale-codec", - "qp-poseidon", "qp-wormhole", "scale-info", "sp-consensus-qpow", @@ -6683,7 +6682,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "primitive-types 0.13.1", - "qp-poseidon-core", + "qp-poseidon-core 1.5.0", "qpow-math", "scale-info", "sp-arithmetic", @@ -6901,8 +6900,7 @@ dependencies = [ "qp-dilithium-crypto", "qp-header", "qp-plonky2", - "qp-poseidon", - "qp-poseidon-core", + "qp-poseidon-core 1.5.0", "qp-wormhole", "qp-wormhole-circuit", "qp-wormhole-circuit-builder", @@ -6926,7 +6924,7 @@ dependencies = [ "frame-system", "log", "parity-scale-codec", - "qp-poseidon-core", + "qp-poseidon-core 1.5.0", "scale-info", "serde", "sp-api", @@ -8118,8 +8116,7 @@ dependencies = [ "env_logger", "log", "parity-scale-codec", - "qp-poseidon", - "qp-poseidon-core", + "qp-poseidon-core 1.5.0", "qp-rusty-crystals-dilithium", "qp-rusty-crystals-hdwallet", "scale-info", @@ -8138,8 +8135,7 @@ dependencies = [ "p3-field", "p3-goldilocks", "parity-scale-codec", - "qp-poseidon", - "qp-poseidon-core", + "qp-poseidon-core 1.5.0", "scale-info", "serde", "serde_json", @@ -8256,26 +8252,6 @@ dependencies = [ "unroll", ] -[[package]] -name = "qp-poseidon" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edc2f8ae69b5424cb407b9f4037f02910bedbd39e761d58f583d1d375d42d8f" -dependencies = [ - "log", - "p3-field", - "p3-goldilocks", - "parity-scale-codec", - "qp-poseidon-constants", - "qp-poseidon-core", - "scale-info", - "serde", - "sp-core", - "sp-runtime", - "sp-storage", - "sp-trie", -] - [[package]] name = "qp-poseidon-constants" version = "1.1.0" @@ -8303,6 +8279,17 @@ dependencies = [ "rand_chacha 0.9.0", ] +[[package]] +name = "qp-poseidon-core" +version = "1.5.0" +dependencies = [ + "p3-field", + "p3-goldilocks", + "p3-poseidon2", + "p3-symmetric", + "qp-poseidon-constants", +] + [[package]] name = "qp-rusty-crystals-dilithium" version = "2.4.0" @@ -8323,7 +8310,7 @@ dependencies = [ "hex", "hex-literal", "hmac 0.12.1", - "qp-poseidon-core", + "qp-poseidon-core 1.4.0", "qp-rusty-crystals-dilithium", "serde", "serde_json", @@ -8347,8 +8334,7 @@ name = "qp-wormhole" version = "0.1.0" dependencies = [ "parity-scale-codec", - "qp-poseidon", - "qp-poseidon-core", + "qp-poseidon-core 1.5.0", "sp-consensus-qpow", "sp-core", "sp-runtime", @@ -8442,7 +8428,7 @@ dependencies = [ "hex", "qp-plonky2", "qp-poseidon-constants", - "qp-poseidon-core", + "qp-poseidon-core 1.4.0", "qp-wormhole-inputs", "rand 0.8.5", "serde", @@ -8456,7 +8442,7 @@ dependencies = [ "log", "primitive-types 0.13.1", "proptest", - "qp-poseidon-core", + "qp-poseidon-core 1.5.0", ] [[package]] @@ -8504,7 +8490,6 @@ dependencies = [ "parity-scale-codec", "prometheus", "qp-dilithium-crypto", - "qp-poseidon", "qp-rusty-crystals-dilithium", "qp-rusty-crystals-hdwallet", "qp-wormhole", @@ -8587,8 +8572,7 @@ dependencies = [ "qp-dilithium-crypto", "qp-header", "qp-high-security", - "qp-poseidon", - "qp-poseidon-core", + "qp-poseidon-core 1.5.0", "qp-scheduler", "qp-wormhole", "qp-wormhole-verifier", diff --git a/Cargo.toml b/Cargo.toml index 90eb9690..c8349615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -151,8 +151,7 @@ sp-consensus-qpow = { path = "./primitives/consensus/qpow", default-features = f # Quantus network dependencies qp-plonky2 = { version = "1.4.1", default-features = false } -qp-poseidon = { version = "1.4.0", default-features = false } -qp-poseidon-core = { version = "1.4.0", default-features = false } +qp-poseidon-core = { path = "../qp-poseidon/core", default-features = false } qp-rusty-crystals-dilithium = { version = "2.4.0", default-features = false } qp-rusty-crystals-hdwallet = { version = "2.3.1" } qp-wormhole-circuit = { version = "2.0.1", default-features = false } diff --git a/node/Cargo.toml b/node/Cargo.toml index 3761d166..4b3b5bec 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -40,7 +40,6 @@ pallet-zk-tree.default-features = true pallet-zk-tree.workspace = true prometheus.workspace = true qp-dilithium-crypto = { workspace = true } -qp-poseidon.workspace = true qp-rusty-crystals-dilithium.workspace = true qp-rusty-crystals-hdwallet.workspace = true qp-wormhole.workspace = true diff --git a/pallets/frame-system/src/mocking.rs b/pallets/frame-system/src/mocking.rs index 6a124786..030ddc82 100644 --- a/pallets/frame-system/src/mocking.rs +++ b/pallets/frame-system/src/mocking.rs @@ -28,16 +28,16 @@ pub type MockUncheckedExtrinsic = generic::Unchec >; pub type MockBlock = generic::Block< - qp_header::Header, + qp_header::Header, MockUncheckedExtrinsic, >; pub type MockBlockU32 = generic::Block< - qp_header::Header, + qp_header::Header, MockUncheckedExtrinsic, >; pub type MockBlockU128 = generic::Block< - qp_header::Header, + qp_header::Header, MockUncheckedExtrinsic, >; diff --git a/pallets/mining-rewards/Cargo.toml b/pallets/mining-rewards/Cargo.toml index 37e8b760..1c19cc26 100644 --- a/pallets/mining-rewards/Cargo.toml +++ b/pallets/mining-rewards/Cargo.toml @@ -23,7 +23,6 @@ frame-support.workspace = true frame-system.workspace = true log.workspace = true pallet-treasury = { path = "../treasury", default-features = false } -qp-poseidon.workspace = true qp-wormhole.workspace = true scale-info = { workspace = true, default-features = false, features = ["derive"] } sp-consensus-qpow.workspace = true @@ -32,7 +31,6 @@ sp-runtime.workspace = true [dev-dependencies] pallet-balances.features = ["std"] pallet-balances.workspace = true -qp-poseidon.workspace = true sp-core.workspace = true sp-io.workspace = true @@ -49,7 +47,6 @@ std = [ "frame-support/std", "frame-system/std", "pallet-treasury/std", - "qp-poseidon/std", "qp-wormhole/std", "scale-info/std", "sp-consensus-qpow/std", diff --git a/pallets/wormhole/Cargo.toml b/pallets/wormhole/Cargo.toml index e65b0586..9e64b5a4 100644 --- a/pallets/wormhole/Cargo.toml +++ b/pallets/wormhole/Cargo.toml @@ -18,7 +18,6 @@ log.workspace = true pallet-balances.workspace = true pallet-zk-tree.workspace = true qp-header = { workspace = true, features = ["serde"] } -qp-poseidon.workspace = true qp-wormhole.workspace = true qp-wormhole-verifier = { workspace = true, default-features = false } scale-info = { workspace = true, default-features = false, features = [ @@ -68,7 +67,6 @@ std = [ "pallet-balances/std", "pallet-zk-tree/std", "qp-header/std", - "qp-poseidon/std", "qp-wormhole-verifier/std", "qp-wormhole/std", "scale-info/std", diff --git a/pallets/wormhole/src/lib.rs b/pallets/wormhole/src/lib.rs index 9b4ff8fe..024dc9de 100644 --- a/pallets/wormhole/src/lib.rs +++ b/pallets/wormhole/src/lib.rs @@ -4,7 +4,6 @@ extern crate alloc; use lazy_static::lazy_static; pub use pallet::*; -pub use qp_poseidon::ToFelts; use qp_wormhole_verifier::WormholeVerifier; #[cfg(feature = "runtime-benchmarks")] @@ -37,7 +36,7 @@ pub const SCALE_DOWN_FACTOR: u128 = 10_000_000_000; #[frame_support::pallet] pub mod pallet { - use crate::{ToFelts, WeightInfo}; + use crate::WeightInfo; use alloc::vec::Vec; use codec::Decode; use frame_support::{ @@ -119,12 +118,11 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - /// Native balance type with ToFelts bound for Poseidon hashing in transfer proofs. + /// Native balance type for transfer proofs. type NativeBalance: Parameter + Member + Default + Copy - + ToFelts + MaxEncodedLen + sp_runtime::traits::AtLeast32BitUnsigned + sp_runtime::traits::CheckedAdd @@ -146,8 +144,8 @@ pub mod pallet { > + fungibles::Mutate<::AccountId> + fungibles::Create<::AccountId>; - /// Asset ID type with bounds needed for Poseidon hashing in transfer proofs. - type AssetId: Parameter + Member + Default + From + Clone + ToFelts + MaxEncodedLen; + /// Asset ID type for transfer proofs. + type AssetId: Parameter + Member + Default + From + Clone + MaxEncodedLen; /// Asset balance type that can convert to/from native balance. type AssetBalance: Parameter @@ -163,8 +161,7 @@ pub mod pallet { + Saturating + Copy + sp_runtime::traits::One - + Into - + ToFelts; + + Into; /// Account ID used as the "from" account when creating transfer proofs for minted tokens #[pallet::constant] @@ -188,7 +185,7 @@ pub mod pallet { /// Weight information for pallet operations. type WeightInfo: WeightInfo; - /// Override system AccountId to make it felts encodable + /// Override system AccountId for wormhole operations type WormholeAccountId: Parameter + Member + MaybeSerializeDeserialize @@ -196,7 +193,6 @@ pub mod pallet { + MaybeDisplay + Ord + MaxEncodedLen - + ToFelts + Into<::AccountId> + From<::AccountId>; diff --git a/pallets/wormhole/src/mock.rs b/pallets/wormhole/src/mock.rs index dab7c7f1..877a3dea 100644 --- a/pallets/wormhole/src/mock.rs +++ b/pallets/wormhole/src/mock.rs @@ -4,7 +4,6 @@ use frame_support::{ traits::{ConstU128, ConstU32, Everything}, }; use frame_system::mocking::MockUncheckedExtrinsic; -use qp_poseidon::PoseidonHasher; use sp_core::H256; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, @@ -28,7 +27,7 @@ pub type Balance = u128; pub const UNIT: Balance = 1_000_000_000_000; pub type AccountId = sp_core::crypto::AccountId32; pub type Block = sp_runtime::generic::Block< - qp_header::Header, + qp_header::Header, MockUncheckedExtrinsic, >; @@ -143,7 +142,7 @@ impl pallet_wormhole::Config for Test { } // Helper function to build a genesis configuration -pub fn new_test_ext() -> sp_state_machine::TestExternalities { +pub fn new_test_ext() -> sp_state_machine::TestExternalities { let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); t.into() } @@ -157,7 +156,7 @@ pub fn new_test_ext() -> sp_state_machine::TestExternalities { /// `Wormhole::on_initialize(1)` to process the endowments. pub fn new_test_ext_with_endowments( endowments: Vec<(AccountId, Balance)>, -) -> sp_state_machine::TestExternalities { +) -> sp_state_machine::TestExternalities { use sp_runtime::BuildStorage; let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); diff --git a/primitives/dilithium-crypto/Cargo.toml b/primitives/dilithium-crypto/Cargo.toml index 55138fa5..ce1f7c93 100644 --- a/primitives/dilithium-crypto/Cargo.toml +++ b/primitives/dilithium-crypto/Cargo.toml @@ -21,7 +21,6 @@ version = "0.3.1" [dependencies] codec = { workspace = true, default-features = false } log = { workspace = true } -qp-poseidon = { workspace = true } qp-poseidon-core = { workspace = true } qp-rusty-crystals-dilithium = { workspace = true, default-features = false } qp-rusty-crystals-hdwallet = { workspace = true, optional = true } @@ -40,13 +39,12 @@ full_crypto = [ "sp-core/full_crypto", ] serde = [ - "qp-poseidon/serde", "sp-core/serde", ] std = [ "codec/std", "full_crypto", - "qp-poseidon/std", + "qp-poseidon-core/std", "qp-rusty-crystals-hdwallet", "scale-info/std", "sp-core/std", diff --git a/primitives/header/Cargo.toml b/primitives/header/Cargo.toml index a164947d..db660ef6 100644 --- a/primitives/header/Cargo.toml +++ b/primitives/header/Cargo.toml @@ -14,7 +14,6 @@ codec = { features = ["derive"], workspace = true } log.workspace = true p3-field = { workspace = true } p3-goldilocks = { workspace = true } -qp-poseidon = { workspace = true, features = ["serde"] } qp-poseidon-core = { workspace = true } scale-info = { features = ["derive", "serde"], workspace = true } serde = { workspace = true, features = ["derive"], optional = true } @@ -33,7 +32,7 @@ serde_json = { workspace = true, default-features = false, features = [ default = ["serde", "std"] std = [ "codec/std", - "qp-poseidon/std", + "qp-poseidon-core/std", "scale-info/std", "sp-core/std", "sp-runtime/std", diff --git a/primitives/header/src/lib.rs b/primitives/header/src/lib.rs index ae994a97..a0881171 100644 --- a/primitives/header/src/lib.rs +++ b/primitives/header/src/lib.rs @@ -1,9 +1,8 @@ //! Fork of sp-runtime's generic implementation of a block header. //! //! Key differences from the standard Substrate header: -//! - **Block hash**: Computed with Poseidon (via the `Hash` type parameter) for ZK circuit -//! compatibility -//! - **State trie**: Uses Blake2 (hardcoded as `Hashing` type) for efficient native execution +//! - **Block hash**: Computed with Poseidon for ZK circuit compatibility +//! - **State trie**: Uses Blake2 (via `Hashing` type parameter) for efficient native execution //! //! This means `HashingFor` returns `BlakeTwo256`, which is used for: //! - State trie merkle root computation @@ -20,7 +19,7 @@ use qp_poseidon_core::{ serialization::{bytes_to_digest, bytes_to_felts}, }; use scale_info::TypeInfo; -use sp_core::U256; +use sp_core::{H256, U256}; use sp_runtime::{ generic::Digest, traits::{AtLeast32BitUnsigned, BlockNumber, Hash as HashT, MaybeDisplay, Member}, @@ -48,10 +47,10 @@ pub trait ZkTreeRootProvider { fn zk_tree_root(&self) -> &Self::Hash; } -/// Custom block header with separate hashers for block hash and state trie. +/// Custom block header with Poseidon block hash and configurable state trie hasher. /// -/// - `Hash`: Used for block hash computation (Poseidon for ZK compatibility) -/// - `StateHash`: Used for state trie / extrinsics root via `Header::Hashing` trait +/// - Block hash is computed using Poseidon for ZK circuit compatibility +/// - `Hashing` type parameter is used for state trie / extrinsics root (typically BlakeTwo256) /// /// ## Field Ordering /// @@ -59,35 +58,31 @@ pub trait ZkTreeRootProvider { /// a fixed offset in the header preimage. This prevents miners from manipulating /// the digest to shift the ZK root's position in the felt encoding. #[derive(Encode, Decode, PartialEq, Eq, Clone, RuntimeDebug, TypeInfo, DecodeWithMemTracking)] -#[scale_info(skip_type_params(Hash, StateHash))] +#[scale_info(skip_type_params(Hashing))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] -#[cfg_attr( - feature = "serde", - serde(bound = "Hash::Output: Serialize + serde::de::DeserializeOwned") -)] -pub struct Header +pub struct Header where Number: Copy + Into + TryFrom, { - pub parent_hash: Hash::Output, + pub parent_hash: H256, #[cfg_attr( feature = "serde", serde(serialize_with = "serialize_number", deserialize_with = "deserialize_number") )] pub number: Number, - pub state_root: Hash::Output, - pub extrinsics_root: Hash::Output, + pub state_root: H256, + pub extrinsics_root: H256, /// Root of the ZK Merkle tree (4-ary Poseidon tree). /// /// This is placed before `digest` to ensure a fixed offset in the header /// preimage for ZK circuit verification. - pub zk_tree_root: Hash::Output, + pub zk_tree_root: H256, pub digest: Digest, #[codec(skip)] #[cfg_attr(feature = "serde", serde(skip))] - _marker: core::marker::PhantomData, + _marker: core::marker::PhantomData, } #[cfg(feature = "serde")] @@ -111,16 +106,15 @@ where TryFrom::try_from(u256).map_err(|_| serde::de::Error::custom("Try from failed")) } -impl sp_runtime::traits::Header for Header +impl sp_runtime::traits::Header for Header where Number: BlockNumber, - Hash: HashT, - StateHash: HashT, + Hashing: HashT, { type Number = Number; - type Hash = sp_core::H256; + type Hash = H256; /// State trie hasher - configurable, defaults to BlakeTwo256. - type Hashing = StateHash; + type Hashing = Hashing; fn new( number: Self::Number, @@ -135,7 +129,7 @@ where state_root, parent_hash, // Initialize with zero; pallet-zk-tree will set the actual root - zk_tree_root: sp_core::H256::zero(), + zk_tree_root: H256::zero(), digest, _marker: core::marker::PhantomData, } @@ -185,7 +179,7 @@ where } } -impl Header +impl Header where Number: Member + core::hash::Hash @@ -195,13 +189,11 @@ where + Codec + Into + TryFrom, - Hash: HashT, - Hash::Output: From<[u8; 32]>, - StateHash: HashT, + Hashing: HashT, { /// Convenience helper for computing the hash of the header without having /// to import the trait. - pub fn hash(&self) -> Hash::Output { + pub fn hash(&self) -> H256 { /// Fixed size for digest encoding - must match circuit expectation const DIGEST_LOGS_SIZE: usize = 110; @@ -245,7 +237,7 @@ where felts.extend(bytes_to_felts(&digest_padded)); let poseidon_hash: [u8; 32] = hash_to_bytes(&felts); - poseidon_hash.into() + H256::from(poseidon_hash) } /// Create a new header with all fields including zk_tree_root. @@ -254,10 +246,10 @@ where /// Use this instead of `Header::new` + `set_zk_tree_root`. pub fn new_with_zk_root( number: Number, - extrinsics_root: Hash::Output, - state_root: Hash::Output, - parent_hash: Hash::Output, - zk_tree_root: Hash::Output, + extrinsics_root: H256, + state_root: H256, + parent_hash: H256, + zk_tree_root: H256, digest: Digest, ) -> Self { Self { @@ -272,25 +264,24 @@ where } /// Get the ZK tree root. - pub fn zk_tree_root(&self) -> &Hash::Output { + pub fn zk_tree_root(&self) -> &H256 { &self.zk_tree_root } /// Set the ZK tree root. /// /// Called by pallet-zk-tree during block finalization. - pub fn set_zk_tree_root(&mut self, root: Hash::Output) { + pub fn set_zk_tree_root(&mut self, root: H256) { self.zk_tree_root = root; } } -impl ZkTreeRootProvider for Header +impl ZkTreeRootProvider for Header where Number: Copy + Into + TryFrom, - Hash: HashT, - StateHash: HashT, + Hashing: HashT, { - type Hash = Hash::Output; + type Hash = H256; fn set_zk_tree_root(&mut self, root: Self::Hash) { self.zk_tree_root = root; @@ -304,7 +295,6 @@ where #[cfg(test)] mod tests { use super::*; - use qp_poseidon::PoseidonHasher; use sp_core::H256; use sp_runtime::{traits::BlakeTwo256, DigestItem}; @@ -340,7 +330,7 @@ mod tests { #[test] fn ensure_format_is_unchanged() { - let header = Header:: { + let header = Header:: { parent_hash: BlakeTwo256::hash(b"1"), number: 2, state_root: BlakeTwo256::hash(b"3"), @@ -352,10 +342,10 @@ mod tests { let header_encoded = header.encode(); let header_decoded = - Header::::decode(&mut &header_encoded[..]).unwrap(); + Header::::decode(&mut &header_encoded[..]).unwrap(); assert_eq!(header_decoded, header); - let header = Header:: { + let header = Header:: { parent_hash: BlakeTwo256::hash(b"1000"), number: 2000, state_root: BlakeTwo256::hash(b"3000"), @@ -367,48 +357,12 @@ mod tests { let header_encoded = header.encode(); let header_decoded = - Header::::decode(&mut &header_encoded[..]).unwrap(); + Header::::decode(&mut &header_encoded[..]).unwrap(); assert_eq!(header_decoded, header); } - fn hash_header(x: &[u8]) -> [u8; 32] { - let mut y = x; - if let Ok(header) = Header::::decode(&mut y) { - if y.is_empty() { - const DIGEST_LOGS_SIZE: usize = 110; - let max_encoded_felts = 4 * 4 + 1 + 28; - let mut felts = Vec::with_capacity(max_encoded_felts); - - felts.extend(bytes_to_digest::( - header.parent_hash.as_bytes().try_into().unwrap(), - )); - felts.push(Goldilocks::from_int(header.number as u64)); - felts.extend(bytes_to_digest::( - header.state_root.as_bytes().try_into().unwrap(), - )); - felts.extend(bytes_to_digest::( - header.extrinsics_root.as_bytes().try_into().unwrap(), - )); - felts.extend(bytes_to_digest::( - header.zk_tree_root.as_bytes().try_into().unwrap(), - )); - - let digest_encoded = header.digest.encode(); - let mut digest_padded = [0u8; DIGEST_LOGS_SIZE]; - let copy_len = digest_encoded.len().min(DIGEST_LOGS_SIZE); - digest_padded[..copy_len].copy_from_slice(&digest_encoded[..copy_len]); - felts.extend(bytes_to_felts(&digest_padded)); - - return hash_to_bytes(&felts); - } - } - PoseidonHasher::hash_for_circuit(x) - } - #[test] - fn poseidon_header_hash_matches_old_path() { - use codec::Encode; - + fn poseidon_header_hash_is_stable() { // Example header from a real block on devnet let parent_hash = "839b2d2ac0bf4aa71b18ad1ba5e2880b4ef06452cefacd255cfd76f6ad2c7966"; let number = 4; @@ -435,7 +389,7 @@ mod tests { ), ], }; - let header = Header:: { + let header = Header:: { parent_hash: H256::from_slice( hex::decode(parent_hash).expect("valid hex parent hash").as_slice(), ), @@ -451,12 +405,14 @@ mod tests { _marker: core::marker::PhantomData, }; - let encoded = header.encode(); - - let old = hash_header(&encoded); // old path - let new: [u8; 32] = header.hash().into(); - println!("Old hash: 0x{}", hex::encode(old)); + // Known good hash for this header - ensures hash algorithm stability + let expected_hash = "b7dbfd398bcef7d1c91821eb105c52e87dab78e822779bfd0dc7ab132d659a55"; + let actual_hash: [u8; 32] = header.hash().into(); - assert_eq!(old, new); + assert_eq!( + hex::encode(actual_hash), + expected_hash, + "Header hash changed! This would break consensus." + ); } } diff --git a/primitives/wormhole/Cargo.toml b/primitives/wormhole/Cargo.toml index 5138a9c9..106e879e 100644 --- a/primitives/wormhole/Cargo.toml +++ b/primitives/wormhole/Cargo.toml @@ -11,7 +11,6 @@ version = "0.1.0" [dependencies] codec = { workspace = true, default-features = false } -qp-poseidon.workspace = true qp-poseidon-core.workspace = true sp-consensus-qpow.workspace = true sp-core = { workspace = true, optional = true } @@ -22,7 +21,6 @@ default = ["std"] std = [ "codec/std", "qp-poseidon-core/std", - "qp-poseidon/std", "sp-consensus-qpow/std", "sp-core", "sp-runtime/std", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index cebdeacb..84f0e136 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -51,7 +51,6 @@ primitive-types.workspace = true qp-dilithium-crypto.workspace = true qp-header = { workspace = true, features = ["serde"] } qp-high-security = { path = "../primitives/high-security", default-features = false } -qp-poseidon = { workspace = true, features = ["serde"] } qp-scheduler.workspace = true qp-wormhole.workspace = true qp-wormhole-verifier = { workspace = true, default-features = false } @@ -122,7 +121,6 @@ std = [ "qp-dilithium-crypto/std", "qp-header/std", "qp-high-security/std", - "qp-poseidon/std", "qp-scheduler/std", "scale-info/std", "serde_json/std", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index c87731e6..0dddcc0e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -32,31 +32,26 @@ pub mod genesis_config_presets; pub mod governance; pub mod transaction_extensions; -use qp_poseidon::PoseidonHasher; /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades /// to even the core data structures. pub mod opaque { use super::*; - use sp_runtime::{generic, traits::Hash as HashT}; + use sp_runtime::generic; pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; - // For whatever reason, changing this causes the block hash and - // the storage root to be computed with poseidon, but not the extrinsics root. - // Block header with separate hashers: - // - PoseidonHasher: for block hash (ZK circuit compatible) - // - BlakeTwo256: for state trie / extrinsics root (efficient native execution) - pub type Header = qp_header::Header; + // Block header with Poseidon block hash and BlakeTwo256 for state trie + pub type Header = qp_header::Header; // Opaque block type. pub type Block = generic::Block; // Opaque block identifier type. pub type BlockId = generic::BlockId; - // Opaque block hash type. - pub type Hash = ::Output; + // Opaque block hash type (H256). + pub type Hash = sp_core::H256; } impl_opaque_keys! { @@ -130,9 +125,8 @@ pub type BlockNumber = u32; pub type Address = MultiAddress; /// Block header type as expected by this runtime. -/// - PoseidonHasher: for block hash (ZK circuit compatible) -/// - BlakeTwo256: for state trie / extrinsics root (efficient native execution) -pub type Header = qp_header::Header; +/// Uses Poseidon for block hash and BlakeTwo256 for state trie / extrinsics root. +pub type Header = qp_header::Header; /// Block type as expected by this runtime. pub type Block = generic::Block; From cf3d94c71ebeff0ca1487874becd7a19823dbf15 Mon Sep 17 00:00:00 2001 From: illuzen Date: Thu, 28 May 2026 17:43:47 +0900 Subject: [PATCH 2/2] update qp-poseidon version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c8349615..52ed5d71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -151,7 +151,7 @@ sp-consensus-qpow = { path = "./primitives/consensus/qpow", default-features = f # Quantus network dependencies qp-plonky2 = { version = "1.4.1", default-features = false } -qp-poseidon-core = { path = "../qp-poseidon/core", default-features = false } +qp-poseidon-core = { version = "2.0.2", default-features = false } qp-rusty-crystals-dilithium = { version = "2.4.0", default-features = false } qp-rusty-crystals-hdwallet = { version = "2.3.1" } qp-wormhole-circuit = { version = "2.0.1", default-features = false }