From 09515765568c7f1aa7671f0c3a1b06af47053ec6 Mon Sep 17 00:00:00 2001 From: linning Date: Wed, 17 Apr 2024 16:07:21 +0800 Subject: [PATCH 1/8] Store MMR root in the consensus runtime state Signed-off-by: linning --- crates/pallet-subspace-mmr/src/lib.rs | 23 ++++++++++++++++++++++- crates/subspace-runtime/src/lib.rs | 5 +++++ test/subspace-test-runtime/src/lib.rs | 5 +++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/crates/pallet-subspace-mmr/src/lib.rs b/crates/pallet-subspace-mmr/src/lib.rs index bc1d8bf59c..428338dc7b 100644 --- a/crates/pallet-subspace-mmr/src/lib.rs +++ b/crates/pallet-subspace-mmr/src/lib.rs @@ -20,6 +20,7 @@ use frame_system::pallet_prelude::BlockNumberFor; pub use pallet::*; +use sp_core::Get; use sp_mmr_primitives::{LeafDataProvider, OnNewRoot}; use sp_runtime::traits::{CheckedSub, One}; use sp_runtime::DigestItem; @@ -28,7 +29,9 @@ use sp_subspace_mmr::{LeafDataV0, MmrDigest, MmrLeaf}; #[frame_support::pallet] mod pallet { + use frame_support::pallet_prelude::*; use frame_support::Parameter; + use frame_system::pallet_prelude::BlockNumberFor; use sp_core::H256; #[pallet::pallet] @@ -36,14 +39,32 @@ mod pallet { #[pallet::config] pub trait Config: frame_system::Config + From> { - type MmrRootHash: Parameter + Copy; + type MmrRootHash: Parameter + Copy + MaxEncodedLen; + + /// The number of mmr root hash store in runtime, it will be used to verify mmmr + /// proof stateless, the number of roots stored here represent the number of blocks + /// for which the mmr proof is valid since it is generated, after that the mmr proof + /// will be expired and the prover need to re-generate the proof. + type MmrRootHashCount: Get; } + + /// Map of block numbers to mmr root hashes. + #[pallet::storage] + #[pallet::getter(fn mmr_root_hash)] + pub type MmrRootHashes = + StorageMap<_, Twox64Concat, BlockNumberFor, T::MmrRootHash, OptionQuery>; } impl OnNewRoot for Pallet { fn on_new_root(root: &T::MmrRootHash) { let digest = DigestItem::new_mmr_root(*root); >::deposit_log(digest); + + let block_number = frame_system::Pallet::::block_number(); + >::insert(block_number, *root); + if let Some(to_prune) = block_number.checked_sub(&T::MmrRootHashCount::get().into()) { + >::remove(to_prune); + } } } diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 9432502034..18b838237a 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -763,8 +763,13 @@ impl pallet_mmr::Config for Runtime { type WeightInfo = (); } +parameter_types! { + pub const MmrRootHashCount: u32 = 1024; +} + impl pallet_subspace_mmr::Config for Runtime { type MmrRootHash = mmr::Hash; + type MmrRootHashCount = MmrRootHashCount; } construct_runtime!( diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index 32cc38eb05..2bd58b505f 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -788,8 +788,13 @@ impl pallet_mmr::Config for Runtime { type WeightInfo = (); } +parameter_types! { + pub const MmrRootHashCount: u32 = 15; +} + impl pallet_subspace_mmr::Config for Runtime { type MmrRootHash = mmr::Hash; + type MmrRootHashCount = MmrRootHashCount; } construct_runtime!( From 2cda1c75ffd3f3f848da0b75271c21133f1f56ef Mon Sep 17 00:00:00 2001 From: linning Date: Wed, 17 Apr 2024 16:16:41 +0800 Subject: [PATCH 2/8] Generate MMR proof that can be verified stateless and add test Signed-off-by: linning --- crates/sp-subspace-mmr/src/lib.rs | 31 +++++++ crates/subspace-runtime/Cargo.toml | 1 + domains/client/domain-operator/Cargo.toml | 4 +- .../src/domain_block_processor.rs | 54 ++++++++++++ domains/client/domain-operator/src/tests.rs | 86 ++++++++++++++++++- test/subspace-test-primitives/Cargo.toml | 9 ++ test/subspace-test-primitives/src/lib.rs | 6 ++ test/subspace-test-runtime/Cargo.toml | 1 + test/subspace-test-runtime/src/lib.rs | 24 ++++++ 9 files changed, 214 insertions(+), 2 deletions(-) diff --git a/crates/sp-subspace-mmr/src/lib.rs b/crates/sp-subspace-mmr/src/lib.rs index 86a7652512..98fdd98fd7 100644 --- a/crates/sp-subspace-mmr/src/lib.rs +++ b/crates/sp-subspace-mmr/src/lib.rs @@ -28,6 +28,7 @@ pub use runtime_interface::{domain_mmr_runtime_interface, subspace_mmr_runtime_i use codec::{Codec, Decode, Encode}; use scale_info::TypeInfo; +use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof as MmrProof}; use sp_runtime::generic::OpaqueDigestItemId; use sp_runtime::DigestItem; @@ -81,3 +82,33 @@ impl MmrDigest for DigestItem { } } } + +/// Consensus chain MMR leaf and its Proof at specific block. +/// +/// The verifier is not required to contains any the MMR offchain data but this proof +/// will be expired after `N` blocks where `N` is the number of MMR root stored in the +// consensus chain runtime. +#[derive(Debug, Encode, Decode, Eq, PartialEq, TypeInfo)] +pub struct ConsensusChainMmrLeafProof { + /// Consensus block info from which this proof was generated. + pub consensus_block_number: CBlockNumber, + pub consensus_block_hash: CBlockHash, + /// Encoded MMR leaf + pub opaque_mmr_leaf: EncodableOpaqueLeaf, + /// MMR proof for the leaf above. + pub proof: MmrProof, +} + +// TODO: update upstream `EncodableOpaqueLeaf` to derive clone. +impl Clone + for ConsensusChainMmrLeafProof +{ + fn clone(&self) -> Self { + Self { + consensus_block_number: self.consensus_block_number.clone(), + consensus_block_hash: self.consensus_block_hash.clone(), + opaque_mmr_leaf: EncodableOpaqueLeaf(self.opaque_mmr_leaf.0.clone()), + proof: self.proof.clone(), + } + } +} diff --git a/crates/subspace-runtime/Cargo.toml b/crates/subspace-runtime/Cargo.toml index 157964fd98..271ef06c81 100644 --- a/crates/subspace-runtime/Cargo.toml +++ b/crates/subspace-runtime/Cargo.toml @@ -123,6 +123,7 @@ std = [ "sp-std/std", "sp-subspace-mmr/std", "sp-transaction-pool/std", + "sp-subspace-mmr/std", "sp-version/std", "subspace-core-primitives/std", "subspace-runtime-primitives/std", diff --git a/domains/client/domain-operator/Cargo.toml b/domains/client/domain-operator/Cargo.toml index 0767bab3d9..69e18a3121 100644 --- a/domains/client/domain-operator/Cargo.toml +++ b/domains/client/domain-operator/Cargo.toml @@ -29,8 +29,10 @@ sp-domain-digests = { version = "0.1.0", path = "../../primitives/digests" } sp-inherents = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-keystore = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-messenger = { version = "0.1.0", path = "../../primitives/messenger" } +sp-mmr-primitives = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-state-machine = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } +sp-subspace-mmr = { version = "0.1.0", default-features = false, path = "../../../crates/sp-subspace-mmr" } sp-transaction-pool = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-trie = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-weights = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } @@ -54,9 +56,9 @@ pallet-transporter = { version = "0.1.0", path = "../../../domains/pallets/trans sc-cli = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f", default-features = false } sc-service = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f", default-features = false } sc-transaction-pool = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } -sp-mmr-primitives = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-state-machine = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } subspace-core-primitives = { version = "0.1.0", default-features = false, path = "../../../crates/subspace-core-primitives" } subspace-test-runtime = { version = "0.1.0", path = "../../../test/subspace-test-runtime" } subspace-test-service = { version = "0.1.0", path = "../../../test/subspace-test-service" } +subspace-test-primitives = { version = "0.1.0", path = "../../../test/subspace-test-primitives" } tempfile = "3.10.1" diff --git a/domains/client/domain-operator/src/domain_block_processor.rs b/domains/client/domain-operator/src/domain_block_processor.rs index 7135d94be2..6b558e1d3e 100644 --- a/domains/client/domain-operator/src/domain_block_processor.rs +++ b/domains/client/domain-operator/src/domain_block_processor.rs @@ -23,8 +23,10 @@ use sp_domains::{BundleValidity, DomainId, DomainsApi, ExecutionReceipt, HeaderH use sp_domains_fraud_proof::fraud_proof::{FraudProof, ValidBundleProof}; use sp_domains_fraud_proof::FraudProofApi; use sp_messenger::MessengerApi; +use sp_mmr_primitives::MmrApi; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, One, Zero}; use sp_runtime::{Digest, Saturating}; +use sp_subspace_mmr::ConsensusChainMmrLeafProof; use std::cmp::Ordering; use std::collections::VecDeque; use std::str::FromStr; @@ -977,6 +979,58 @@ where } } +/// Generate MMR proof for the block `to_prove` in the current best fork. The returned proof +/// can be later used to verify stateless (without query offchain MMR leaf) and extract the state +/// root at `to_prove`. +// TODO: remove `dead_code` after it is used in fraud proof generation +#[allow(dead_code)] +pub(crate) fn generate_mmr_proof( + consensus_client: &Arc, + to_prove: NumberFor, +) -> sp_blockchain::Result, CBlock::Hash, H256>> +where + CBlock: BlockT, + CClient: HeaderBackend + ProvideRuntimeApi + 'static, + CClient::Api: MmrApi>, +{ + let api = consensus_client.runtime_api(); + let prove_at_hash = consensus_client.info().best_hash; + let prove_at_number = consensus_client.info().best_number; + + if to_prove >= prove_at_number { + return Err(sp_blockchain::Error::Application(Box::from(format!( + "Can't generate MMR proof for block {to_prove:?} >= best block {prove_at_number:?}" + )))); + } + + let (mut leaves, proof) = api + // NOTE: the mmr leaf data is added in the next block so to generate the MMR proof of + // block `to_prove` we need to use `to_prove + 1` here. + .generate_proof( + prove_at_hash, + vec![to_prove + One::one()], + Some(prove_at_number), + )? + .map_err(|err| { + sp_blockchain::Error::Application(Box::from(format!( + "Failed to generate MMR proof: {err}" + ))) + })?; + debug_assert!(leaves.len() == 1, "should always be of length 1"); + let leaf = leaves + .pop() + .ok_or(sp_blockchain::Error::Application(Box::from( + "Unexpected missing mmr leaf".to_string(), + )))?; + + Ok(ConsensusChainMmrLeafProof { + consensus_block_number: prove_at_number, + consensus_block_hash: prove_at_hash, + opaque_mmr_leaf: leaf, + proof, + }) +} + #[cfg(test)] mod tests { use super::*; diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index b9a536d9b7..dbe11810e4 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -1,4 +1,6 @@ -use crate::domain_block_processor::{DomainBlockProcessor, PendingConsensusBlocks}; +use crate::domain_block_processor::{ + generate_mmr_proof, DomainBlockProcessor, PendingConsensusBlocks, +}; use crate::domain_bundle_producer::DomainBundleProducer; use crate::domain_bundle_proposer::DomainBundleProposer; use crate::fraud_proof::{FraudProofGenerator, TraceDiffType}; @@ -4571,3 +4573,85 @@ async fn test_handle_duplicated_tx_with_diff_nonce_in_previous_bundle() { assert_eq!(alice.free_balance(Bob.to_account_id()), bob_pre_balance + 3); assert_eq!(alice.account_nonce(), nonce + 3); } + +#[tokio::test(flavor = "multi_thread")] +async fn test_verify_mmr_proof_stateless() { + use subspace_test_primitives::OnchainStateApi as _; + let directory = TempDir::new().expect("Must be able to create temporary directory"); + + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_colors(false); + let _ = builder.init(); + + let tokio_handle = tokio::runtime::Handle::current(); + + // Start Ferdie with Alice Key since that is the sudo key + let mut ferdie = MockConsensusNode::run_with_finalization_depth( + tokio_handle.clone(), + Sr25519Alice, + BasePath::new(directory.path().join("ferdie")), + // finalization depth + Some(10), + ); + + // Run Alice (an evm domain) + let alice = domain_test_service::DomainNodeBuilder::new( + tokio_handle.clone(), + Alice, + BasePath::new(directory.path().join("alice")), + ) + .build_evm_node(Role::Authority, GENESIS_DOMAIN_ID, &mut ferdie) + .await; + + produce_blocks!(ferdie, alice, 3).await.unwrap(); + + let to_prove = ferdie.client.info().best_number; + let expected_state_root = *ferdie + .client + .header(ferdie.client.info().best_hash) + .unwrap() + .unwrap() + .state_root(); + + // Can't generate MMR proof for the current best block + assert!(generate_mmr_proof(&ferdie.client, to_prove).is_err()); + + produce_blocks!(ferdie, alice, 1).await.unwrap(); + + let proof = generate_mmr_proof(&ferdie.client, to_prove).unwrap(); + for i in 0..20 { + let res = ferdie + .client + .runtime_api() + .verify_proof_and_extract_consensus_state_root( + ferdie.client.info().best_hash, + proof.clone(), + ) + .unwrap(); + + produce_blocks!(ferdie, alice, 1).await.unwrap(); + + // The MMR is proof is valid for the first `MmrRootHashCount` (i.e. 15) blocks but then expired + if i < 15 { + assert_eq!(res, Some(expected_state_root)); + } else { + assert_eq!(res, None); + } + } + + // Re-generate MMR proof for the same block, it will be valid for the next `MmrRootHashCount` blocks + let proof = generate_mmr_proof(&ferdie.client, to_prove).unwrap(); + for _ in 0..15 { + let res = ferdie + .client + .runtime_api() + .verify_proof_and_extract_consensus_state_root( + ferdie.client.info().best_hash, + proof.clone(), + ) + .unwrap(); + + assert_eq!(res, Some(expected_state_root)); + produce_blocks!(ferdie, alice, 1).await.unwrap(); + } +} diff --git a/test/subspace-test-primitives/Cargo.toml b/test/subspace-test-primitives/Cargo.toml index 91dc6d7d2b..9a89288381 100644 --- a/test/subspace-test-primitives/Cargo.toml +++ b/test/subspace-test-primitives/Cargo.toml @@ -14,13 +14,22 @@ include = [ [dependencies] codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false, features = ["derive"] } sp-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f", default-features = false } +sp-domains = { version = "0.1.0", path = "../../crates/sp-domains", default-features = false } +sp-core = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-messenger = { version = "0.1.0", default-features = false, path = "../../domains/primitives/messenger" } +sp-runtime = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } +sp-subspace-mmr = { version = "0.1.0", default-features = false, path = "../../crates/sp-subspace-mmr" } subspace-runtime-primitives = { version = "0.1.0", path = "../../crates/subspace-runtime-primitives", default-features = false } [features] default = ["std"] std = [ + "codec/std", "sp-api/std", + "sp-domains/std", + "sp-core/std", "sp-messenger/std", + "sp-runtime/std", + "sp-subspace-mmr/std", "subspace-runtime-primitives/std", ] diff --git a/test/subspace-test-primitives/src/lib.rs b/test/subspace-test-primitives/src/lib.rs index ce2e66d5b8..2b35e74308 100644 --- a/test/subspace-test-primitives/src/lib.rs +++ b/test/subspace-test-primitives/src/lib.rs @@ -2,7 +2,10 @@ //! Test primitive crates that expose necessary extensions that are used in tests. use codec::{Decode, Encode}; +use sp_core::H256; use sp_messenger::messages::{ChainId, ChannelId}; +use sp_runtime::traits::NumberFor; +use sp_subspace_mmr::ConsensusChainMmrLeafProof; sp_api::decl_runtime_apis! { /// Api for querying onchain state in the test @@ -16,5 +19,8 @@ sp_api::decl_runtime_apis! { /// Returns the last open channel for a given domain. fn get_open_channel_for_chain(dst_chain_id: ChainId) -> Option; + + /// Verify the mmr proof stateless and extract the state root. + fn verify_proof_and_extract_consensus_state_root(proof: ConsensusChainMmrLeafProof, Block::Hash, H256>) -> Option; } } diff --git a/test/subspace-test-runtime/Cargo.toml b/test/subspace-test-runtime/Cargo.toml index 25265508ee..5c767bf9c6 100644 --- a/test/subspace-test-runtime/Cargo.toml +++ b/test/subspace-test-runtime/Cargo.toml @@ -114,6 +114,7 @@ std = [ "sp-std/std", "sp-subspace-mmr/std", "sp-transaction-pool/std", + "sp-subspace-mmr/std", "sp-version/std", "subspace-core-primitives/std", "subspace-runtime-primitives/std", diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index 2bd58b505f..ab224e7303 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -1536,6 +1536,30 @@ impl_runtime_apis! { fn get_open_channel_for_chain(dst_chain_id: ChainId) -> Option { Messenger::get_open_channel_for_chain(dst_chain_id).map(|(c, _)| c) } + + fn verify_proof_and_extract_consensus_state_root(mmr_leaf_proof: sp_subspace_mmr::ConsensusChainMmrLeafProof, ::Hash, H256>) -> Option { + let sp_subspace_mmr::ConsensusChainMmrLeafProof { + consensus_block_number, + opaque_mmr_leaf, + proof, + .. + } = mmr_leaf_proof; + + let mmr_root = SubspaceMmr::mmr_root_hash(consensus_block_number)?; + + pallet_mmr::verify_leaves_proof::( + mmr_root, + vec![mmr::DataOrHash::Data( + EncodableOpaqueLeaf(opaque_mmr_leaf.0.clone()).into_opaque_leaf(), + )], + proof, + ) + .ok()?; + + let leaf: mmr::Leaf = opaque_mmr_leaf.into_opaque_leaf().try_decode()?; + + Some(leaf.state_root()) + } } impl sp_genesis_builder::GenesisBuilder for Runtime { From 5ded1048b8e3761337a167e3ca4a7a20b0c60c22 Mon Sep 17 00:00:00 2001 From: linning Date: Wed, 17 Apr 2024 16:33:11 +0800 Subject: [PATCH 3/8] Update ConsensusChainMmrLeafProof defination to include consensus block number at which the proof is generated This will be used later in the consensus runtime to verify XDM stateless Signed-off-by: linning --- crates/sp-subspace-mmr/src/lib.rs | 16 +++++ crates/subspace-runtime/src/lib.rs | 21 ++++-- domains/client/domain-operator/src/tests.rs | 6 +- domains/client/relayer/Cargo.toml | 1 + domains/client/relayer/src/lib.rs | 66 ++++++++++--------- domains/client/relayer/src/worker.rs | 4 +- domains/pallets/messenger/Cargo.toml | 2 + domains/pallets/messenger/src/benchmarking.rs | 13 ++-- domains/pallets/messenger/src/lib.rs | 33 +++++----- domains/pallets/messenger/src/tests.rs | 8 ++- domains/primitives/messenger/Cargo.toml | 4 +- domains/primitives/messenger/src/lib.rs | 26 ++------ domains/primitives/messenger/src/messages.rs | 49 ++++---------- domains/runtime/auto-id/src/lib.rs | 25 ++++--- domains/runtime/evm/src/lib.rs | 25 ++++--- domains/service/src/domain.rs | 6 +- domains/test/runtime/evm/src/lib.rs | 25 ++++--- domains/test/service/src/domain.rs | 4 +- test/subspace-test-runtime/src/lib.rs | 20 ++++-- 19 files changed, 184 insertions(+), 170 deletions(-) diff --git a/crates/sp-subspace-mmr/src/lib.rs b/crates/sp-subspace-mmr/src/lib.rs index 98fdd98fd7..1e91e4f8c6 100644 --- a/crates/sp-subspace-mmr/src/lib.rs +++ b/crates/sp-subspace-mmr/src/lib.rs @@ -112,3 +112,19 @@ impl Clone } } } + +/// Trait to verify MMR proofs +pub trait MmrProofVerifier { + /// Returns consensus state root if the given MMR proof is valid + fn verify_proof_and_extract_consensus_state_root( + mmr_leaf_proof: ConsensusChainMmrLeafProof, + ) -> Option; +} + +impl MmrProofVerifier for () { + fn verify_proof_and_extract_consensus_state_root( + _mmr_leaf_proof: ConsensusChainMmrLeafProof, + ) -> Option { + None + } +} diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 18b838237a..6b4c4ce68a 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -72,7 +72,7 @@ use sp_messenger::messages::{ BlockMessagesWithStorageKey, ChainId, CrossDomainMessage, MessageId, MessageKey, }; use sp_messenger_host_functions::{get_storage_key, StorageKeyRequest}; -use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof}; +use sp_mmr_primitives::EncodableOpaqueLeaf; use sp_runtime::traits::{ AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Keccak256, NumberFor, }; @@ -85,6 +85,7 @@ use sp_std::collections::btree_map::BTreeMap; use sp_std::marker::PhantomData; use sp_std::prelude::*; use sp_subspace_mmr::subspace_mmr_runtime_interface::consensus_block_hash; +use sp_subspace_mmr::ConsensusChainMmrLeafProof; use sp_version::RuntimeVersion; use static_assertions::const_assert; use subspace_core_primitives::objects::BlockObjectMapping; @@ -504,11 +505,17 @@ impl sp_messenger::OnXDMRewards for OnXDMRewards { pub struct MmrProofVerifier; -impl sp_messenger::MmrProofVerifier for MmrProofVerifier { +// TODO: verify the proof stateless +impl sp_subspace_mmr::MmrProofVerifier, Hash> for MmrProofVerifier { fn verify_proof_and_extract_consensus_state_root( - opaque_leaf: EncodableOpaqueLeaf, - proof: Proof, + mmr_leaf_proof: ConsensusChainMmrLeafProof, Hash, mmr::Hash>, ) -> Option { + let ConsensusChainMmrLeafProof { + opaque_mmr_leaf: opaque_leaf, + proof, + .. + } = mmr_leaf_proof; + let leaf: mmr::Leaf = opaque_leaf.into_opaque_leaf().try_decode()?; let state_root = leaf.state_root(); Mmr::verify_leaves(vec![leaf], proof).ok()?; @@ -1342,16 +1349,16 @@ impl_runtime_apis! { } } - impl sp_messenger::RelayerApi::Hash> for Runtime { + impl sp_messenger::RelayerApi::Hash> for Runtime { fn block_messages() -> BlockMessagesWithStorageKey { Messenger::get_block_messages() } - fn outbox_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn outbox_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::outbox_message_unsigned(msg) } - fn inbox_response_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn inbox_response_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::inbox_response_message_unsigned(msg) } diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index dbe11810e4..e5fc20faae 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -39,9 +39,7 @@ use sp_domains_fraud_proof::fraud_proof::{ InvalidTransfersProof, }; use sp_domains_fraud_proof::InvalidTransactionCode; -use sp_messenger::messages::{ - ConsensusChainMmrLeafProof, CrossDomainMessage, FeeModel, InitiateChannelParams, Proof, -}; +use sp_messenger::messages::{CrossDomainMessage, FeeModel, InitiateChannelParams, Proof}; use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof as MmrProof}; use sp_runtime::generic::{BlockId, DigestItem}; use sp_runtime::traits::{ @@ -50,6 +48,7 @@ use sp_runtime::traits::{ use sp_runtime::transaction_validity::InvalidTransaction; use sp_runtime::OpaqueExtrinsic; use sp_state_machine::backend::AsTrieBackend; +use sp_subspace_mmr::ConsensusChainMmrLeafProof; use std::collections::BTreeMap; use std::sync::Arc; use subspace_core_primitives::PotOutput; @@ -1558,6 +1557,7 @@ async fn test_invalid_xdm_proof_creation_and_verification() { nonce: Default::default(), proof: Proof::Domain { consensus_chain_mmr_proof: ConsensusChainMmrLeafProof { + consensus_block_number: Default::default(), consensus_block_hash: Default::default(), opaque_mmr_leaf: EncodableOpaqueLeaf(vec![0, 1, 2]), proof: MmrProof { diff --git a/domains/client/relayer/Cargo.toml b/domains/client/relayer/Cargo.toml index 4ada898123..b2c9f8d632 100644 --- a/domains/client/relayer/Cargo.toml +++ b/domains/client/relayer/Cargo.toml @@ -27,4 +27,5 @@ sp-domains = { version = "0.1.0", path = "../../../crates/sp-domains" } sp-messenger = { version = "0.1.0", path = "../../primitives/messenger" } sp-mmr-primitives = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } +sp-subspace-mmr = { version = "0.1.0", default-features = false, path = "../../../crates/sp-subspace-mmr" } tracing = "0.1.40" diff --git a/domains/client/relayer/src/lib.rs b/domains/client/relayer/src/lib.rs index 6723ba49b2..340dc6c931 100644 --- a/domains/client/relayer/src/lib.rs +++ b/domains/client/relayer/src/lib.rs @@ -13,13 +13,13 @@ use sp_api::ProvideRuntimeApi; use sp_core::H256; use sp_domains::{DomainId, DomainsApi}; use sp_messenger::messages::{ - BlockMessageWithStorageKey, BlockMessagesWithStorageKey, ChainId, ConsensusChainMmrLeafProof, - CrossDomainMessage, Proof, + BlockMessageWithStorageKey, BlockMessagesWithStorageKey, ChainId, CrossDomainMessage, Proof, }; use sp_messenger::{MessengerApi, RelayerApi}; -use sp_mmr_primitives::{EncodableOpaqueLeaf, MmrApi}; +use sp_mmr_primitives::MmrApi; use sp_runtime::traits::{Block as BlockT, CheckedSub, Header as HeaderT, NumberFor, One, Zero}; use sp_runtime::ArithmeticError; +use sp_subspace_mmr::ConsensusChainMmrLeafProof; use std::marker::PhantomData; use std::sync::Arc; @@ -88,13 +88,13 @@ impl From for Error { } } -type ProofOf = Proof<::Hash, H256>; +type ProofOf = Proof, ::Hash, H256>; type UnProcessedBlocks = Vec<(NumberFor, ::Hash)>; fn construct_consensus_mmr_proof( consensus_chain_client: &Arc, block_number: NumberFor, -) -> Result<(EncodableOpaqueLeaf, sp_mmr_primitives::Proof), Error> +) -> Result, Block::Hash, H256>, Error> where Block: BlockT, Client: ProvideRuntimeApi + HeaderBackend, @@ -108,10 +108,17 @@ where .map_err(Error::MmrProof)?; debug_assert!(leaves.len() == 1, "should always be of length 1"); let leaf = leaves.pop().ok_or(Error::MmrLeafMissing)?; - Ok((leaf, proof)) + + Ok(ConsensusChainMmrLeafProof { + // FIXME: recorrect the value + consensus_block_number: Default::default(), + consensus_block_hash: Default::default(), + opaque_mmr_leaf: leaf, + proof, + }) } -fn construct_cross_chain_message_and_submit( +fn construct_cross_chain_message_and_submit( msgs: Vec, proof_constructor: ProofConstructor, submitter: Submitter, @@ -149,16 +156,17 @@ where } /// Sends an Outbox message from src_domain to dst_domain. -fn gossip_outbox_message( +fn gossip_outbox_message( client: &Arc, - msg: CrossDomainMessage, + msg: CrossDomainMessage, sink: &GossipMessageSink, ) -> Result<(), Error> where Block: BlockT, + CNumber: Codec, CHash: Codec, Client: ProvideRuntimeApi + HeaderBackend, - Client::Api: RelayerApi, CHash>, + Client::Api: RelayerApi, CNumber, CHash>, { let best_hash = client.info().best_hash; let dst_chain_id = msg.dst_chain_id; @@ -177,16 +185,17 @@ where /// Sends an Inbox message response from src_domain to dst_domain /// Inbox message was earlier sent by dst_domain to src_domain and /// this message is the response of the Inbox message execution. -fn gossip_inbox_message_response( +fn gossip_inbox_message_response( client: &Arc, msg: CrossDomainMessage, sink: &GossipMessageSink, ) -> Result<(), Error> where Block: BlockT, + CNumber: Codec, CHash: Codec, Client: ProvideRuntimeApi + HeaderBackend, - Client::Api: RelayerApi, CHash>, + Client::Api: RelayerApi, CNumber, CHash>, { let best_hash = client.info().best_hash; let dst_chain_id = msg.dst_chain_id; @@ -221,26 +230,23 @@ where .read_proof(block_hash_to_process, &mut [key].into_iter()) .map_err(|_| Error::ConstructStorageProof)?; - let (mmr_leaf, mmr_proof) = + let consensus_chain_mmr_proof = construct_consensus_mmr_proof(consensus_chain_client, finalized_block.0)?; Ok(Proof::Consensus { - consensus_chain_mmr_proof: ConsensusChainMmrLeafProof { - consensus_block_hash: finalized_block.1, - opaque_mmr_leaf: mmr_leaf, - proof: mmr_proof, - }, + consensus_chain_mmr_proof, message_proof: proof, }) } - fn filter_messages( + fn filter_messages( client: &Arc, mut msgs: BlockMessagesWithStorageKey, ) -> Result where CHash: Codec, - Client::Api: RelayerApi, CHash>, + CNumber: Codec, + Client::Api: RelayerApi, CNumber, CHash>, { let api = client.runtime_api(); let best_hash = client.info().best_hash; @@ -286,7 +292,7 @@ where ) -> Result<(), Error> where Client::Api: MmrApi> - + RelayerApi, Block::Hash>, + + RelayerApi, NumberFor, Block::Hash>, { // since the `finalized_block_number - 1` block MMR leaf is included in `finalized_block_number`, // we process that block instead while using the finalized block number to generate the MMR @@ -310,7 +316,7 @@ where return Ok(()); } - construct_cross_chain_message_and_submit::( + construct_cross_chain_message_and_submit::, Block::Hash, _, _>( filtered_messages.outbox, |key| { Self::construct_consensus_chain_xdm_proof_for_key_at( @@ -323,7 +329,7 @@ where |msg| gossip_outbox_message(consensus_chain_client, msg, gossip_message_sink), )?; - construct_cross_chain_message_and_submit::( + construct_cross_chain_message_and_submit::, Block::Hash, _, _>( filtered_messages.inbox_responses, |key| { Self::construct_consensus_chain_xdm_proof_for_key_at( @@ -353,7 +359,7 @@ where CClient::Api: DomainsApi + MessengerApi + MmrApi>, - Client::Api: RelayerApi, CBlock::Hash>, + Client::Api: RelayerApi, NumberFor, CBlock::Hash>, { // fetch messages to be relayed let domain_api = domain_client.runtime_api(); @@ -390,7 +396,7 @@ where &mut [storage_key.as_ref()].into_iter(), )?; - construct_cross_chain_message_and_submit::( + construct_cross_chain_message_and_submit::, CBlock::Hash, _, _>( filtered_messages.outbox, |key| { Self::construct_domain_chain_xdm_proof_for_key_at( @@ -405,7 +411,7 @@ where |msg| gossip_outbox_message(domain_client, msg, gossip_message_sink), )?; - construct_cross_chain_message_and_submit::( + construct_cross_chain_message_and_submit::, CBlock::Hash, _, _>( filtered_messages.inbox_responses, |key| { Self::construct_domain_chain_xdm_proof_for_key_at( @@ -439,7 +445,7 @@ where + MessengerApi + MmrApi>, { - let (mmr_leaf, mmr_proof) = construct_consensus_mmr_proof( + let consensus_chain_mmr_proof = construct_consensus_mmr_proof( consensus_chain_client, consensus_chain_finalized_block.0, )?; @@ -449,11 +455,7 @@ where .map_err(|_| Error::ConstructStorageProof)?; Ok(Proof::Domain { - consensus_chain_mmr_proof: ConsensusChainMmrLeafProof { - consensus_block_hash: consensus_chain_finalized_block.1, - opaque_mmr_leaf: mmr_leaf, - proof: mmr_proof, - }, + consensus_chain_mmr_proof, domain_proof, message_proof: proof, }) diff --git a/domains/client/relayer/src/worker.rs b/domains/client/relayer/src/worker.rs index 2be81ba829..b5d7d95368 100644 --- a/domains/client/relayer/src/worker.rs +++ b/domains/client/relayer/src/worker.rs @@ -25,7 +25,7 @@ pub async fn relay_consensus_chain_messages( + AuxStore + ProofProvider + ProvideRuntimeApi, - Client::Api: RelayerApi, Block::Hash> + Client::Api: RelayerApi, NumberFor, Block::Hash> + MmrApi>, SO: SyncOracle, { @@ -79,7 +79,7 @@ pub async fn relay_domain_messages( Block: BlockT, CBlock: BlockT, Client: HeaderBackend + AuxStore + ProofProvider + ProvideRuntimeApi, - Client::Api: RelayerApi, CBlock::Hash>, + Client::Api: RelayerApi, NumberFor, CBlock::Hash>, CClient: BlockchainEvents + HeaderBackend + ProvideRuntimeApi diff --git a/domains/pallets/messenger/Cargo.toml b/domains/pallets/messenger/Cargo.toml index f0adb92d1e..941e26ddb7 100644 --- a/domains/pallets/messenger/Cargo.toml +++ b/domains/pallets/messenger/Cargo.toml @@ -26,6 +26,7 @@ sp-messenger = { version = "0.1.0", default-features = false, path = "../../prim sp-mmr-primitives = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-runtime = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-trie = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } +sp-subspace-mmr = { version = "0.1.0", default-features = false, path = "../../../crates/sp-subspace-mmr" } [dev-dependencies] domain-runtime-primitives = { path = "../../primitives/runtime" } @@ -48,6 +49,7 @@ std = [ "sp-mmr-primitives/std", "sp-runtime/std", "sp-trie/std", + "sp-subspace-mmr/std", ] try-runtime = ["frame-support/try-runtime"] runtime-benchmarks = [ diff --git a/domains/pallets/messenger/src/benchmarking.rs b/domains/pallets/messenger/src/benchmarking.rs index 394e8b9242..989267e614 100644 --- a/domains/pallets/messenger/src/benchmarking.rs +++ b/domains/pallets/messenger/src/benchmarking.rs @@ -8,10 +8,11 @@ use frame_support::traits::Get; use frame_system::RawOrigin; use sp_messenger::endpoint::{Endpoint, EndpointRequest}; use sp_messenger::messages::{ - ConsensusChainMmrLeafProof, CrossDomainMessage, InitiateChannelParams, Message, - MessageWeightTag, Payload, Proof, RequestResponse, VersionedPayload, + CrossDomainMessage, InitiateChannelParams, Message, MessageWeightTag, Payload, Proof, + RequestResponse, VersionedPayload, }; use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof as MmrProof}; +use sp_subspace_mmr::ConsensusChainMmrLeafProof; use sp_trie::StorageProof; #[benchmarks] @@ -120,7 +121,7 @@ mod benchmarks { }; Inbox::::put(msg); - let xdm = CrossDomainMessage:: { + let xdm = CrossDomainMessage::, T::Hash, T::MmrHash> { src_chain_id: dst_chain_id, dst_chain_id: T::SelfChainId::get(), channel_id, @@ -187,7 +188,7 @@ mod benchmarks { }; OutboxResponses::::put(resp_msg); - let xdm = CrossDomainMessage:: { + let xdm = CrossDomainMessage::, T::Hash, T::MmrHash> { src_chain_id: dst_chain_id, dst_chain_id: T::SelfChainId::get(), channel_id, @@ -240,12 +241,14 @@ mod benchmarks { ); } -pub fn dummy_proof() -> Proof +pub fn dummy_proof() -> Proof where + CNumber: Default, CBlockHash: Default, { Proof::Consensus { consensus_chain_mmr_proof: ConsensusChainMmrLeafProof { + consensus_block_number: Default::default(), consensus_block_hash: Default::default(), opaque_mmr_leaf: EncodableOpaqueLeaf(vec![]), proof: MmrProof { diff --git a/domains/pallets/messenger/src/lib.rs b/domains/pallets/messenger/src/lib.rs index 7332956ea1..19faeb8f63 100644 --- a/domains/pallets/messenger/src/lib.rs +++ b/domains/pallets/messenger/src/lib.rs @@ -35,6 +35,7 @@ extern crate alloc; use codec::{Decode, Encode}; use frame_support::traits::fungible::{Inspect, InspectHold}; +use frame_system::pallet_prelude::BlockNumberFor; pub use pallet::*; use scale_info::TypeInfo; use sp_core::U256; @@ -158,11 +159,10 @@ mod pallet { MessageWeightTag, Payload, ProtocolMessageRequest, RequestResponse, VersionedPayload, }; use sp_messenger::{ - InherentError, InherentType, MmrProofVerifier, OnXDMRewards, StorageKeys, - INHERENT_IDENTIFIER, + InherentError, InherentType, OnXDMRewards, StorageKeys, INHERENT_IDENTIFIER, }; - use sp_mmr_primitives::EncodableOpaqueLeaf; use sp_runtime::ArithmeticError; + use sp_subspace_mmr::MmrProofVerifier; #[cfg(feature = "std")] use std::collections::BTreeSet; @@ -187,7 +187,11 @@ mod pallet { /// Hash type of MMR type MmrHash: Parameter + Member + Default + Clone; /// MMR proof verifier - type MmrProofVerifier: MmrProofVerifier>; + type MmrProofVerifier: MmrProofVerifier< + Self::MmrHash, + BlockNumberFor, + StateRootOf, + >; /// Storage key provider. type StorageKeys: StorageKeys; /// Domain owner provider. @@ -619,7 +623,7 @@ mod pallet { #[pallet::weight((T::WeightInfo::relay_message().saturating_add(Pallet::< T >::message_weight(& msg.weight_tag)), Pays::No))] pub fn relay_message( origin: OriginFor, - msg: CrossDomainMessage, + msg: CrossDomainMessage, T::Hash, T::MmrHash>, ) -> DispatchResult { ensure_none(origin)?; let inbox_msg = Inbox::::take().ok_or(Error::::MissingMessage)?; @@ -632,7 +636,7 @@ mod pallet { #[pallet::weight((T::WeightInfo::relay_message_response().saturating_add(Pallet::< T >::message_weight(& msg.weight_tag)), Pays::No))] pub fn relay_message_response( origin: OriginFor, - msg: CrossDomainMessage, + msg: CrossDomainMessage, T::Hash, T::MmrHash>, ) -> DispatchResult { ensure_none(origin)?; let outbox_resp_msg = OutboxResponses::::take().ok_or(Error::::MissingMessage)?; @@ -988,7 +992,7 @@ mod pallet { } pub fn validate_relay_message( - xdm: &CrossDomainMessage, + xdm: &CrossDomainMessage, T::Hash, T::MmrHash>, ) -> Result>, TransactionValidityError> { let mut should_init_channel = false; let next_nonce = match Channels::::get(xdm.src_chain_id, xdm.channel_id) { @@ -1087,7 +1091,7 @@ mod pallet { } pub fn validate_relay_message_response( - xdm: &CrossDomainMessage, + xdm: &CrossDomainMessage, T::Hash, T::MmrHash>, ) -> Result<(Message>, Nonce), TransactionValidityError> { // channel should be open and message should be present in outbox let next_nonce = match Channels::::get(xdm.src_chain_id, xdm.channel_id) { @@ -1134,7 +1138,7 @@ mod pallet { pub(crate) fn do_verify_xdm( next_nonce: Nonce, storage_key: StorageKey, - xdm: &CrossDomainMessage, + xdm: &CrossDomainMessage, T::Hash, T::MmrHash>, ) -> Result>, TransactionValidityError> { // channel should be either already be created or match the next channelId for chain. let next_channel_id = NextChannelId::::get(xdm.src_chain_id); @@ -1144,13 +1148,8 @@ mod pallet { // nonce should be either be next or in future. ensure!(xdm.nonce >= next_nonce, InvalidTransaction::Call); - let mmr_leaf = - EncodableOpaqueLeaf(xdm.proof.consensus_mmr_proof().opaque_mmr_leaf.0.clone()); - - let mmr_proof = xdm.proof.consensus_mmr_proof().proof.clone(); - let state_root = T::MmrProofVerifier::verify_proof_and_extract_consensus_state_root( - mmr_leaf, mmr_proof, + xdm.proof.consensus_mmr_proof(), ) .ok_or(InvalidTransaction::BadProof)?; @@ -1222,14 +1221,14 @@ where T: Config + frame_system::offchain::SendTransactionTypes>, { pub fn outbox_message_unsigned( - msg: CrossDomainMessage, + msg: CrossDomainMessage, T::Hash, T::MmrHash>, ) -> Option { let call = Call::relay_message { msg }; T::Extrinsic::new(call.into(), None) } pub fn inbox_response_message_unsigned( - msg: CrossDomainMessage, + msg: CrossDomainMessage, T::Hash, T::MmrHash>, ) -> Option { let call = Call::relay_message_response { msg }; T::Extrinsic::new(call.into(), None) diff --git a/domains/pallets/messenger/src/tests.rs b/domains/pallets/messenger/src/tests.rs index 1917979eae..87d749c513 100644 --- a/domains/pallets/messenger/src/tests.rs +++ b/domains/pallets/messenger/src/tests.rs @@ -17,11 +17,12 @@ use sp_core::{Blake2Hasher, H256}; use sp_domains::proof_provider_and_verifier::{StorageProofVerifier, VerificationError}; use sp_messenger::endpoint::{Endpoint, EndpointPayload, EndpointRequest, Sender}; use sp_messenger::messages::{ - ChainId, ConsensusChainMmrLeafProof, CrossDomainMessage, InitiateChannelParams, - MessageWeightTag, Payload, Proof, ProtocolMessageRequest, RequestResponse, VersionedPayload, + ChainId, CrossDomainMessage, InitiateChannelParams, MessageWeightTag, Payload, Proof, + ProtocolMessageRequest, RequestResponse, VersionedPayload, }; use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof as MmrProof}; use sp_runtime::traits::Convert; +use sp_subspace_mmr::ConsensusChainMmrLeafProof; use sp_trie::StorageProof; use std::collections::BTreeSet; @@ -83,8 +84,9 @@ fn create_channel(chain_id: ChainId, channel_id: ChannelId, fee_model: FeeModel< assert_eq!(messages_with_keys.outbox[0].storage_key, expected_key); } -fn default_consensus_proof() -> ConsensusChainMmrLeafProof { +fn default_consensus_proof() -> ConsensusChainMmrLeafProof { ConsensusChainMmrLeafProof { + consensus_block_number: Default::default(), consensus_block_hash: Default::default(), opaque_mmr_leaf: EncodableOpaqueLeaf(vec![]), proof: MmrProof { diff --git a/domains/primitives/messenger/Cargo.toml b/domains/primitives/messenger/Cargo.toml index a9094a6515..08600410bf 100644 --- a/domains/primitives/messenger/Cargo.toml +++ b/domains/primitives/messenger/Cargo.toml @@ -28,6 +28,7 @@ sp-inherents = { default-features = false, git = "https://github.com/subspace/po sp-mmr-primitives = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-runtime = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } sp-trie = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "ac2f3efb476ee3f5ac6bafefb458e9be158adb7f" } +sp-subspace-mmr = { version = "0.1.0", default-features = false, path = "../../../crates/sp-subspace-mmr" } [features] default = ["std"] @@ -45,7 +46,8 @@ std = [ "sp-inherents/std", "sp-mmr-primitives/std", "sp-runtime/std", - "sp-trie/std" + "sp-trie/std", + "sp-subspace-mmr/std" ] runtime-benchmarks = [] diff --git a/domains/primitives/messenger/src/lib.rs b/domains/primitives/messenger/src/lib.rs index 85558efb0c..890f41ca13 100644 --- a/domains/primitives/messenger/src/lib.rs +++ b/domains/primitives/messenger/src/lib.rs @@ -31,7 +31,6 @@ use frame_support::inherent::{InherentData, InherentIdentifier, IsFatalError}; use messages::{BlockMessagesWithStorageKey, CrossDomainMessage, MessageId}; use sp_domains::{ChainId, DomainAllowlistUpdates, DomainId}; use sp_inherents::Error; -use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof}; /// Messenger inherent identifier. pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"messengr"; @@ -45,24 +44,6 @@ impl OnXDMRewards for () { fn on_xdm_rewards(_: Balance) {} } -/// Trait to verify MMR proofs -pub trait MmrProofVerifier { - /// Returns consensus state root if the given MMR proof is valid - fn verify_proof_and_extract_consensus_state_root( - leaf: EncodableOpaqueLeaf, - proof: Proof, - ) -> Option; -} - -impl MmrProofVerifier for () { - fn verify_proof_and_extract_consensus_state_root( - _leaf: EncodableOpaqueLeaf, - _proof: Proof, - ) -> Option { - None - } -} - /// Trait that return various storage keys for storages on Consensus chain and domains pub trait StorageKeys { /// Returns the storage key for confirmed domain block on conensus chain @@ -158,9 +139,10 @@ impl sp_inherents::InherentDataProvider for InherentDataProvider { sp_api::decl_runtime_apis! { /// Api useful for relayers to fetch messages and submit transactions. - pub trait RelayerApi + pub trait RelayerApi where BlockNumber: Encode + Decode, + CNumber: Encode + Decode, CHash: Encode + Decode, { /// Returns all the outbox and inbox responses to deliver. @@ -169,12 +151,12 @@ sp_api::decl_runtime_apis! { /// Constructs an outbox message to the dst_chain as an unsigned extrinsic. fn outbox_message_unsigned( - msg: CrossDomainMessage, + msg: CrossDomainMessage, ) -> Option; /// Constructs an inbox response message to the dst_chain as an unsigned extrinsic. fn inbox_response_message_unsigned( - msg: CrossDomainMessage, + msg: CrossDomainMessage, ) -> Option; /// Returns true if the outbox message is ready to be relayed to dst_chain. diff --git a/domains/primitives/messenger/src/messages.rs b/domains/primitives/messenger/src/messages.rs index f65c78e8c6..9f564167f3 100644 --- a/domains/primitives/messenger/src/messages.rs +++ b/domains/primitives/messenger/src/messages.rs @@ -7,9 +7,9 @@ use alloc::vec::Vec; use codec::{Decode, Encode}; use scale_info::TypeInfo; pub use sp_domains::{ChainId, ChannelId}; -use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof as MmrProof}; use sp_runtime::app_crypto::sp_core::U256; use sp_runtime::DispatchError; +use sp_subspace_mmr::ConsensusChainMmrLeafProof; use sp_trie::StorageProof; /// Nonce used as an identifier and ordering of messages within a channel. @@ -135,43 +135,17 @@ pub struct Message { pub last_delivered_message_response_nonce: Option, } -/// Consensus chain MMR leaf and its Proof at specific block -#[derive(Debug, Encode, Decode, Eq, PartialEq, TypeInfo)] -pub struct ConsensusChainMmrLeafProof { - /// Consensus block info from which this proof was generated. - pub consensus_block_hash: BlockHash, - /// Encoded MMR leaf - pub opaque_mmr_leaf: EncodableOpaqueLeaf, - /// MMR proof for the leaf above. - pub proof: MmrProof, -} - -// TODO: update upstream `EncodableOpaqueLeaf` to derive clone. -impl Clone for ConsensusChainMmrLeafProof -where - BlockHash: Clone, - MmrHash: Clone, -{ - fn clone(&self) -> Self { - Self { - consensus_block_hash: self.consensus_block_hash.clone(), - opaque_mmr_leaf: EncodableOpaqueLeaf(self.opaque_mmr_leaf.0.clone()), - proof: self.proof.clone(), - } - } -} - #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] -pub enum Proof { +pub enum Proof { Consensus { /// Consensus chain MMR leaf proof. - consensus_chain_mmr_proof: ConsensusChainMmrLeafProof, + consensus_chain_mmr_proof: ConsensusChainMmrLeafProof, /// Storage proof that message is processed on src_chain. message_proof: StorageProof, }, Domain { /// Consensus chain MMR leaf proof. - consensus_chain_mmr_proof: ConsensusChainMmrLeafProof, + consensus_chain_mmr_proof: ConsensusChainMmrLeafProof, /// Storage proof that src domain chain's block is out of the challenge period on Consensus chain. domain_proof: StorageProof, /// Storage proof that message is processed on src_chain. @@ -179,7 +153,7 @@ pub enum Proof { }, } -impl Proof { +impl Proof { pub fn message_proof(&self) -> StorageProof { match self { Proof::Consensus { message_proof, .. } => message_proof.clone(), @@ -187,8 +161,11 @@ impl Proof { } } - pub fn consensus_mmr_proof(&self) -> ConsensusChainMmrLeafProof + pub fn consensus_mmr_proof( + &self, + ) -> ConsensusChainMmrLeafProof where + CBlockNumber: Clone, CBlockHash: Clone, MmrHash: Clone, { @@ -214,7 +191,7 @@ impl Proof { /// Cross Domain message contains Message and its proof on src_chain. #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] -pub struct CrossDomainMessage { +pub struct CrossDomainMessage { /// Chain which initiated this message. pub src_chain_id: ChainId, /// Chain this message is intended for. @@ -224,7 +201,7 @@ pub struct CrossDomainMessage { /// Message nonce within the channel. pub nonce: Nonce, /// Proof of message processed on src_chain. - pub proof: Proof, + pub proof: Proof, /// The message weight tag pub weight_tag: MessageWeightTag, } @@ -253,10 +230,10 @@ pub struct BlockMessagesWithStorageKey { pub inbox_responses: Vec, } -impl CrossDomainMessage { +impl CrossDomainMessage { pub fn from_relayer_msg_with_proof( r_msg: BlockMessageWithStorageKey, - proof: Proof, + proof: Proof, ) -> Self { CrossDomainMessage { src_chain_id: r_msg.src_chain_id, diff --git a/domains/runtime/auto-id/src/lib.rs b/domains/runtime/auto-id/src/lib.rs index 2a4dc5f7ad..513e935318 100644 --- a/domains/runtime/auto-id/src/lib.rs +++ b/domains/runtime/auto-id/src/lib.rs @@ -45,11 +45,11 @@ use sp_messenger::messages::{ BlockMessagesWithStorageKey, ChainId, CrossDomainMessage, MessageId, MessageKey, }; use sp_messenger_host_functions::{get_storage_key, StorageKeyRequest}; -use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof}; +use sp_mmr_primitives::EncodableOpaqueLeaf; use sp_runtime::generic::Era; use sp_runtime::traits::{ - AccountIdLookup, BlakeTwo256, Block as BlockT, Checkable, Keccak256, One, SignedExtension, - ValidateUnsigned, Zero, + AccountIdLookup, BlakeTwo256, Block as BlockT, Checkable, Keccak256, NumberFor, One, + SignedExtension, ValidateUnsigned, Zero, }; use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, @@ -62,7 +62,7 @@ pub use sp_runtime::{MultiAddress, Perbill, Permill}; use sp_std::marker::PhantomData; use sp_std::prelude::*; use sp_subspace_mmr::domain_mmr_runtime_interface::verify_mmr_proof; -use sp_subspace_mmr::MmrLeaf; +use sp_subspace_mmr::{ConsensusChainMmrLeafProof, MmrLeaf}; use sp_version::RuntimeVersion; use subspace_runtime_primitives::{ BlockNumber as ConsensusBlockNumber, Hash as ConsensusBlockHash, Moment, @@ -314,11 +314,16 @@ type MmrHash = ::Output; pub struct MmrProofVerifier; -impl sp_messenger::MmrProofVerifier for MmrProofVerifier { +impl sp_subspace_mmr::MmrProofVerifier, Hash> for MmrProofVerifier { fn verify_proof_and_extract_consensus_state_root( - opaque_leaf: EncodableOpaqueLeaf, - proof: Proof, + mmr_leaf_proof: ConsensusChainMmrLeafProof, Hash, MmrHash>, ) -> Option { + let ConsensusChainMmrLeafProof { + opaque_mmr_leaf: opaque_leaf, + proof, + .. + } = mmr_leaf_proof; + let leaf: MmrLeaf = opaque_leaf.into_opaque_leaf().try_decode()?; let state_root = leaf.state_root(); @@ -825,16 +830,16 @@ impl_runtime_apis! { } } - impl sp_messenger::RelayerApi for Runtime { + impl sp_messenger::RelayerApi for Runtime { fn block_messages() -> BlockMessagesWithStorageKey { Messenger::get_block_messages() } - fn outbox_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn outbox_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::outbox_message_unsigned(msg) } - fn inbox_response_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn inbox_response_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::inbox_response_message_unsigned(msg) } diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index 29aeecb692..56f32e2b64 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -55,12 +55,12 @@ use sp_messenger::messages::{ BlockMessagesWithStorageKey, ChainId, CrossDomainMessage, MessageId, MessageKey, }; use sp_messenger_host_functions::{get_storage_key, StorageKeyRequest}; -use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof}; +use sp_mmr_primitives::EncodableOpaqueLeaf; use sp_runtime::generic::Era; use sp_runtime::traits::{ BlakeTwo256, Block as BlockT, Checkable, DispatchInfoOf, Dispatchable, IdentifyAccount, - IdentityLookup, Keccak256, One, PostDispatchInfoOf, SignedExtension, UniqueSaturatedInto, - ValidateUnsigned, Verify, Zero, + IdentityLookup, Keccak256, NumberFor, One, PostDispatchInfoOf, SignedExtension, + UniqueSaturatedInto, ValidateUnsigned, Verify, Zero, }; use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, @@ -73,7 +73,7 @@ pub use sp_runtime::{MultiAddress, Perbill, Permill}; use sp_std::marker::PhantomData; use sp_std::prelude::*; use sp_subspace_mmr::domain_mmr_runtime_interface::verify_mmr_proof; -use sp_subspace_mmr::MmrLeaf; +use sp_subspace_mmr::{ConsensusChainMmrLeafProof, MmrLeaf}; use sp_version::RuntimeVersion; use subspace_runtime_primitives::{ BlockNumber as ConsensusBlockNumber, Hash as ConsensusBlockHash, Moment, @@ -426,11 +426,16 @@ type MmrHash = ::Output; pub struct MmrProofVerifier; -impl sp_messenger::MmrProofVerifier for MmrProofVerifier { +impl sp_subspace_mmr::MmrProofVerifier, Hash> for MmrProofVerifier { fn verify_proof_and_extract_consensus_state_root( - opaque_leaf: EncodableOpaqueLeaf, - proof: Proof, + mmr_leaf_proof: ConsensusChainMmrLeafProof, Hash, MmrHash>, ) -> Option { + let ConsensusChainMmrLeafProof { + opaque_mmr_leaf: opaque_leaf, + proof, + .. + } = mmr_leaf_proof; + let leaf: MmrLeaf = opaque_leaf.into_opaque_leaf().try_decode()?; let state_root = leaf.state_root(); @@ -1122,16 +1127,16 @@ impl_runtime_apis! { } } - impl sp_messenger::RelayerApi for Runtime { + impl sp_messenger::RelayerApi for Runtime { fn block_messages() -> BlockMessagesWithStorageKey { Messenger::get_block_messages() } - fn outbox_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn outbox_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::outbox_message_unsigned(msg) } - fn inbox_response_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn inbox_response_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::inbox_response_message_unsigned(msg) } diff --git a/domains/service/src/domain.rs b/domains/service/src/domain.rs index 9e842777c0..768ae41208 100644 --- a/domains/service/src/domain.rs +++ b/domains/service/src/domain.rs @@ -86,7 +86,7 @@ where + TransactionPaymentRuntimeApi + DomainCoreApi + MessengerApi - + RelayerApi, CBlock::Hash>, + + RelayerApi, NumberFor, CBlock::Hash>, AccountId: Encode + Decode, { /// Task manager. @@ -273,7 +273,7 @@ where + Sync + 'static, CClient::Api: DomainsApi - + RelayerApi, CBlock::Hash> + + RelayerApi, NumberFor, CBlock::Hash> + MessengerApi + BundleProducerElectionApi + FraudProofApi @@ -293,7 +293,7 @@ where + TaggedTransactionQueue + AccountNonceApi + TransactionPaymentRuntimeApi - + RelayerApi, CBlock::Hash>, + + RelayerApi, NumberFor, CBlock::Hash>, AccountId: DeserializeOwned + Encode + Decode diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 9c815f1d61..0fc44271d5 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -53,12 +53,12 @@ use sp_messenger::messages::{ BlockMessagesWithStorageKey, ChainId, ChannelId, CrossDomainMessage, MessageId, MessageKey, }; use sp_messenger_host_functions::{get_storage_key, StorageKeyRequest}; -use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof}; +use sp_mmr_primitives::EncodableOpaqueLeaf; use sp_runtime::generic::Era; use sp_runtime::traits::{ BlakeTwo256, Block as BlockT, Checkable, DispatchInfoOf, Dispatchable, IdentifyAccount, - IdentityLookup, Keccak256, One, PostDispatchInfoOf, SignedExtension, UniqueSaturatedInto, - ValidateUnsigned, Verify, Zero, + IdentityLookup, Keccak256, NumberFor, One, PostDispatchInfoOf, SignedExtension, + UniqueSaturatedInto, ValidateUnsigned, Verify, Zero, }; use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, @@ -71,7 +71,7 @@ pub use sp_runtime::{MultiAddress, Perbill, Permill}; use sp_std::marker::PhantomData; use sp_std::prelude::*; use sp_subspace_mmr::domain_mmr_runtime_interface::verify_mmr_proof; -use sp_subspace_mmr::MmrLeaf; +use sp_subspace_mmr::{ConsensusChainMmrLeafProof, MmrLeaf}; use sp_version::RuntimeVersion; use subspace_runtime_primitives::{ BlockNumber as ConsensusBlockNumber, Hash as ConsensusBlockHash, Moment, SSC, @@ -412,11 +412,16 @@ type MmrHash = ::Output; pub struct MmrProofVerifier; -impl sp_messenger::MmrProofVerifier for MmrProofVerifier { +impl sp_subspace_mmr::MmrProofVerifier, Hash> for MmrProofVerifier { fn verify_proof_and_extract_consensus_state_root( - opaque_leaf: EncodableOpaqueLeaf, - proof: Proof, + mmr_leaf_proof: ConsensusChainMmrLeafProof, Hash, MmrHash>, ) -> Option { + let ConsensusChainMmrLeafProof { + opaque_mmr_leaf: opaque_leaf, + proof, + .. + } = mmr_leaf_proof; + let leaf: MmrLeaf = opaque_leaf.into_opaque_leaf().try_decode()?; let state_root = leaf.state_root(); @@ -1083,16 +1088,16 @@ impl_runtime_apis! { } } - impl sp_messenger::RelayerApi for Runtime { + impl sp_messenger::RelayerApi for Runtime { fn block_messages() -> BlockMessagesWithStorageKey { Messenger::get_block_messages() } - fn outbox_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn outbox_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::outbox_message_unsigned(msg) } - fn inbox_response_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn inbox_response_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::inbox_response_message_unsigned(msg) } diff --git a/domains/test/service/src/domain.rs b/domains/test/service/src/domain.rs index 2f86177f33..41ab41fb39 100644 --- a/domains/test/service/src/domain.rs +++ b/domains/test/service/src/domain.rs @@ -86,7 +86,7 @@ where + TaggedTransactionQueue + AccountNonceApi + TransactionPaymentRuntimeApi - + RelayerApi, ::Hash>, + + RelayerApi, NumberFor, ::Hash>, AccountId: Encode + Decode + FromKeyring, { /// The domain id @@ -137,7 +137,7 @@ where + AccountNonceApi + TransactionPaymentRuntimeApi + MessengerApi - + RelayerApi, ::Hash> + + RelayerApi, NumberFor, ::Hash> + OnchainStateApi + EthereumRuntimeRPCApi, AccountId: DeserializeOwned diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index ab224e7303..135b43a4ee 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -65,7 +65,7 @@ use sp_messenger::messages::{ BlockMessagesWithStorageKey, ChainId, ChannelId, CrossDomainMessage, MessageId, MessageKey, }; use sp_messenger_host_functions::{get_storage_key, StorageKeyRequest}; -use sp_mmr_primitives::{EncodableOpaqueLeaf, Proof}; +use sp_mmr_primitives::EncodableOpaqueLeaf; use sp_runtime::traits::{ AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, ConstBool, DispatchInfoOf, Keccak256, NumberFor, PostDispatchInfoOf, Zero, @@ -80,6 +80,7 @@ use sp_std::collections::btree_map::BTreeMap; use sp_std::iter::Peekable; use sp_std::marker::PhantomData; use sp_std::prelude::*; +use sp_subspace_mmr::ConsensusChainMmrLeafProof; use sp_version::RuntimeVersion; use static_assertions::const_assert; use subspace_core_primitives::objects::{BlockObject, BlockObjectMapping}; @@ -543,11 +544,16 @@ parameter_types! { pub struct MmrProofVerifier; -impl sp_messenger::MmrProofVerifier for MmrProofVerifier { +impl sp_subspace_mmr::MmrProofVerifier, Hash> for MmrProofVerifier { fn verify_proof_and_extract_consensus_state_root( - opaque_leaf: EncodableOpaqueLeaf, - proof: Proof, + mmr_leaf_proof: ConsensusChainMmrLeafProof, Hash, mmr::Hash>, ) -> Option { + let ConsensusChainMmrLeafProof { + opaque_mmr_leaf: opaque_leaf, + proof, + .. + } = mmr_leaf_proof; + let leaf: mmr::Leaf = opaque_leaf.into_opaque_leaf().try_decode()?; let state_root = leaf.state_root(); Mmr::verify_leaves(vec![leaf], proof).ok()?; @@ -1447,16 +1453,16 @@ impl_runtime_apis! { } } - impl sp_messenger::RelayerApi::Hash> for Runtime { + impl sp_messenger::RelayerApi::Hash> for Runtime { fn block_messages() -> BlockMessagesWithStorageKey { Messenger::get_block_messages() } - fn outbox_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn outbox_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::outbox_message_unsigned(msg) } - fn inbox_response_message_unsigned(msg: CrossDomainMessage<::Hash, ::Hash>) -> Option<::Extrinsic> { + fn inbox_response_message_unsigned(msg: CrossDomainMessage, ::Hash, ::Hash>) -> Option<::Extrinsic> { Messenger::inbox_response_message_unsigned(msg) } From bde4d7bdb237f80742afbcb5e69b7c1db33c8451 Mon Sep 17 00:00:00 2001 From: linning Date: Wed, 17 Apr 2024 16:35:33 +0800 Subject: [PATCH 4/8] Verify XDM stateless in the consensus chain Signed-off-by: linning --- Cargo.lock | 9 +++++ crates/subspace-runtime/src/lib.rs | 22 ++++++++---- domains/client/relayer/src/lib.rs | 52 +++++++++++++++++++-------- test/subspace-test-runtime/src/lib.rs | 45 ++++++++++------------- 4 files changed, 80 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 740954a207..7452f01156 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2679,6 +2679,7 @@ dependencies = [ "sp-messenger", "sp-mmr-primitives", "sp-runtime", + "sp-subspace-mmr", "tracing", ] @@ -2725,11 +2726,13 @@ dependencies = [ "sp-mmr-primitives", "sp-runtime", "sp-state-machine", + "sp-subspace-mmr", "sp-transaction-pool", "sp-trie", "sp-weights", "subspace-core-primitives", "subspace-runtime-primitives", + "subspace-test-primitives", "subspace-test-runtime", "subspace-test-service", "tempfile", @@ -7643,6 +7646,7 @@ dependencies = [ "sp-mmr-primitives", "sp-runtime", "sp-state-machine", + "sp-subspace-mmr", "sp-trie", ] @@ -11562,6 +11566,7 @@ dependencies = [ "sp-inherents", "sp-mmr-primitives", "sp-runtime", + "sp-subspace-mmr", "sp-trie", ] @@ -12652,7 +12657,11 @@ version = "0.1.0" dependencies = [ "parity-scale-codec", "sp-api", + "sp-core", + "sp-domains", "sp-messenger", + "sp-runtime", + "sp-subspace-mmr", "subspace-runtime-primitives", ] diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 6b4c4ce68a..1cf99579f8 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -505,21 +505,31 @@ impl sp_messenger::OnXDMRewards for OnXDMRewards { pub struct MmrProofVerifier; -// TODO: verify the proof stateless impl sp_subspace_mmr::MmrProofVerifier, Hash> for MmrProofVerifier { fn verify_proof_and_extract_consensus_state_root( mmr_leaf_proof: ConsensusChainMmrLeafProof, Hash, mmr::Hash>, ) -> Option { let ConsensusChainMmrLeafProof { - opaque_mmr_leaf: opaque_leaf, + consensus_block_number, + opaque_mmr_leaf, proof, .. } = mmr_leaf_proof; - let leaf: mmr::Leaf = opaque_leaf.into_opaque_leaf().try_decode()?; - let state_root = leaf.state_root(); - Mmr::verify_leaves(vec![leaf], proof).ok()?; - Some(state_root) + let mmr_root = SubspaceMmr::mmr_root_hash(consensus_block_number)?; + + pallet_mmr::verify_leaves_proof::( + mmr_root, + vec![mmr::DataOrHash::Data( + EncodableOpaqueLeaf(opaque_mmr_leaf.0.clone()).into_opaque_leaf(), + )], + proof, + ) + .ok()?; + + let leaf: mmr::Leaf = opaque_mmr_leaf.into_opaque_leaf().try_decode()?; + + Some(leaf.state_root()) } } diff --git a/domains/client/relayer/src/lib.rs b/domains/client/relayer/src/lib.rs index 340dc6c931..a8e301f4a4 100644 --- a/domains/client/relayer/src/lib.rs +++ b/domains/client/relayer/src/lib.rs @@ -93,7 +93,8 @@ type UnProcessedBlocks = Vec<(NumberFor, ::Hash)> fn construct_consensus_mmr_proof( consensus_chain_client: &Arc, - block_number: NumberFor, + dst_chain_id: ChainId, + (block_number, block_hash): (NumberFor, Block::Hash), ) -> Result, Block::Hash, H256>, Error> where Block: BlockT, @@ -102,17 +103,31 @@ where { let api = consensus_chain_client.runtime_api(); let best_hash = consensus_chain_client.info().best_hash; + let best_number = consensus_chain_client.info().best_number; + + let (prove_at_number, prove_at_hash) = match dst_chain_id { + // The consensus chain will verify the MMR proof stateless with the MMR root + // stored in the runtime, we need to generate the proof with the best block + // with the latest MMR root in the runtime so the proof will be valid as long + // as the MMR root is available when verifying the proof. + ChainId::Consensus => (best_number, best_hash), + // The domain chain will verify the MMR proof with the offchain MMR leaf data + // in the consensus client, we need to generate the proof with the proving block + // (i.e. finalized block) to avoid potential consensus fork and ensure the verification + // result is deterministic. + ChainId::Domain(_) => (block_number, block_hash), + }; + let (mut leaves, proof) = api - .generate_proof(best_hash, vec![block_number], Some(block_number)) + .generate_proof(best_hash, vec![block_number], Some(prove_at_number)) .map_err(Error::ApiError)? .map_err(Error::MmrProof)?; debug_assert!(leaves.len() == 1, "should always be of length 1"); let leaf = leaves.pop().ok_or(Error::MmrLeafMissing)?; Ok(ConsensusChainMmrLeafProof { - // FIXME: recorrect the value - consensus_block_number: Default::default(), - consensus_block_hash: Default::default(), + consensus_block_number: prove_at_number, + consensus_block_hash: prove_at_hash, opaque_mmr_leaf: leaf, proof, }) @@ -124,11 +139,11 @@ fn construct_cross_chain_message_and_submit Result<(), Error> where - Submitter: Fn(CrossDomainMessage) -> Result<(), Error>, - ProofConstructor: Fn(&[u8]) -> Result, Error>, + Submitter: Fn(CrossDomainMessage) -> Result<(), Error>, + ProofConstructor: Fn(&[u8], ChainId) -> Result, Error>, { for msg in msgs { - let proof = match proof_constructor(&msg.storage_key) { + let proof = match proof_constructor(&msg.storage_key, msg.dst_chain_id) { Ok(proof) => proof, Err(err) => { tracing::error!( @@ -187,7 +202,7 @@ where /// this message is the response of the Inbox message execution. fn gossip_inbox_message_response( client: &Arc, - msg: CrossDomainMessage, + msg: CrossDomainMessage, sink: &GossipMessageSink, ) -> Result<(), Error> where @@ -222,6 +237,7 @@ where finalized_block: (NumberFor, Block::Hash), block_hash_to_process: Block::Hash, key: &[u8], + dst_chain_id: ChainId, ) -> Result, Error> where Client::Api: MmrApi>, @@ -231,7 +247,7 @@ where .map_err(|_| Error::ConstructStorageProof)?; let consensus_chain_mmr_proof = - construct_consensus_mmr_proof(consensus_chain_client, finalized_block.0)?; + construct_consensus_mmr_proof(consensus_chain_client, dst_chain_id, finalized_block)?; Ok(Proof::Consensus { consensus_chain_mmr_proof, @@ -318,12 +334,13 @@ where construct_cross_chain_message_and_submit::, Block::Hash, _, _>( filtered_messages.outbox, - |key| { + |key, dst_chain_id| { Self::construct_consensus_chain_xdm_proof_for_key_at( consensus_chain_client, finalized_block, block_hash_to_process, key, + dst_chain_id, ) }, |msg| gossip_outbox_message(consensus_chain_client, msg, gossip_message_sink), @@ -331,12 +348,13 @@ where construct_cross_chain_message_and_submit::, Block::Hash, _, _>( filtered_messages.inbox_responses, - |key| { + |key, dst_chain_id| { Self::construct_consensus_chain_xdm_proof_for_key_at( consensus_chain_client, finalized_block, block_hash_to_process, key, + dst_chain_id, ) }, |msg| gossip_inbox_message_response(consensus_chain_client, msg, gossip_message_sink), @@ -398,7 +416,7 @@ where construct_cross_chain_message_and_submit::, CBlock::Hash, _, _>( filtered_messages.outbox, - |key| { + |key, dst_chain_id| { Self::construct_domain_chain_xdm_proof_for_key_at( finalized_consensus_block, consensus_chain_client, @@ -406,6 +424,7 @@ where confirmed_domain_block_hash, key, domain_proof.clone(), + dst_chain_id, ) }, |msg| gossip_outbox_message(domain_client, msg, gossip_message_sink), @@ -413,7 +432,7 @@ where construct_cross_chain_message_and_submit::, CBlock::Hash, _, _>( filtered_messages.inbox_responses, - |key| { + |key, dst_chain_id| { Self::construct_domain_chain_xdm_proof_for_key_at( finalized_consensus_block, consensus_chain_client, @@ -421,6 +440,7 @@ where confirmed_domain_block_hash, key, domain_proof.clone(), + dst_chain_id, ) }, |msg| gossip_inbox_message_response(domain_client, msg, gossip_message_sink), @@ -437,6 +457,7 @@ where block_hash: Block::Hash, key: &[u8], domain_proof: StorageProof, + dst_chain_id: ChainId, ) -> Result, Error> where CBlock: BlockT, @@ -447,7 +468,8 @@ where { let consensus_chain_mmr_proof = construct_consensus_mmr_proof( consensus_chain_client, - consensus_chain_finalized_block.0, + dst_chain_id, + consensus_chain_finalized_block, )?; let proof = domain_client diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index 135b43a4ee..98e5e8fd65 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -549,15 +549,26 @@ impl sp_subspace_mmr::MmrProofVerifier, Hash> for Mm mmr_leaf_proof: ConsensusChainMmrLeafProof, Hash, mmr::Hash>, ) -> Option { let ConsensusChainMmrLeafProof { - opaque_mmr_leaf: opaque_leaf, + consensus_block_number, + opaque_mmr_leaf, proof, .. } = mmr_leaf_proof; - let leaf: mmr::Leaf = opaque_leaf.into_opaque_leaf().try_decode()?; - let state_root = leaf.state_root(); - Mmr::verify_leaves(vec![leaf], proof).ok()?; - Some(state_root) + let mmr_root = SubspaceMmr::mmr_root_hash(consensus_block_number)?; + + pallet_mmr::verify_leaves_proof::( + mmr_root, + vec![mmr::DataOrHash::Data( + EncodableOpaqueLeaf(opaque_mmr_leaf.0.clone()).into_opaque_leaf(), + )], + proof, + ) + .ok()?; + + let leaf: mmr::Leaf = opaque_mmr_leaf.into_opaque_leaf().try_decode()?; + + Some(leaf.state_root()) } } @@ -1543,28 +1554,8 @@ impl_runtime_apis! { Messenger::get_open_channel_for_chain(dst_chain_id).map(|(c, _)| c) } - fn verify_proof_and_extract_consensus_state_root(mmr_leaf_proof: sp_subspace_mmr::ConsensusChainMmrLeafProof, ::Hash, H256>) -> Option { - let sp_subspace_mmr::ConsensusChainMmrLeafProof { - consensus_block_number, - opaque_mmr_leaf, - proof, - .. - } = mmr_leaf_proof; - - let mmr_root = SubspaceMmr::mmr_root_hash(consensus_block_number)?; - - pallet_mmr::verify_leaves_proof::( - mmr_root, - vec![mmr::DataOrHash::Data( - EncodableOpaqueLeaf(opaque_mmr_leaf.0.clone()).into_opaque_leaf(), - )], - proof, - ) - .ok()?; - - let leaf: mmr::Leaf = opaque_mmr_leaf.into_opaque_leaf().try_decode()?; - - Some(leaf.state_root()) + fn verify_proof_and_extract_consensus_state_root(mmr_leaf_proof: ConsensusChainMmrLeafProof, ::Hash, H256>) -> Option { + >::verify_proof_and_extract_consensus_state_root(mmr_leaf_proof) } } From 1846c6b744971cdc39243c6d93a0e2e9c65aec13 Mon Sep 17 00:00:00 2001 From: linning Date: Fri, 19 Apr 2024 02:58:13 +0800 Subject: [PATCH 5/8] Add TODO for removing the mmr root digest Signed-off-by: linning --- crates/pallet-subspace-mmr/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/pallet-subspace-mmr/src/lib.rs b/crates/pallet-subspace-mmr/src/lib.rs index 428338dc7b..035dadde6c 100644 --- a/crates/pallet-subspace-mmr/src/lib.rs +++ b/crates/pallet-subspace-mmr/src/lib.rs @@ -57,6 +57,8 @@ mod pallet { impl OnNewRoot for Pallet { fn on_new_root(root: &T::MmrRootHash) { + // TODO: this digest is not used remove it before next network reset but keep it + // as is for now to keep compatible with gemini-3h. let digest = DigestItem::new_mmr_root(*root); >::deposit_log(digest); From 75d47e942d60db4d3f9048c7c4d7ee7a183bd447 Mon Sep 17 00:00:00 2001 From: NingLin-P Date: Fri, 19 Apr 2024 16:44:56 +0800 Subject: [PATCH 6/8] Update crates/pallet-subspace-mmr/src/lib.rs Co-authored-by: Dastan <88332432+dastansam@users.noreply.github.com> --- crates/pallet-subspace-mmr/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/pallet-subspace-mmr/src/lib.rs b/crates/pallet-subspace-mmr/src/lib.rs index 035dadde6c..4d9d3f9a82 100644 --- a/crates/pallet-subspace-mmr/src/lib.rs +++ b/crates/pallet-subspace-mmr/src/lib.rs @@ -41,10 +41,10 @@ mod pallet { pub trait Config: frame_system::Config + From> { type MmrRootHash: Parameter + Copy + MaxEncodedLen; - /// The number of mmr root hash store in runtime, it will be used to verify mmmr - /// proof stateless, the number of roots stored here represent the number of blocks - /// for which the mmr proof is valid since it is generated, after that the mmr proof - /// will be expired and the prover need to re-generate the proof. + /// The number of mmr root hashes to store in the runtime. It will be used to verify mmr + /// proof statelessly and the number of roots stored here represents the number of blocks + /// for which the mmr proof is valid since it is generated. After that the mmr proof + /// will be expired and the prover needs to re-generate the proof. type MmrRootHashCount: Get; } From 19f3d3a7ad090c83c7e05330f6769f1ddf0704a8 Mon Sep 17 00:00:00 2001 From: NingLin-P Date: Fri, 19 Apr 2024 16:45:04 +0800 Subject: [PATCH 7/8] Update test/subspace-test-primitives/src/lib.rs Co-authored-by: Dastan <88332432+dastansam@users.noreply.github.com> --- test/subspace-test-primitives/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/subspace-test-primitives/src/lib.rs b/test/subspace-test-primitives/src/lib.rs index 2b35e74308..7a6151aab0 100644 --- a/test/subspace-test-primitives/src/lib.rs +++ b/test/subspace-test-primitives/src/lib.rs @@ -20,7 +20,7 @@ sp_api::decl_runtime_apis! { /// Returns the last open channel for a given domain. fn get_open_channel_for_chain(dst_chain_id: ChainId) -> Option; - /// Verify the mmr proof stateless and extract the state root. + /// Verify the mmr proof statelessly and extract the state root. fn verify_proof_and_extract_consensus_state_root(proof: ConsensusChainMmrLeafProof, Block::Hash, H256>) -> Option; } } From ff17a5f3ac3fa6568ccbfa48399f14949b25f037 Mon Sep 17 00:00:00 2001 From: NingLin-P Date: Fri, 19 Apr 2024 16:45:10 +0800 Subject: [PATCH 8/8] Update domains/client/relayer/src/lib.rs Co-authored-by: Dastan <88332432+dastansam@users.noreply.github.com> --- domains/client/relayer/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domains/client/relayer/src/lib.rs b/domains/client/relayer/src/lib.rs index a8e301f4a4..cfd396d892 100644 --- a/domains/client/relayer/src/lib.rs +++ b/domains/client/relayer/src/lib.rs @@ -106,7 +106,7 @@ where let best_number = consensus_chain_client.info().best_number; let (prove_at_number, prove_at_hash) = match dst_chain_id { - // The consensus chain will verify the MMR proof stateless with the MMR root + // The consensus chain will verify the MMR proof statelessly with the MMR root // stored in the runtime, we need to generate the proof with the best block // with the latest MMR root in the runtime so the proof will be valid as long // as the MMR root is available when verifying the proof.