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 ══════════════════╝ }