From 9eebf6f0c5f16668dc8327ea004d2b278c3f2858 Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Wed, 22 Oct 2025 10:32:26 +0100 Subject: [PATCH 1/6] some config changes --- Cargo.lock | 12 ++++ Cargo.toml | 5 +- parachain/node/Cargo.toml | 2 + parachain/node/src/chain_spec.rs | 2 +- parachain/node/src/command.rs | 12 ++-- parachain/node/src/rpc.rs | 2 +- parachain/node/src/runtime_api.rs | 4 +- parachain/node/src/service.rs | 2 +- parachain/runtimes/gargantua/Cargo.toml | 3 +- parachain/runtimes/gargantua/src/lib.rs | 79 ++++++++++++++++++++++++- parachain/runtimes/messier/Cargo.toml | 2 + parachain/runtimes/messier/src/lib.rs | 6 ++ 12 files changed, 116 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05e386c99..f89a1586c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9416,6 +9416,7 @@ dependencies = [ "ismp-parachain-runtime-api", "jsonrpsee 0.24.9", "log", + "messier-runtime", "mmr-gadget 29.0.1", "nexus-runtime", "pallet-ismp", @@ -11841,6 +11842,7 @@ dependencies = [ "pallet-ismp-host-executive", "pallet-ismp-relayer", "pallet-ismp-runtime-api", + "pallet-log-emitter", "pallet-mmr-runtime-api", "pallet-mmr-tree", "pallet-revive-ismp-dispatcher", @@ -14896,6 +14898,16 @@ dependencies = [ "zstd-safe 7.2.4", ] +[[package]] +name = "pallet-log-emitter" +version = "0.1.0" +dependencies = [ + "hex", + "parity-scale-codec", + "polkadot-sdk", + "scale-info", +] + [[package]] name = "pallet-lottery" version = "41.0.0" diff --git a/Cargo.toml b/Cargo.toml index 870ed8559..9f1b7cc01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,7 +107,7 @@ members = [ # Airdrop - "modules/pallets/bridge-drop", + "modules/pallets/bridge-drop", "modules/pallets/pallet-log-emitter", ] # Config for 'cargo dist' @@ -257,6 +257,8 @@ pallet-state-coprocessor = { path = "modules/pallets/state-coprocessor", default pallet-token-gateway-inspector = { path = "modules/pallets/token-gateway-inspector", default-features = false } pallet-bridge-airdrop = { path = "modules/pallets/bridge-drop", default-features = false } +pallet-log-emitter = { path = "modules/pallets/pallet-log-emitter", default-features = false } + # merkle trees ethereum-triedb = { version = "0.1.1", path = "./modules/trees/ethereum", default-features = false } pallet-mmr-tree = { path = "modules/pallets/mmr", default-features = false } @@ -268,6 +270,7 @@ mmr-gadget = { path = "modules/pallets/mmr/gadget" } # runtimes gargantua-runtime = { path = "./parachain/runtimes/gargantua", default-features = false } nexus-runtime = { path = "./parachain/runtimes/nexus", default-features = false } +messier-runtime = { path = "./parachain/runtimes/messier", default-features = false } # tesseract tesseract-primitives = { path = "tesseract/messaging/primitives" } diff --git a/parachain/node/Cargo.toml b/parachain/node/Cargo.toml index b1639cc38..9b43afb2e 100644 --- a/parachain/node/Cargo.toml +++ b/parachain/node/Cargo.toml @@ -24,6 +24,7 @@ json = { workspace = true, default-features = true } # local gargantua-runtime = { workspace = true, default-features = true } nexus-runtime = { workspace = true, default-features = true } +messier-runtime = { workspace = true, default-features = true } pallet-ismp-rpc = { workspace = true } pallet-mmr-rpc = { workspace = true } pallet-ismp-runtime-api = { workspace = true } @@ -95,6 +96,7 @@ features = [ "cumulus-client-consensus-proposer", "cumulus-client-collator", "pallet-transaction-payment", + "pallet-revive-eth-rpc" ] diff --git a/parachain/node/src/chain_spec.rs b/parachain/node/src/chain_spec.rs index ba4bb1fd2..2b2028804 100644 --- a/parachain/node/src/chain_spec.rs +++ b/parachain/node/src/chain_spec.rs @@ -15,7 +15,7 @@ use codec::Encode; use cumulus_primitives_core::ParaId; -use gargantua_runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT, MICROUNIT}; +use messier_runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT, MICROUNIT}; use ismp_parachain::ParachainData; use polkadot_sdk::*; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; diff --git a/parachain/node/src/command.rs b/parachain/node/src/command.rs index 12b9c087f..671e1170f 100644 --- a/parachain/node/src/command.rs +++ b/parachain/node/src/command.rs @@ -19,7 +19,7 @@ use std::{borrow::Cow, str::FromStr}; use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; use cumulus_primitives_core::ParaId; use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; -use gargantua_runtime::Block; +use messier_runtime::Block; use log::info; use polkadot_cli::service; use sc_cli::{ @@ -38,7 +38,7 @@ use crate::{ fn load_spec(id: &str) -> std::result::Result, String> { Ok(match id { "dev" | "gargantua" => Box::new(chain_spec::ChainSpec::from_json_bytes( - include_bytes!("../../chainspec/gargantua.paseo.json").to_vec(), + include_bytes!("../../chainspec/messier.json").to_vec(), )?), "" | "nexus" => Box::new(chain_spec::ChainSpec::from_json_bytes( include_bytes!("../../chainspec/nexus.json").to_vec(), @@ -145,7 +145,7 @@ macro_rules! construct_async_run { chain if chain.contains("gargantua") || chain.contains("dev") => { runner.async_run(|$config| { let executor = sc_service::new_wasm_executor::(&$config.executor); - let $components = new_partial::(&$config, executor)?; + let $components = new_partial::(&$config, executor)?; Ok::<_, sc_cli::Error>(( { $( $code )* }, $components.task_manager)) }) } @@ -230,7 +230,7 @@ pub fn run() -> Result<()> { match config.chain_spec.id() { chain if chain.contains("gargantua") || chain.contains("dev") => { let components = - new_partial::(&config, executor)?; + new_partial::(&config, executor)?; cmd.run(components.client.clone()) }, @@ -273,7 +273,7 @@ pub fn run() -> Result<()> { match config.chain_spec.id() { chain if chain.contains("gargantua") || chain.contains("dev") => { let components = - new_partial::(&config, executor)?; + new_partial::(&config, executor)?; cmd.run(components.client) }, chain if chain.contains("nexus") => { @@ -298,7 +298,7 @@ pub fn run() -> Result<()> { match config.chain_spec.id() { chain if chain.contains("gargantua") || chain.contains("dev") => { let components = - new_partial::(&config, executor)?; + new_partial::(&config, executor)?; let db = components.backend.expose_db(); let storage = components.backend.expose_storage(); let shared_trie_cache = components.backend.expose_shared_trie_cache(); diff --git a/parachain/node/src/rpc.rs b/parachain/node/src/rpc.rs index a8cbb2575..a06e017a9 100644 --- a/parachain/node/src/rpc.rs +++ b/parachain/node/src/rpc.rs @@ -23,7 +23,7 @@ use polkadot_sdk::*; use std::sync::Arc; -use gargantua_runtime::{opaque::Block, AccountId, Balance, Index as Nonce}; +use messier_runtime::{opaque::Block, AccountId, Balance, Index as Nonce}; use crate::runtime_api::opaque; use sc_client_api::{AuxStore, BlockBackend, ProofProvider}; diff --git a/parachain/node/src/runtime_api.rs b/parachain/node/src/runtime_api.rs index 337297f04..fbfb7dee0 100644 --- a/parachain/node/src/runtime_api.rs +++ b/parachain/node/src/runtime_api.rs @@ -91,7 +91,7 @@ pub trait BaseHostRuntimeApis: pallet_ismp::offchain::Leaf, > + simnode_runtime_api::CreateTransactionApi< opaque::Block, - gargantua_runtime::RuntimeCall, + messier_runtime::RuntimeCall, opaque::AccountId, > { @@ -118,7 +118,7 @@ impl BaseHostRuntimeApis for Api where pallet_ismp::offchain::Leaf, > + simnode_runtime_api::CreateTransactionApi< opaque::Block, - gargantua_runtime::RuntimeCall, + messier_runtime::RuntimeCall, opaque::AccountId, > { diff --git a/parachain/node/src/service.rs b/parachain/node/src/service.rs index d37be8064..2ad44c29d 100644 --- a/parachain/node/src/service.rs +++ b/parachain/node/src/service.rs @@ -515,7 +515,7 @@ pub async fn start_parachain_node( ) -> sc_service::error::Result { match parachain_config.chain_spec.id() { chain if chain.contains("gargantua") => - start_node_impl::( + start_node_impl::( parachain_config, polkadot_config, collator_options, diff --git a/parachain/runtimes/gargantua/Cargo.toml b/parachain/runtimes/gargantua/Cargo.toml index 95507728e..e3df04abd 100644 --- a/parachain/runtimes/gargantua/Cargo.toml +++ b/parachain/runtimes/gargantua/Cargo.toml @@ -106,7 +106,8 @@ features = [ "pallet-collator-selection", "staging-parachain-info", "parachains-common", - "pallet-vesting" + "pallet-vesting", + "pallet-revive", ] [features] diff --git a/parachain/runtimes/gargantua/src/lib.rs b/parachain/runtimes/gargantua/src/lib.rs index 68b21ab3a..9a18b327f 100644 --- a/parachain/runtimes/gargantua/src/lib.rs +++ b/parachain/runtimes/gargantua/src/lib.rs @@ -85,6 +85,7 @@ pub use sp_runtime::{MultiAddress, Perbill, Permill}; #[cfg(feature = "runtime-benchmarks")] use staging_xcm::latest::Location; use xcm::XcmOriginToTransactDispatchOrigin; +use pallet_revive::evm::runtime::EthExtra; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -107,6 +108,7 @@ use staging_xcm::latest::prelude::BodyId; use pallet_collective::PrimeDefaultVote; #[cfg(feature = "runtime-benchmarks")] use pallet_treasury::ArgumentsFactory; +use polkadot_sdk::frame_system::EnsureSigned; use pallet_ismp::offchain::{Leaf, ProofKeys}; use sp_core::{crypto::AccountId32, Get}; @@ -150,6 +152,19 @@ pub type SignedBlock = generic::SignedBlock; /// BlockId type as expected by this runtime. pub type BlockId = generic::BlockId; +/// The extension to the basic transaction logic. +pub type TxExtension = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, + frame_metadata_hash_extension::CheckMetadataHash, +); + /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( frame_system::CheckNonZeroSender, @@ -163,12 +178,48 @@ pub type SignedExtra = ( frame_metadata_hash_extension::CheckMetadataHash, ); +/// Default extensions applied to Ethereum transactions. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct EthExtraImpl; + +impl EthExtra for EthExtraImpl { + type Config = Runtime; + type Extension = TxExtension; + + fn get_eth_extension(nonce: u32, tip: Balance) -> Self::Extension { + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::from(generic::Era::Immortal), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + frame_metadata_hash_extension::CheckMetadataHash::::new(false), + ) + } +} + + /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; +pallet_revive::evm::runtime::UncheckedExtrinsic; /// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type CheckedExtrinsic = generic::CheckedExtrinsic; + +// This impl is no longer necessary post stable-2506. +impl TryFrom for pallet_revive::Call { + type Error = (); + + fn try_from(value: RuntimeCall) -> Result { + match value { + RuntimeCall::Revive(call) => Ok(call), + _ => Err(()), + } + } +} /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -712,6 +763,30 @@ impl pallet_vesting::Config for Runtime { type BlockNumberProvider = System; } +impl pallet_revive::Config for Runtime { + type Time = Timestamp; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type DepositPerItem = DepositPerItem; + type DepositPerByte = DepositPerByte; + type WeightPrice = pallet_transaction_payment::Pallet; + type WeightInfo = (); + type Precompiles = (); + type AddressMapper = pallet_revive::AccountId32Mapper; + type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; + type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; + type UnsafeUnstableInterface = ConstBool; + type UploadOrigin = EnsureSigned; + type InstantiateOrigin = EnsureSigned; + type RuntimeHoldReason = RuntimeHoldReason; + type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; + type ChainId = ConstU64<420_420_420>; + type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12. + type EthGasEncoder = (); + type FindAuthor = ::FindAuthor; +} + // Create the runtime by composing the FRAME pallets that were previously configured. #[frame_support::runtime] mod runtime { diff --git a/parachain/runtimes/messier/Cargo.toml b/parachain/runtimes/messier/Cargo.toml index 9f5e1460a..64efecb6b 100644 --- a/parachain/runtimes/messier/Cargo.toml +++ b/parachain/runtimes/messier/Cargo.toml @@ -53,6 +53,7 @@ ismp-arbitrum = { workspace = true } ismp-optimism = { workspace = true } pallet-revive-ismp-dispatcher = { workspace = true } pallet-hyperbridge = { workspace = true } +pallet-log-emitter = { workspace = true } [dependencies.polkadot-sdk] workspace = true @@ -151,6 +152,7 @@ std = [ "ismp-arbitrum/std", "ismp-optimism/std", "pallet-revive-ismp-dispatcher/std", + "pallet-log-emitter/std", "pallet-hyperbridge/std", "alloy-primitives/std", ] diff --git a/parachain/runtimes/messier/src/lib.rs b/parachain/runtimes/messier/src/lib.rs index 30603542d..bad3af564 100644 --- a/parachain/runtimes/messier/src/lib.rs +++ b/parachain/runtimes/messier/src/lib.rs @@ -756,6 +756,9 @@ impl pallet_revive::Config for Runtime { type FindAuthor = ::FindAuthor; } +impl pallet_log_emitter::Config for Runtime { +} + // Create the runtime by composing the FRAME pallets that were previously configured. #[frame_support::runtime] mod runtime { @@ -871,6 +874,9 @@ mod runtime { pub type IsmpArbitrum = ismp_arbitrum::pallet; #[runtime::pallet_index(84)] pub type IsmpOptimism = ismp_optimism::pallet; + + #[runtime::pallet_index(85)] + pub type LogEmitter = pallet_log_emitter; // consensus clients #[runtime::pallet_index(255)] pub type IsmpGrandpa = ismp_grandpa; From 6c18aa4c556ba92644774093c72eeecbf4e3969f Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Wed, 22 Oct 2025 12:00:45 +0100 Subject: [PATCH 2/6] pallet revive on gargantua --- Cargo.lock | 2 +- parachain/node/Cargo.toml | 2 - parachain/node/src/chain_spec.rs | 2 +- parachain/node/src/command.rs | 12 +-- parachain/node/src/rpc.rs | 2 +- parachain/node/src/runtime_api.rs | 4 +- parachain/node/src/service.rs | 2 +- parachain/runtimes/gargantua/Cargo.toml | 4 +- parachain/runtimes/gargantua/src/lib.rs | 98 +++++++++++++++---------- 9 files changed, 75 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f89a1586c..50f4a7d79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8177,6 +8177,7 @@ dependencies = [ "pallet-ismp-host-executive", "pallet-ismp-relayer", "pallet-ismp-runtime-api", + "pallet-log-emitter", "pallet-mmr-runtime-api", "pallet-mmr-tree", "pallet-state-coprocessor", @@ -9416,7 +9417,6 @@ dependencies = [ "ismp-parachain-runtime-api", "jsonrpsee 0.24.9", "log", - "messier-runtime", "mmr-gadget 29.0.1", "nexus-runtime", "pallet-ismp", diff --git a/parachain/node/Cargo.toml b/parachain/node/Cargo.toml index 9b43afb2e..b1639cc38 100644 --- a/parachain/node/Cargo.toml +++ b/parachain/node/Cargo.toml @@ -24,7 +24,6 @@ json = { workspace = true, default-features = true } # local gargantua-runtime = { workspace = true, default-features = true } nexus-runtime = { workspace = true, default-features = true } -messier-runtime = { workspace = true, default-features = true } pallet-ismp-rpc = { workspace = true } pallet-mmr-rpc = { workspace = true } pallet-ismp-runtime-api = { workspace = true } @@ -96,7 +95,6 @@ features = [ "cumulus-client-consensus-proposer", "cumulus-client-collator", "pallet-transaction-payment", - "pallet-revive-eth-rpc" ] diff --git a/parachain/node/src/chain_spec.rs b/parachain/node/src/chain_spec.rs index 2b2028804..ba4bb1fd2 100644 --- a/parachain/node/src/chain_spec.rs +++ b/parachain/node/src/chain_spec.rs @@ -15,7 +15,7 @@ use codec::Encode; use cumulus_primitives_core::ParaId; -use messier_runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT, MICROUNIT}; +use gargantua_runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT, MICROUNIT}; use ismp_parachain::ParachainData; use polkadot_sdk::*; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; diff --git a/parachain/node/src/command.rs b/parachain/node/src/command.rs index 671e1170f..12b9c087f 100644 --- a/parachain/node/src/command.rs +++ b/parachain/node/src/command.rs @@ -19,7 +19,7 @@ use std::{borrow::Cow, str::FromStr}; use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; use cumulus_primitives_core::ParaId; use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE}; -use messier_runtime::Block; +use gargantua_runtime::Block; use log::info; use polkadot_cli::service; use sc_cli::{ @@ -38,7 +38,7 @@ use crate::{ fn load_spec(id: &str) -> std::result::Result, String> { Ok(match id { "dev" | "gargantua" => Box::new(chain_spec::ChainSpec::from_json_bytes( - include_bytes!("../../chainspec/messier.json").to_vec(), + include_bytes!("../../chainspec/gargantua.paseo.json").to_vec(), )?), "" | "nexus" => Box::new(chain_spec::ChainSpec::from_json_bytes( include_bytes!("../../chainspec/nexus.json").to_vec(), @@ -145,7 +145,7 @@ macro_rules! construct_async_run { chain if chain.contains("gargantua") || chain.contains("dev") => { runner.async_run(|$config| { let executor = sc_service::new_wasm_executor::(&$config.executor); - let $components = new_partial::(&$config, executor)?; + let $components = new_partial::(&$config, executor)?; Ok::<_, sc_cli::Error>(( { $( $code )* }, $components.task_manager)) }) } @@ -230,7 +230,7 @@ pub fn run() -> Result<()> { match config.chain_spec.id() { chain if chain.contains("gargantua") || chain.contains("dev") => { let components = - new_partial::(&config, executor)?; + new_partial::(&config, executor)?; cmd.run(components.client.clone()) }, @@ -273,7 +273,7 @@ pub fn run() -> Result<()> { match config.chain_spec.id() { chain if chain.contains("gargantua") || chain.contains("dev") => { let components = - new_partial::(&config, executor)?; + new_partial::(&config, executor)?; cmd.run(components.client) }, chain if chain.contains("nexus") => { @@ -298,7 +298,7 @@ pub fn run() -> Result<()> { match config.chain_spec.id() { chain if chain.contains("gargantua") || chain.contains("dev") => { let components = - new_partial::(&config, executor)?; + new_partial::(&config, executor)?; let db = components.backend.expose_db(); let storage = components.backend.expose_storage(); let shared_trie_cache = components.backend.expose_shared_trie_cache(); diff --git a/parachain/node/src/rpc.rs b/parachain/node/src/rpc.rs index a06e017a9..a8cbb2575 100644 --- a/parachain/node/src/rpc.rs +++ b/parachain/node/src/rpc.rs @@ -23,7 +23,7 @@ use polkadot_sdk::*; use std::sync::Arc; -use messier_runtime::{opaque::Block, AccountId, Balance, Index as Nonce}; +use gargantua_runtime::{opaque::Block, AccountId, Balance, Index as Nonce}; use crate::runtime_api::opaque; use sc_client_api::{AuxStore, BlockBackend, ProofProvider}; diff --git a/parachain/node/src/runtime_api.rs b/parachain/node/src/runtime_api.rs index fbfb7dee0..337297f04 100644 --- a/parachain/node/src/runtime_api.rs +++ b/parachain/node/src/runtime_api.rs @@ -91,7 +91,7 @@ pub trait BaseHostRuntimeApis: pallet_ismp::offchain::Leaf, > + simnode_runtime_api::CreateTransactionApi< opaque::Block, - messier_runtime::RuntimeCall, + gargantua_runtime::RuntimeCall, opaque::AccountId, > { @@ -118,7 +118,7 @@ impl BaseHostRuntimeApis for Api where pallet_ismp::offchain::Leaf, > + simnode_runtime_api::CreateTransactionApi< opaque::Block, - messier_runtime::RuntimeCall, + gargantua_runtime::RuntimeCall, opaque::AccountId, > { diff --git a/parachain/node/src/service.rs b/parachain/node/src/service.rs index 2ad44c29d..d37be8064 100644 --- a/parachain/node/src/service.rs +++ b/parachain/node/src/service.rs @@ -515,7 +515,7 @@ pub async fn start_parachain_node( ) -> sc_service::error::Result { match parachain_config.chain_spec.id() { chain if chain.contains("gargantua") => - start_node_impl::( + start_node_impl::( parachain_config, polkadot_config, collator_options, diff --git a/parachain/runtimes/gargantua/Cargo.toml b/parachain/runtimes/gargantua/Cargo.toml index e3df04abd..559199894 100644 --- a/parachain/runtimes/gargantua/Cargo.toml +++ b/parachain/runtimes/gargantua/Cargo.toml @@ -48,6 +48,7 @@ pallet-token-gateway = { workspace = true } pallet-bridge-airdrop = { workspace = true } ismp-arbitrum = { workspace = true } ismp-optimism = { workspace = true } +pallet-log-emitter = { workspace = true } [dependencies.polkadot-sdk] workspace = true @@ -144,7 +145,8 @@ std = [ "frame-benchmarking/std", "pallet-bridge-airdrop/std", "ismp-arbitrum/std", - "ismp-optimism/std" + "ismp-optimism/std", + "pallet-log-emitter/std" ] runtime-benchmarks = [ "hex-literal", diff --git a/parachain/runtimes/gargantua/src/lib.rs b/parachain/runtimes/gargantua/src/lib.rs index 9a18b327f..69524a925 100644 --- a/parachain/runtimes/gargantua/src/lib.rs +++ b/parachain/runtimes/gargantua/src/lib.rs @@ -131,6 +131,9 @@ pub type Balance = u128; /// Index of a transaction in the chain. pub type Index = u32; +/// Type alias for Nonce +pub type Nonce = Index; + /// A hash of some data used by the chain. pub type Hash = sp_core::H256; @@ -762,6 +765,11 @@ impl pallet_vesting::Config for Runtime { type UnvestedFundsAllowedWithdrawReasons = UnvestedFundsAllowedWithdrawReasons; type BlockNumberProvider = System; } +parameter_types! { + pub const DepositPerItem: Balance = EXISTENTIAL_DEPOSIT; + pub const DepositPerByte: Balance = EXISTENTIAL_DEPOSIT; + pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(30); +} impl pallet_revive::Config for Runtime { type Time = Timestamp; @@ -787,6 +795,9 @@ impl pallet_revive::Config for Runtime { type FindAuthor = ::FindAuthor; } +impl pallet_log_emitter::Config for Runtime { +} + // Create the runtime by composing the FRAME pallets that were previously configured. #[frame_support::runtime] mod runtime { @@ -900,6 +911,11 @@ mod runtime { pub type IsmpArbitrum = ismp_arbitrum::pallet; #[runtime::pallet_index(84)] pub type IsmpOptimism = ismp_optimism::pallet; + + #[runtime::pallet_index(85)] + pub type Revive = pallet_revive; + #[runtime::pallet_index(86)] + pub type LogEmitter = pallet_log_emitter; // consensus clients #[runtime::pallet_index(255)] pub type IsmpGrandpa = ismp_grandpa; @@ -937,10 +953,14 @@ mod benches { ); } -impl_runtime_apis! { + +pallet_revive::impl_runtime_apis_plus_revive!( + Runtime, + Executive, + EthExtraImpl, impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) } fn authorities() -> Vec { @@ -1084,6 +1104,21 @@ impl_runtime_apis! { TransactionPayment::length_to_fee(length) } } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> sp_genesis_builder::Result { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, crate::genesis_config::get_preset) + } + + fn preset_names() -> Vec { + crate::genesis_config::preset_names() + } + } + impl pallet_mmr_runtime_api::MmrRuntimeApi::Hash, BlockNumber, Leaf> for Runtime { /// Return Block number where pallet-mmr was added to the runtime fn pallet_genesis() -> Result, sp_mmr_primitives::Error> { @@ -1114,19 +1149,21 @@ impl_runtime_apis! { impl pallet_ismp_runtime_api::IsmpRuntimeApi::Hash> for Runtime { fn host_state_machine() -> StateMachine { - ::HostStateMachine::get() + crate::ismp::HostStateMachine::get() } fn challenge_period(state_machine_id: StateMachineId) -> Option { Ismp::challenge_period(state_machine_id) } - /// Fetch all ISMP events in the block, should only be called from runtime-api. + + + /// Fetch all ISMP events fn block_events() -> Vec<::ismp::events::Event> { Ismp::block_events() } - /// Fetch all ISMP events and their extrinsic metadata, should only be called from runtime-api. + /// Fetch all ISMP events and their extrinsic metadata fn block_events_with_metadata() -> Vec<(::ismp::events::Event, Option)> { Ismp::block_events_with_metadata() } @@ -1146,7 +1183,6 @@ impl_runtime_apis! { Ismp::latest_state_machine_height(id) } - /// Get actual requests fn requests(commitments: Vec) -> Vec { Ismp::requests(commitments) @@ -1213,27 +1249,27 @@ impl_runtime_apis! { fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig - ) -> Result, alloc::string::String> { + ) -> Result, sp_runtime::RuntimeString> { use frame_benchmarking::{Benchmarking, BenchmarkBatch}; + use frame_support::traits::TrackedStorageKey; use frame_system_benchmarking::Pallet as SystemBench; - - impl frame_system_benchmarking::Config for Runtime { - fn setup_set_code_requirements(code: &sp_std::vec::Vec) -> Result<(), frame_benchmarking::BenchmarkError> { - ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); - Ok(()) - } - - fn verify_set_code() { - System::assert_last_event(cumulus_pallet_parachain_system::Event::::ValidationFunctionStored.into()); - } - } - - use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + + impl frame_system_benchmarking::Config for Runtime {} impl cumulus_pallet_session_benchmarking::Config for Runtime {} - use frame_support::traits::WhitelistedStorageKeys; - let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), + // Total Issuance + hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(), + ]; let mut batches = Vec::::new(); let params = (&config, &*whitelist); @@ -1244,20 +1280,6 @@ impl_runtime_apis! { } } - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(config: Vec) -> sp_genesis_builder::Result { - build_state::(config) - } - - fn get_preset(id: &Option) -> Option> { - get_preset::(id, crate::genesis_config::get_preset) - } - - fn preset_names() -> Vec { - crate::genesis_config::preset_names() - } - } - impl simnode_runtime_api::CreateTransactionApi for Runtime where RuntimeCall: codec::Codec, @@ -1287,7 +1309,7 @@ impl_runtime_apis! { ); let signature = MultiSignature::from(sr25519::Signature::default()); let address = sp_runtime::traits::AccountIdLookup::unlookup(account.into()); - let ext = generic::UncheckedExtrinsic::::new_signed( + let ext = generic::UncheckedExtrinsic::::new_signed( call, address, signature, @@ -1297,7 +1319,7 @@ impl_runtime_apis! { } } -} +); cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, From 62f078fd1ef07ba4c2193dff59ecd3b79bccf284 Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Thu, 23 Oct 2025 12:54:25 +0100 Subject: [PATCH 3/6] some changes --- Cargo.lock | 2 -- Cargo.toml | 4 ++++ parachain/runtimes/gargantua/src/lib.rs | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 50f4a7d79..78af36265 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15444,8 +15444,6 @@ dependencies = [ [[package]] name = "pallet-revive" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474840408264f98eea7f187839ff2157f83a92bec6f3f3503dbf855c38f4de6b" dependencies = [ "alloy-core", "derive_more 0.99.20", diff --git a/Cargo.toml b/Cargo.toml index 9f1b7cc01..87d7d1a86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -346,3 +346,7 @@ features = ["derive"] [workspace.dependencies.reconnecting-jsonrpsee-ws-client] version = "0.5.0" default-features = false + + +[patch.crates-io] +pallet-revive = { path = "/Users/dharjeezy/Documents/polytope/hyperbridge/vendor/pallet-revive" } diff --git a/parachain/runtimes/gargantua/src/lib.rs b/parachain/runtimes/gargantua/src/lib.rs index 69524a925..8a6f5d42f 100644 --- a/parachain/runtimes/gargantua/src/lib.rs +++ b/parachain/runtimes/gargantua/src/lib.rs @@ -793,6 +793,10 @@ impl pallet_revive::Config for Runtime { type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12. type EthGasEncoder = (); type FindAuthor = ::FindAuthor; + + type InherentHandlers = ( + pallet_log_emitter::pallet, + ); } impl pallet_log_emitter::Config for Runtime { From f167947bfb60af0d6ae647364857fe726b8808ae Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Tue, 28 Oct 2025 11:21:20 +0100 Subject: [PATCH 4/6] implement hyperbridge parachain inherent and inherent pallet handler --- Cargo.lock | 31 ++ Cargo.toml | 5 +- .../hyperbridge-parachain/client/Cargo.toml | 56 ++++ .../hyperbridge-parachain/client/src/lib.rs | 309 ++++++++++++++++++ .../hyperbridge-parachain/inherent/Cargo.toml | 31 ++ .../hyperbridge-parachain/inherent/src/lib.rs | 123 +++++++ parachain/runtimes/gargantua/src/lib.rs | 3 +- 7 files changed, 555 insertions(+), 3 deletions(-) create mode 100644 modules/ismp/clients/hyperbridge-parachain/client/Cargo.toml create mode 100644 modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs create mode 100644 modules/ismp/clients/hyperbridge-parachain/inherent/Cargo.toml create mode 100644 modules/ismp/clients/hyperbridge-parachain/inherent/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 78af36265..529ce282c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9448,6 +9448,37 @@ dependencies = [ "substrate-state-machine", ] +[[package]] +name = "hyperbridge-ismp-parachain" +version = "0.1.0" +dependencies = [ + "hex", + "hex-literal 0.4.1", + "ismp", + "log", + "pallet-ismp", + "parity-scale-codec", + "polkadot-sdk", + "primitive-types 0.13.1", + "scale-info", + "serde", + "substrate-state-machine", +] + +[[package]] +name = "hyperbridge-ismp-parachain-inherent" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "hyperbridge-ismp-parachain", + "ismp", + "ismp-parachain", + "log", + "parity-scale-codec", + "polkadot-sdk", +] + [[package]] name = "hyperclient" version = "0.6.0" diff --git a/Cargo.toml b/Cargo.toml index 87d7d1a86..5b3b127dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,7 +107,7 @@ members = [ # Airdrop - "modules/pallets/bridge-drop", "modules/pallets/pallet-log-emitter", + "modules/pallets/bridge-drop", "modules/pallets/pallet-log-emitter", "modules/ismp/clients/hyperbridge-parachain/client", "modules/ismp/clients/hyperbridge-parachain/inherent", ] # Config for 'cargo dist' @@ -239,6 +239,7 @@ arbitrum-verifier = { path = "./modules/ismp/clients/arbitrum", default-features op-verifier = { path = "./modules/ismp/clients/optimism", default-features = false } ismp-arbitrum = { path = "modules/ismp/clients/ismp-arbitrum", default-features = false } ismp-optimism = { path = "modules/ismp/clients/ismp-optimism", default-features = false } +hyperbridge-ismp-parachain = { path = "modules/ismp/clients/hyperbridge-parachain/client", default-features = false } # state machine clients evm-state-machine = { path = "./modules/ismp/state-machines/evm", default-features = false } @@ -349,4 +350,4 @@ default-features = false [patch.crates-io] -pallet-revive = { path = "/Users/dharjeezy/Documents/polytope/hyperbridge/vendor/pallet-revive" } +pallet-revive = { path = "vendor/pallet-revive" } diff --git a/modules/ismp/clients/hyperbridge-parachain/client/Cargo.toml b/modules/ismp/clients/hyperbridge-parachain/client/Cargo.toml new file mode 100644 index 000000000..69d1bcc38 --- /dev/null +++ b/modules/ismp/clients/hyperbridge-parachain/client/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "hyperbridge-ismp-parachain" +version = "0.1.0" +edition = "2024" +authors = ["Polytope Labs "] +license = "Apache-2.0" + +[dependencies] +# crates.io +serde = { workspace = true, features = ["derive"], optional = false } +codec = { workspace = true, features = ["derive"] } +scale-info = { workspace = true, features = ["derive"] } +hex-literal = { workspace = true } +hex = { workspace = true } +primitive-types = { workspace = true } +log = { workspace = true } + +# local +substrate-state-machine = { workspace = true } +ismp = { workspace = true } +pallet-ismp = { workspace = true } + +[dependencies.polkadot-sdk] +workspace = true +features = [ + "frame-support", + "frame-system", + "sp-trie", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-consensus-aura", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "pallet-revive" +] + +[features] +default = ["std"] +std = [ + "codec/std", + "polkadot-sdk/std", + "scale-info/std", + "ismp/std", + "log/std", + "primitive-types/std", + "pallet-ismp/std", + "substrate-state-machine/std", +] +try-runtime = ["polkadot-sdk/try-runtime"] +runtime-benchmarks = [ + "polkadot-sdk/frame-benchmarking", + "polkadot-sdk/runtime-benchmarks", + "pallet-ismp/runtime-benchmarks", +] + diff --git a/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs b/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs new file mode 100644 index 000000000..52d775ce8 --- /dev/null +++ b/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs @@ -0,0 +1,309 @@ +// Copyright (C) Polytope Labs Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +use polkadot_sdk::*; +pub use pallet::*; + +extern crate alloc; +use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, DecodeWithMemTracking}; +use cumulus_pallet_parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; +use frame_support::{pallet_prelude::*, traits::Get}; +use frame_system::pallet_prelude::*; +use pallet_revive::{inherent_handlers::InherentHandler, H160, H256}; +use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; +use sp_core::ConstU32; +use sp_io::hashing::{keccak_256, twox_64}; +use sp_runtime::{ + app_crypto::sp_core::storage::StorageKey, + generic::Header, + traits::{BlakeTwo256, Block as BlockT, Header as HeaderT}, + DigestItem, DispatchError, DispatchResult, +}; +use sp_std::time::Duration; +use sp_trie::StorageProof; +use substrate_state_machine::read_proof_check_for_parachain; + +/// Hyperbridge's Inherent identifier +pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"hypbridg"; + +/// The `ConsensusEngineId` of ISMP `ConsensusDigest` in the parachain header. +pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; + +/// Consensus log digest for pallet ismp +#[derive(Encode, Decode, Clone, scale_info::TypeInfo, Default)] +pub struct ConsensusDigest { + /// Mmr root hash + pub mmr_root: H256, + /// Child trie root hash + pub child_trie_root: H256, +} + +/// Proof for proving finality +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, DecodeWithMemTracking)] +pub struct HyperbridgeConsensusProof { + /// Height of the relay chain for the given proof + pub relay_height: u32, + /// Storage proof for the parachain headers + pub storage_proof: Vec>, +} + +/// Commitment to Hyperbridge State Machine at a given height +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, Default, MaxEncodedLen)] +pub struct StateCommitment { + /// Timestamp in seconds + pub timestamp: u64, + /// Root hash of the request/response overlay trie if the state machine supports it. + pub overlay_root: Option, + /// Root hash of the global state trie. + pub state_root: H256, +} + +/// Identifies a state commitment at a given height +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, Default, MaxEncodedLen)] +pub struct StateCommitmentHeight { + /// The state machine identifier + pub commitment: StateCommitment, + /// The corresponding block height + pub height: u64, +} + +#[polkadot_sdk::frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: polkadot_sdk::frame_system::Config + pallet_revive::Config + cumulus_pallet_parachain_system::Config + { + /// The specific parachain ID for Hyperbridge that this pallet verifies. + #[pallet::constant] + type HyperbridgeParaId: Get; + + /// The expected slot duration for Hyperbridge blocks. Needed for timestamp calculation. + #[pallet::constant] + type HyperbridgeSlotDuration: Get; + + /// The ISMP Host contract address for Hyperbridge + #[pallet::constant] + type IsmpHostContractAddress: Get; + } + + /// Stores the latest verified StateCommitmentHeight for Hyperbridge. + #[pallet::storage] + #[pallet::getter(fn hyperbridge_state_commitment_height)] + pub type HyperbridgeStateCommitmentHeight = StorageValue<_, StateCommitmentHeight, OptionQuery>; + + /// Tracks whether the inherent has run for the current block. + #[pallet::storage] + #[pallet::getter(fn inherent_processed)] + pub type InherentProcessed = StorageValue<_, bool, ValueQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub fn deposit_event)] + pub enum Event { + HyperbridgeProofVerified { commitment: StateCommitmentHeight }, + } + + #[pallet::error] + pub enum Error { + InherentAlreadyProcessed, + StorageProofVerificationFailed, + HyperbridgeHeaderNotFound, + HeaderDecodingFailed, + DigestExtractionError, + TimestampNotFound, + HandlerProofDecodingFailed, + } + + #[pallet::call] + impl Pallet { + /// Inherent extrinsic to submit and verify the Hyperbridge proof. + #[pallet::call_index(0)] + #[pallet::weight(Weight::MAX)] + pub fn submit_hyperbridge_proof( + origin: OriginFor, + proof: HyperbridgeConsensusProof, + ) -> DispatchResultWithPostInfo { + ensure_none(origin)?; + ensure!(!InherentProcessed::::get(), Error::::InherentAlreadyProcessed); + + let commitment = Self::verify_store_and_extract(&proof)?; + + Self::deposit_event(Event::HyperbridgeProofVerified { + commitment, + }); + + InherentProcessed::::put(true); + Ok(Pays::No.into()) + } + } + + #[pallet::inherent] + impl ProvideInherent for Pallet { + type Call = Call; + type Error = sp_inherents::MakeFatalError<()>; + const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; + + fn create_inherent(data: &InherentData) -> Option { + if InherentProcessed::::get() { return None; } + let proof_data: HyperbridgeConsensusProof = + data.get_data(&Self::INHERENT_IDENTIFIER).ok().flatten()?; + Some(Call::submit_hyperbridge_proof { proof: proof_data }) + } + + fn is_inherent(call: &Self::Call) -> bool { + matches!(call, Call::submit_hyperbridge_proof { .. }) && !InherentProcessed::::get() + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: BlockNumberFor) -> Weight { + InherentProcessed::::put(false); + Weight::zero() + } + } + + impl InherentHandler for Pallet { + fn handler_name() -> &'static [u8] { + b"hyperbridge_proof_verifier_v1" + } + + fn handle_message(message: Vec) -> DispatchResult { + let proof = HyperbridgeConsensusProof::decode(&mut &message[..]) + .map_err(|_| Error::::HandlerProofDecodingFailed)?; + + let final_commitment = Self::verify_store_and_extract(&proof)?; + + let ismp_host_address = T::IsmpHostContractAddress::get(); + let topic_0_hash = keccak_256(b"StateMachineUpdated(bytes32,bytes)"); + let topic_0: H256 = H256::from(topic_0_hash); + + let topic_1_hash = keccak_256(b"hyperbridge_state_machine"); + let topic_1: H256 = H256::from(topic_1_hash); + let topics = vec![topic_0, topic_1]; + + let data = final_commitment.encode(); + + >::deposit_event( + ::RuntimeEvent::from( + pallet_revive::Event::ContractEmitted { + contract: ismp_host_address, + topics, + data, + }, + ), + ); + + Ok(()) + } + } +} + +impl Pallet { + /// Verifies proof for Hyperbridge, extracts digest info, stores the commitment height. + fn verify_store_and_extract( + proof: &HyperbridgeConsensusProof, + ) -> Result { + let state = RelaychainDataProvider::::current_relay_chain_state(); + let relay_root = state.state_root; + + let storage_proof = StorageProof::new(proof.storage_proof.clone()); + let hyperbridge_para_id = T::HyperbridgeParaId::get(); + let header_key = parachain_header_storage_key(hyperbridge_para_id).0; + let keys_to_prove = alloc::vec![header_key]; + + let read_result = read_proof_check_for_parachain::( + &relay_root, + storage_proof, + keys_to_prove.iter().cloned(), + ) + .map_err(|e| { + log::error!(target: "runtime::hyperbridge-verifier", "Storage proof verification failed: {:?}", e); + Error::::StorageProofVerificationFailed + })?; + + let header_bytes = read_result + .get(&keys_to_prove[0]) + .cloned() + .flatten() + .ok_or(Error::::HyperbridgeHeaderNotFound)?; + + let decoded_vec: Vec = Decode::decode(&mut &header_bytes[..]) + .map_err(|_| Error::::HeaderDecodingFailed)?; + let header = Header::::decode(&mut &decoded_vec[..]) + .map_err(|_| Error::::HeaderDecodingFailed)?; + + let mut timestamp: u64 = 0; + let mut overlay_root: H256 = H256::default(); + let slot_duration_ms = T::HyperbridgeSlotDuration::get(); + + for digest in header.digest().logs.iter() { + match digest { + DigestItem::PreRuntime(consensus_engine_id, value) + if *consensus_engine_id == AURA_ENGINE_ID => + { + if let Ok(slot) = Slot::decode(&mut &value[..]) { + timestamp = Duration::from_millis(*slot * slot_duration_ms).as_secs(); + } else { + log::warn!(target: "ismp::hyperbridge-verifier", "Failed to decode Aura slot"); + return Err(Error::::DigestExtractionError.into()); + } + }, + DigestItem::Consensus(consensus_engine_id, value) + if *consensus_engine_id == ISMP_ID => + { + if let Ok(log) = ConsensusDigest::decode(&mut &value[..]) { + overlay_root = log.child_trie_root; + } else { + log::warn!(target: "ismp::hyperbridge-verifier", "Invalid ISMP digest found"); + } + }, + _ => {} + }; + } + + ensure!(timestamp > 0, Error::::TimestampNotFound); + + let block_height: u64 = (*header.number()).into(); + + let commitment_height = + StateCommitmentHeight { + commitment: StateCommitment { + timestamp, + overlay_root: Some(overlay_root), + state_root: *header.state_root(), + }, + height: block_height, + }; + HyperbridgeStateCommitmentHeight::::put(commitment_height.clone()); + + Ok(commitment_height) + } +} + +pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { + let mut storage_key = storage::storage_prefix(b"Paras", b"Heads").to_vec(); + let encoded_para_id = para_id.encode(); + storage_key.extend_from_slice(twox_64(&encoded_para_id).as_slice()); + storage_key.extend_from_slice(&encoded_para_id); + StorageKey(storage_key) +} + diff --git a/modules/ismp/clients/hyperbridge-parachain/inherent/Cargo.toml b/modules/ismp/clients/hyperbridge-parachain/inherent/Cargo.toml new file mode 100644 index 000000000..994d5755f --- /dev/null +++ b/modules/ismp/clients/hyperbridge-parachain/inherent/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "hyperbridge-ismp-parachain-inherent" +version = "0.1.0" +edition = "2024" +authors = ["Polytope Labs "] +license = "Apache-2.0" + + +[dependencies] +# crates.io +async-trait = { version = "0.1.63" } +codec = { workspace = true, features = ["derive"], default-features = true } +anyhow = { workspace = true } +log = { workspace = true } + +# local +ismp = { workspace = true, default-features = true } +ismp-parachain = { workspace = true, default-features = true } +hyperbridge-ismp-parachain = { workspace = true, default-features = true } + +[dependencies.polkadot-sdk] +workspace = true +default-features = true +features = [ + "sp-inherents", + "sp-api", + "sp-blockchain", + "sp-runtime", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", +] \ No newline at end of file diff --git a/modules/ismp/clients/hyperbridge-parachain/inherent/src/lib.rs b/modules/ismp/clients/hyperbridge-parachain/inherent/src/lib.rs new file mode 100644 index 000000000..f42b41f75 --- /dev/null +++ b/modules/ismp/clients/hyperbridge-parachain/inherent/src/lib.rs @@ -0,0 +1,123 @@ +use anyhow::anyhow; +use cumulus_relay_chain_interface::RelayChainInterface; +use hyperbridge_ismp_parachain::{parachain_header_storage_key, HyperbridgeConsensusProof, INHERENT_IDENTIFIER}; +use polkadot_sdk::*; +use sp_api::{ApiExt, ProvideRuntimeApi}; +use sp_blockchain::HeaderBackend; +use sp_inherents::{Error, InherentData, InherentDataProvider, InherentIdentifier}; +use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Hash as HashT, Header as HeaderT}}; +use std::sync::Arc; + +sp_api::decl_runtime_apis! { + /// Hyperbridge Parachain consensus client runtime APIs + pub trait HyperbridgeVerifierApi { + /// Return the hyperbridge para id + fn hyperbridge_para_id() -> u32; + /// Return the current relay chain state. + fn current_relay_chain_state() -> Option; + } +} + +/// Provides the `HyperbridgeConsensusProof` inherent data. +pub struct HyperbridgeInherentProvider { + relay_chain_interface: Arc, + consensus_proof: Option, +} + +impl HyperbridgeInherentProvider { + /// Creates the inherent provider and fetches the necessary proof data for Hyperbridge. + pub async fn create( + parent_hash: B::Hash, + client: Arc, + relay_chain_interface: Arc, + ) -> Result + where + B: BlockT, + B::Hash: HashT, + C: ProvideRuntimeApi + HeaderBackend + Send + Sync + 'static, + C::Api: HyperbridgeVerifierApi, + ::Hash: HashT + From, + { + if !client.runtime_api().has_api::>(parent_hash)? { + log::trace!("HyperbridgeVerifierApi not implemented by runtime"); + return Ok(Self { relay_chain_interface, consensus_proof: None }); + } + + let hyperbridge_para_id = client.runtime_api().hyperbridge_para_id(parent_hash)?; + log::trace!("Target Hyperbridge ParaId from runtime: {}", hyperbridge_para_id); + + let maybe_relay_state = client + .runtime_api() + .current_relay_chain_state(parent_hash) + .map_err(|e| anyhow!("Failed to get current relay chain state: {:?}", e))?; + + let relay_state = match maybe_relay_state { + Some(state) => state, + None => { + log::warn!("Runtime not providing relay chain state via API."); + return Ok(Self { relay_chain_interface, consensus_proof: None }); + } + }; + + let relay_height = relay_state.number; + log::trace!("Current relay chain height from runtime API: {}", relay_height); + + if relay_height == 0 { + return Ok(Self { relay_chain_interface, consensus_proof: None }); + } + + let relay_header = match relay_chain_interface.header(BlockId::Number(relay_height)).await { + Ok(Some(header)) => header, + _ => { + log::trace!("Relay chain header not available for height {}", relay_height); + return Ok(Self { relay_chain_interface, consensus_proof: None }); + } + }; + let relay_hash = relay_header.hash(); + + let header_key = parachain_header_storage_key(hyperbridge_para_id).0; + let keys_to_prove = vec![header_key]; + + let storage_proof = match relay_chain_interface.prove_read(relay_hash, &keys_to_prove).await { + Ok(proof) => proof.into_iter_nodes().collect(), + Err(e) => { + log::error!("Failed to get storage proof from relay chain for height {}: {:?}", relay_height, e); + return Ok(Self { relay_chain_interface, consensus_proof: None }); + } + }; + + let proof = HyperbridgeConsensusProof { + relay_height, + storage_proof, + }; + + log::trace!("Successfully created Hyperbridge consensus proof for relay height {}", relay_height); + Ok(Self { relay_chain_interface, consensus_proof: Some(proof) }) + } +} + +#[async_trait::async_trait] +impl InherentDataProvider for HyperbridgeInherentProvider { + async fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), Error> { + if let Some(ref proof) = self.consensus_proof { + inherent_data.put_data(INHERENT_IDENTIFIER, proof)?; + log::trace!("Provided inherent data for {}", String::from_utf8_lossy(&INHERENT_IDENTIFIER)); + } + Ok(()) + } + + async fn try_handle_error(&self, identifier: &InherentIdentifier, error: &[u8]) -> Option> { + if *identifier == INHERENT_IDENTIFIER { + log::error!( + target: "hyperbridge-inherent", + "Inherent error for {}: {}", + String::from_utf8_lossy(identifier), + String::from_utf8_lossy(error) + ); + None + } else { + None + } + } +} + diff --git a/parachain/runtimes/gargantua/src/lib.rs b/parachain/runtimes/gargantua/src/lib.rs index 8a6f5d42f..1fea14a12 100644 --- a/parachain/runtimes/gargantua/src/lib.rs +++ b/parachain/runtimes/gargantua/src/lib.rs @@ -86,6 +86,7 @@ pub use sp_runtime::{MultiAddress, Perbill, Permill}; use staging_xcm::latest::Location; use xcm::XcmOriginToTransactDispatchOrigin; use pallet_revive::evm::runtime::EthExtra; +use pallet_revive::inherent_handlers::{InherentHandler, InherentHandlers}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -795,7 +796,7 @@ impl pallet_revive::Config for Runtime { type FindAuthor = ::FindAuthor; type InherentHandlers = ( - pallet_log_emitter::pallet, + pallet_log_emitter::Pallet, ); } From de64e10c6ddce64690ce0008b2e901dcf2c3f397 Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Wed, 29 Oct 2025 17:56:28 +0100 Subject: [PATCH 5/6] use time --- .../hyperbridge-parachain/client/src/lib.rs | 124 +++++++++++------- 1 file changed, 74 insertions(+), 50 deletions(-) diff --git a/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs b/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs index 52d775ce8..9c9777820 100644 --- a/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs +++ b/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs @@ -15,24 +15,24 @@ #![cfg_attr(not(feature = "std"), no_std)] -use polkadot_sdk::*; pub use pallet::*; +use polkadot_sdk::*; extern crate alloc; use alloc::{vec, vec::Vec}; -use codec::{Decode, Encode, DecodeWithMemTracking}; +use codec::{Decode, DecodeWithMemTracking, Encode}; use cumulus_pallet_parachain_system::{RelaychainDataProvider, RelaychainStateProvider}; use frame_support::{pallet_prelude::*, traits::Get}; use frame_system::pallet_prelude::*; -use pallet_revive::{inherent_handlers::InherentHandler, H160, H256}; -use sp_consensus_aura::{Slot, AURA_ENGINE_ID}; +use pallet_revive::{H160, H256, inherent_handlers::InherentHandler}; +use sp_consensus_aura::{AURA_ENGINE_ID, Slot}; use sp_core::ConstU32; use sp_io::hashing::{keccak_256, twox_64}; use sp_runtime::{ + DigestItem, DispatchError, DispatchResult, app_crypto::sp_core::storage::StorageKey, generic::Header, traits::{BlakeTwo256, Block as BlockT, Header as HeaderT}, - DigestItem, DispatchError, DispatchResult, }; use sp_std::time::Duration; use sp_trie::StorageProof; @@ -44,6 +44,15 @@ pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"hypbridg"; /// The `ConsensusEngineId` of ISMP `ConsensusDigest` in the parachain header. pub const ISMP_ID: sp_runtime::ConsensusEngineId = *b"ISMP"; +pub const ISMP_TIMESTAMP_ID: sp_runtime::ConsensusEngineId = *b"ISTM"; + +/// Timestamp log digest for pallet ismp +#[derive(Encode, Decode, Clone, scale_info::TypeInfo, Default)] +pub struct TimestampDigest { + /// Timestamp value in seconds + pub timestamp: u64, +} + /// Consensus log digest for pallet ismp #[derive(Encode, Decode, Clone, scale_info::TypeInfo, Default)] pub struct ConsensusDigest { @@ -63,7 +72,18 @@ pub struct HyperbridgeConsensusProof { } /// Commitment to Hyperbridge State Machine at a given height -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, Default, MaxEncodedLen)] +#[derive( + Debug, + Encode, + Decode, + Clone, + PartialEq, + Eq, + DecodeWithMemTracking, + TypeInfo, + Default, + MaxEncodedLen, +)] pub struct StateCommitment { /// Timestamp in seconds pub timestamp: u64, @@ -74,7 +94,18 @@ pub struct StateCommitment { } /// Identifies a state commitment at a given height -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, DecodeWithMemTracking, TypeInfo, Default, MaxEncodedLen)] +#[derive( + Debug, + Encode, + Decode, + Clone, + PartialEq, + Eq, + DecodeWithMemTracking, + TypeInfo, + Default, + MaxEncodedLen, +)] pub struct StateCommitmentHeight { /// The state machine identifier pub commitment: StateCommitment, @@ -90,16 +121,15 @@ pub mod pallet { pub struct Pallet(_); #[pallet::config] - pub trait Config: polkadot_sdk::frame_system::Config + pallet_revive::Config + cumulus_pallet_parachain_system::Config + pub trait Config: + polkadot_sdk::frame_system::Config + + pallet_revive::Config + + cumulus_pallet_parachain_system::Config { /// The specific parachain ID for Hyperbridge that this pallet verifies. #[pallet::constant] type HyperbridgeParaId: Get; - /// The expected slot duration for Hyperbridge blocks. Needed for timestamp calculation. - #[pallet::constant] - type HyperbridgeSlotDuration: Get; - /// The ISMP Host contract address for Hyperbridge #[pallet::constant] type IsmpHostContractAddress: Get; @@ -108,7 +138,8 @@ pub mod pallet { /// Stores the latest verified StateCommitmentHeight for Hyperbridge. #[pallet::storage] #[pallet::getter(fn hyperbridge_state_commitment_height)] - pub type HyperbridgeStateCommitmentHeight = StorageValue<_, StateCommitmentHeight, OptionQuery>; + pub type HyperbridgeStateCommitmentHeight = + StorageValue<_, StateCommitmentHeight, OptionQuery>; /// Tracks whether the inherent has run for the current block. #[pallet::storage] @@ -146,9 +177,7 @@ pub mod pallet { let commitment = Self::verify_store_and_extract(&proof)?; - Self::deposit_event(Event::HyperbridgeProofVerified { - commitment, - }); + Self::deposit_event(Event::HyperbridgeProofVerified { commitment }); InherentProcessed::::put(true); Ok(Pays::No.into()) @@ -162,7 +191,9 @@ pub mod pallet { const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; fn create_inherent(data: &InherentData) -> Option { - if InherentProcessed::::get() { return None; } + if InherentProcessed::::get() { + return None; + } let proof_data: HyperbridgeConsensusProof = data.get_data(&Self::INHERENT_IDENTIFIER).ok().flatten()?; Some(Call::submit_hyperbridge_proof { proof: proof_data }) @@ -246,37 +277,32 @@ impl Pallet { .flatten() .ok_or(Error::::HyperbridgeHeaderNotFound)?; - let decoded_vec: Vec = Decode::decode(&mut &header_bytes[..]) - .map_err(|_| Error::::HeaderDecodingFailed)?; + let decoded_vec: Vec = + Decode::decode(&mut &header_bytes[..]).map_err(|_| Error::::HeaderDecodingFailed)?; let header = Header::::decode(&mut &decoded_vec[..]) .map_err(|_| Error::::HeaderDecodingFailed)?; let mut timestamp: u64 = 0; let mut overlay_root: H256 = H256::default(); - let slot_duration_ms = T::HyperbridgeSlotDuration::get(); - for digest in header.digest().logs.iter() { match digest { - DigestItem::PreRuntime(consensus_engine_id, value) - if *consensus_engine_id == AURA_ENGINE_ID => - { - if let Ok(slot) = Slot::decode(&mut &value[..]) { - timestamp = Duration::from_millis(*slot * slot_duration_ms).as_secs(); - } else { - log::warn!(target: "ismp::hyperbridge-verifier", "Failed to decode Aura slot"); - return Err(Error::::DigestExtractionError.into()); - } - }, DigestItem::Consensus(consensus_engine_id, value) - if *consensus_engine_id == ISMP_ID => - { - if let Ok(log) = ConsensusDigest::decode(&mut &value[..]) { - overlay_root = log.child_trie_root; - } else { - log::warn!(target: "ismp::hyperbridge-verifier", "Invalid ISMP digest found"); - } - }, - _ => {} + if *consensus_engine_id == ISMP_TIMESTAMP_ID => + { + let timestamp_digest = TimestampDigest::decode(&mut &value[..]) + .map_err(|_| Error::::DigestExtractionError)?; + timestamp = timestamp_digest.timestamp; + }, + DigestItem::Consensus(consensus_engine_id, value) + if *consensus_engine_id == ISMP_ID => + { + if let Ok(log) = ConsensusDigest::decode(&mut &value[..]) { + overlay_root = log.child_trie_root; + } else { + log::warn!(target: "ismp::hyperbridge-verifier", "Invalid ISMP digest found"); + } + }, + _ => {}, }; } @@ -284,15 +310,14 @@ impl Pallet { let block_height: u64 = (*header.number()).into(); - let commitment_height = - StateCommitmentHeight { - commitment: StateCommitment { - timestamp, - overlay_root: Some(overlay_root), - state_root: *header.state_root(), - }, - height: block_height, - }; + let commitment_height = StateCommitmentHeight { + commitment: StateCommitment { + timestamp, + overlay_root: Some(overlay_root), + state_root: *header.state_root(), + }, + height: block_height, + }; HyperbridgeStateCommitmentHeight::::put(commitment_height.clone()); Ok(commitment_height) @@ -306,4 +331,3 @@ pub fn parachain_header_storage_key(para_id: u32) -> StorageKey { storage_key.extend_from_slice(&encoded_para_id); StorageKey(storage_key) } - From 2d17b9dea27fa02da1dea98cfbe72ba66f036b1b Mon Sep 17 00:00:00 2001 From: dharjeezy Date: Tue, 4 Nov 2025 11:31:54 +0100 Subject: [PATCH 6/6] include the precompile for fetching state machine commitments --- .../hyperbridge-parachain/client/Cargo.toml | 5 +- .../client/src/IHyperbridgeVerifier.sol | 14 ++++ .../hyperbridge-parachain/client/src/lib.rs | 6 +- .../client/src/precompile.rs | 81 +++++++++++++++++++ 4 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 modules/ismp/clients/hyperbridge-parachain/client/src/IHyperbridgeVerifier.sol create mode 100644 modules/ismp/clients/hyperbridge-parachain/client/src/precompile.rs diff --git a/modules/ismp/clients/hyperbridge-parachain/client/Cargo.toml b/modules/ismp/clients/hyperbridge-parachain/client/Cargo.toml index 69d1bcc38..cadd982e1 100644 --- a/modules/ismp/clients/hyperbridge-parachain/client/Cargo.toml +++ b/modules/ismp/clients/hyperbridge-parachain/client/Cargo.toml @@ -14,7 +14,8 @@ hex-literal = { workspace = true } hex = { workspace = true } primitive-types = { workspace = true } log = { workspace = true } - +alloy-rlp = { workspace = true } +alloy-sol-types = { workspace = true, default-features = false } # local substrate-state-machine = { workspace = true } ismp = { workspace = true } @@ -46,6 +47,8 @@ std = [ "primitive-types/std", "pallet-ismp/std", "substrate-state-machine/std", + "alloy-rlp/std", + "alloy-sol-types/std", ] try-runtime = ["polkadot-sdk/try-runtime"] runtime-benchmarks = [ diff --git a/modules/ismp/clients/hyperbridge-parachain/client/src/IHyperbridgeVerifier.sol b/modules/ismp/clients/hyperbridge-parachain/client/src/IHyperbridgeVerifier.sol new file mode 100644 index 000000000..8a1b024d4 --- /dev/null +++ b/modules/ismp/clients/hyperbridge-parachain/client/src/IHyperbridgeVerifier.sol @@ -0,0 +1,14 @@ +interface IHyperbridgeVerifier { + struct StateCommitment { + uint64 timestamp; + bytes32 overlayRoot; + bytes32 stateRoot; + } + + struct StateCommitmentHeight { + StateCommitment commitment; + uint64 height; + } + + function latestStateCommitment() external view returns (StateCommitmentHeight memory); +} diff --git a/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs b/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs index 9c9777820..0a7f9b7a8 100644 --- a/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs +++ b/modules/ismp/clients/hyperbridge-parachain/client/src/lib.rs @@ -15,6 +15,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub mod precompile; + pub use pallet::*; use polkadot_sdk::*; @@ -227,9 +229,7 @@ pub mod pallet { let topic_0_hash = keccak_256(b"StateMachineUpdated(bytes32,bytes)"); let topic_0: H256 = H256::from(topic_0_hash); - let topic_1_hash = keccak_256(b"hyperbridge_state_machine"); - let topic_1: H256 = H256::from(topic_1_hash); - let topics = vec![topic_0, topic_1]; + let topics = vec![topic_0]; let data = final_commitment.encode(); diff --git a/modules/ismp/clients/hyperbridge-parachain/client/src/precompile.rs b/modules/ismp/clients/hyperbridge-parachain/client/src/precompile.rs new file mode 100644 index 000000000..a158e43bc --- /dev/null +++ b/modules/ismp/clients/hyperbridge-parachain/client/src/precompile.rs @@ -0,0 +1,81 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use polkadot_sdk::*; +use alloc::vec::Vec; +use core::{marker::PhantomData, num::NonZero}; +use frame_support::{traits::Get, weights::Weight}; +use pallet_revive::precompiles::{ + alloy::{ + primitives::{FixedBytes}, + self, + sol_types::{Revert, SolValue}, + }, + AddressMatcher, Error, Ext, Precompile, +}; + +use crate::{Config as VerifierConfig, Pallet as VerifierPallet}; + +alloy::sol!("src/IHyperbridgeVerifier.sol"); +use IHyperbridgeVerifier::IHyperbridgeVerifierCalls; + +/// Trait that provides weights for the verifier precompile operations. +pub trait VerifierWeightSchedule { + /// Weight for fetching the latest state commitment. + fn latest_state_commitment() -> Weight; +} + +/// [`pallet_revive::precompiles::Precompile`] implementation for the +/// Hyperbridge ismp parachain pallet. +pub struct VerifierPrecompile( + PhantomData<(Runtime, WeightSchedule)>, +); + + +impl Precompile for VerifierPrecompile +where + Runtime: VerifierConfig + pallet_revive::Config, + WeightSchedule: VerifierWeightSchedule, +{ + type T = Runtime; + + const MATCHER: AddressMatcher = AddressMatcher::Fixed(NonZero::new(3368).unwrap()); + + const HAS_CONTRACT_INFO: bool = false; + + type Interface = IHyperbridgeVerifier::IHyperbridgeVerifierCalls; + + fn call( + _address: &[u8; 20], + input: &Self::Interface, + env: &mut impl Ext, + ) -> Result, Error> { + match input { + IHyperbridgeVerifier::IHyperbridgeVerifierCalls::latestStateCommitment( + _call, + ) => { + env.charge(WeightSchedule::latest_state_commitment())?; + + let state = VerifierPallet::::hyperbridge_state_commitment_height() + .ok_or(Error::Revert(Revert { + reason: "No verified state commitment found".into(), + }))?; + + let commitment = IHyperbridgeVerifier::StateCommitment { + timestamp: state.commitment.timestamp, + overlayRoot: FixedBytes(state.commitment.overlay_root.unwrap_or_default().0), + stateRoot: FixedBytes(state.commitment.state_root.0), + }; + + let sol_state = IHyperbridgeVerifier::StateCommitmentHeight { + commitment, + height: state.height, + }; + + Ok(sol_state.abi_encode()) + } + } + } +} +