From 42656728258372d04940ce7f3d224ff77ebf4ae2 Mon Sep 17 00:00:00 2001 From: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> Date: Tue, 6 May 2025 14:38:53 +0300 Subject: [PATCH] =?UTF-8?q?feat(operator):=20=E2=9C=A8=20Store=20message?= =?UTF-8?q?=20commitments=20from=20Snowbrigde=20Outbound=20pallet=20in=20t?= =?UTF-8?q?he=20Beefy=20leaf=20extra=20field=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR implements functionality to store outbound message commitments in the BEEFY MMR leaf extra field for cross-chain verification. A new `pallet-outbound-commitment-store` has been introduced to facilitate this process. ## Changes - Added a new `pallet-outbound-commitment-store` pallet to capture and store outbound message commitments - Implemented the `CommitmentHandler` to receive commitments from the outbound queue and store them in the new pallet - Updated the `LeafExtraDataProvider` to include these commitments in the BEEFY MMR leaf extra field ## Implementation Details The process works as follows: 1. The outbound queue generates a commitment hash for messages 2. The commitment is stored in the `pallet-outbound-commitment-store` via the `CommitmentHandler` 3. The `LeafExtraDataProvider` retrieves the latest commitment and includes it in the BEEFY MMR leaf extra field 4. This commitment can then be verified by other chains using the BEEFY light client The new pallet provides the necessary functions to store and retrieve these commitments. --- operator/Cargo.lock | 12 +++++ operator/Cargo.toml | 2 + .../outbound-commitment-store/Cargo.toml | 34 +++++++++++++ .../outbound-commitment-store/src/lib.rs | 49 +++++++++++++++++++ operator/runtime/Cargo.toml | 4 ++ operator/runtime/src/configs/mod.rs | 24 ++++++--- operator/runtime/src/lib.rs | 2 + 7 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 operator/pallets/outbound-commitment-store/Cargo.toml create mode 100644 operator/pallets/outbound-commitment-store/src/lib.rs diff --git a/operator/Cargo.lock b/operator/Cargo.lock index 11d0ea0b..28aae80d 100644 --- a/operator/Cargo.lock +++ b/operator/Cargo.lock @@ -2425,6 +2425,7 @@ dependencies = [ "pallet-mmr", "pallet-multisig", "pallet-offences", + "pallet-outbound-commitment-store", "pallet-parameters", "pallet-preimage", "pallet-scheduler", @@ -7631,6 +7632,17 @@ dependencies = [ "sp-staking", ] +[[package]] +name = "pallet-outbound-commitment-store" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", +] + [[package]] name = "pallet-parameters" version = "0.10.1" diff --git a/operator/Cargo.toml b/operator/Cargo.toml index fdefbf8c..713981c5 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [workspace] members = [ "node", + "pallets/outbound-commitment-store", "pallets/ethereum-client", "pallets/inbound-queue-v2", "pallets/outbound-queue-v2", @@ -28,6 +29,7 @@ datahaven-runtime = { path = "./runtime", default-features = false } datahaven-runtime-common = { path = "./runtime/common", default-features = false } dhp-bridge = { path = "./primitives/bridge", default-features = false } pallet-validator-set = { path = "./pallets/validator-set", default-features = false } +pallet-outbound-commitment-store = { path = "./pallets/outbound-commitment-store", default-features = false } # Crates.io (wasm) alloy-core = { version = "0.8.15", default-features = false } diff --git a/operator/pallets/outbound-commitment-store/Cargo.toml b/operator/pallets/outbound-commitment-store/Cargo.toml new file mode 100644 index 00000000..69fbfe9d --- /dev/null +++ b/operator/pallets/outbound-commitment-store/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "pallet-outbound-commitment-store" +version = "0.1.0" +description = "Pallet for storing the latest commitment hash from the outbound queue for cross-chain verification" +authors = { workspace = true } +edition = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true, features = ["derive"] } +scale-info = { workspace = true, features = ["derive"] } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] \ No newline at end of file diff --git a/operator/pallets/outbound-commitment-store/src/lib.rs b/operator/pallets/outbound-commitment-store/src/lib.rs new file mode 100644 index 00000000..be1f2fc9 --- /dev/null +++ b/operator/pallets/outbound-commitment-store/src/lib.rs @@ -0,0 +1,49 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{pallet_prelude::*, traits::StorageVersion}; +use sp_core::H256; + +pub use pallet::*; + +/// Current storage version. +const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + +/// A pallet for storing the latest commitment hash from the outbound queue. +/// +/// This pallet provides a simple way to track the most recent commitment hash, +/// which can be included in BEEFY MMR leaves for cross-chain verification. +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::storage] + #[pallet::getter(fn latest_commitment)] + pub type LatestCommitment = StorageValue<_, H256, OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + CommitmentStored { hash: H256 }, + } +} + +impl Pallet { + pub fn store_commitment(commitment: H256) { + LatestCommitment::::put(commitment); + + Self::deposit_event(Event::CommitmentStored { hash: commitment }); + } + + pub fn get_latest_commitment() -> Option { + LatestCommitment::::get() + } +} diff --git a/operator/runtime/Cargo.toml b/operator/runtime/Cargo.toml index db1441d3..cf71f061 100644 --- a/operator/runtime/Cargo.toml +++ b/operator/runtime/Cargo.toml @@ -93,6 +93,7 @@ sp-version = { features = ["serde"], workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +pallet-outbound-commitment-store = { workspace = true } [build-dependencies] substrate-wasm-builder = { optional = true, workspace = true, default-features = true } @@ -171,6 +172,7 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "pallet-outbound-commitment-store/std", ] runtime-benchmarks = [ @@ -206,6 +208,7 @@ runtime-benchmarks = [ "snowbridge-pallet-outbound-queue-v2/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "snowbridge-pallet-system/runtime-benchmarks", + "pallet-outbound-commitment-store/runtime-benchmarks", ] try-runtime = [ @@ -243,6 +246,7 @@ try-runtime = [ "snowbridge-pallet-outbound-queue-v2/try-runtime", "sp-runtime/try-runtime", "snowbridge-pallet-system/try-runtime", + "pallet-outbound-commitment-store/try-runtime", ] fast-runtime = [ diff --git a/operator/runtime/src/configs/mod.rs b/operator/runtime/src/configs/mod.rs index 2e0bc284..005a5dad 100644 --- a/operator/runtime/src/configs/mod.rs +++ b/operator/runtime/src/configs/mod.rs @@ -28,10 +28,10 @@ mod runtime_params; use super::{ deposit, AccountId, Babe, Balance, Balances, BeefyMmrLeaf, Block, BlockNumber, EthereumBeaconClient, EvmChainId, Hash, Historical, ImOnline, MessageQueue, Nonce, Offences, - OriginCaller, OutboundQueueV2, PalletInfo, Preimage, Runtime, RuntimeCall, RuntimeEvent, - RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask, Session, SessionKeys, - Signature, System, Timestamp, ValidatorSet, EXISTENTIAL_DEPOSIT, SLOT_DURATION, - STORAGE_BYTE_FEE, SUPPLY_FACTOR, UNIT, VERSION, + OriginCaller, OutboundCommitmentStore, OutboundQueueV2, PalletInfo, Preimage, Runtime, + RuntimeCall, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, RuntimeOrigin, RuntimeTask, + Session, SessionKeys, Signature, System, Timestamp, ValidatorSet, EXISTENTIAL_DEPOSIT, + SLOT_DURATION, STORAGE_BYTE_FEE, SUPPLY_FACTOR, UNIT, VERSION, }; use codec::{Decode, Encode}; use datahaven_runtime_common::{ @@ -78,6 +78,7 @@ use snowbridge_outbound_queue_primitives::{ v2::ConstantGasMeter, SendError, SendMessageFeeProvider, }; +use snowbridge_pallet_outbound_queue_v2::OnNewCommitment; use snowbridge_pallet_system::BalanceOf; use sp_consensus_beefy::{ ecdsa_crypto::AuthorityId as BeefyId, @@ -356,7 +357,7 @@ pub struct LeafExtraDataProvider; impl BeefyDataProvider for LeafExtraDataProvider { fn extra_data() -> LeafExtraData { LeafExtraData { - extra: H256::zero(), + extra: OutboundCommitmentStore::get_latest_commitment().unwrap_or_default(), } } } @@ -805,6 +806,13 @@ parameter_types! { pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; } +pub struct CommitmentHandler; +impl OnNewCommitment for CommitmentHandler { + fn on_new_commitment(commitment: H256) { + OutboundCommitmentStore::store_commitment(commitment); + } +} + impl snowbridge_pallet_outbound_queue_v2::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Hashing = Keccak256; @@ -813,7 +821,7 @@ impl snowbridge_pallet_outbound_queue_v2::Config for Runtime { type Balance = Balance; type MaxMessagePayloadSize = ConstU32<2048>; type MaxMessagesPerBlock = ConstU32<32>; - type OnNewCommitment = (); + type OnNewCommitment = CommitmentHandler; type WeightToFee = IdentityFee; type WeightInfo = (); type Verifier = EthereumBeaconClient; @@ -860,3 +868,7 @@ pub mod benchmark_helpers { } } } + +impl pallet_outbound_commitment_store::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} diff --git a/operator/runtime/src/lib.rs b/operator/runtime/src/lib.rs index 6117b170..a91b41fe 100644 --- a/operator/runtime/src/lib.rs +++ b/operator/runtime/src/lib.rs @@ -347,5 +347,7 @@ mod runtime { // ╔═══════════════════ DataHaven-specific Pallets ══════════════════╗ // Start with index 100 + #[runtime::pallet_index(100)] + pub type OutboundCommitmentStore = pallet_outbound_commitment_store; // ╚═══════════════════ DataHaven-specific Pallets ══════════════════╝ }