From cde0f753c23fd321e39be8cd94ef606e2851d747 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Fri, 14 Nov 2025 07:22:59 -0500 Subject: [PATCH] Correct Serai header on genesis Includes a couple misc fixes for the RPC as well. --- substrate/abi/src/block.rs | 28 +++++++--- substrate/client/serai/Cargo.toml | 2 +- substrate/client/serai/src/lib.rs | 3 +- substrate/client/serai/src/validator_sets.rs | 6 +-- substrate/core/src/iumt.rs | 4 +- substrate/core/src/lib.rs | 50 +++++++++++++++--- substrate/node/Cargo.toml | 1 + substrate/node/src/chain_spec.rs | 54 +++++++++++++++++++- substrate/node/src/rpc/validator_sets.rs | 30 +++++------ substrate/node/src/service.rs | 17 ++++-- substrate/runtime/src/wasm/mod.rs | 9 +--- 11 files changed, 148 insertions(+), 56 deletions(-) diff --git a/substrate/abi/src/block.rs b/substrate/abi/src/block.rs index 559b4e8f..4e523f8f 100644 --- a/substrate/abi/src/block.rs +++ b/substrate/abi/src/block.rs @@ -7,15 +7,26 @@ use crate::{ Transaction, }; +/// The tag for a block's header, forming a leaf of the Merkle tree which is `builds_upon`. +pub const BLOCK_HEADER_LEAF_TAG: u8 = 0; +/// The tag for branch hashes in `builds_upon`. +pub const BLOCK_HEADER_BRANCH_TAG: u8 = 1; + +/// The tag for a transaction, forming a leaf of the Merkle tree which is the transactions' +/// commitment. +pub const TRANSACTION_COMMITMENT_LEAF_TAG: u8 = 2; +/// The tag for branch hashes in the transactions' commitment. +pub const TRANSACTION_COMMITMENT_BRANCH_TAG: u8 = 3; + /// The tag for the hash of a transaction's event, forming a leaf of the Merkle tree of its events. -pub const TRANSACTION_EVENTS_COMMITMENT_LEAF_TAG: u8 = 0; -/// The tag for the branch hashes of transaction events. -pub const TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG: u8 = 1; +pub const TRANSACTION_EVENTS_COMMITMENT_LEAF_TAG: u8 = 4; +/// The tag for the branch hashes of the Merkle tree for a transaction's events. +pub const TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG: u8 = 5; /// The tag for the hash of a transaction's hash and its events' Merkle root, forming a leaf of the -/// Merkle tree which is the events commitment. -pub const EVENTS_COMMITMENT_LEAF_TAG: u8 = 2; -/// The tag for for the branch hashes of the Merkle tree which is the events commitments. -pub const EVENTS_COMMITMENT_BRANCH_TAG: u8 = 3; +/// Merkle tree which is the events' commitment. +pub const EVENTS_COMMITMENT_LEAF_TAG: u8 = 6; +/// The tag for branch hashes in the events' commitment. +pub const EVENTS_COMMITMENT_BRANCH_TAG: u8 = 7; /// A V1 header for a block. #[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)] @@ -38,7 +49,8 @@ pub struct HeaderV1 { pub builds_upon: UnbalancedMerkleTree, /// The UNIX time in milliseconds this block was created at. pub unix_time_in_millis: u64, - /// The commitment to the transactions within this block. + /// The commitment to the transactions within this block, including the inherent start/end of + /// block transactions. pub transactions_commitment: UnbalancedMerkleTree, /// The commitment to the events within this block. /// diff --git a/substrate/client/serai/Cargo.toml b/substrate/client/serai/Cargo.toml index d2ff002b..f4036fd5 100644 --- a/substrate/client/serai/Cargo.toml +++ b/substrate/client/serai/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/serai-dex/serai/tree/develop/substrate/client/s authors = ["Luke Parker "] keywords = ["serai"] edition = "2021" -rust-version = "1.85" +rust-version = "1.89" [package.metadata.docs.rs] all-features = true diff --git a/substrate/client/serai/src/lib.rs b/substrate/client/serai/src/lib.rs index 6add4c60..da19cefe 100644 --- a/substrate/client/serai/src/lib.rs +++ b/substrate/client/serai/src/lib.rs @@ -153,8 +153,7 @@ impl Serai { /// Fetch a block from the Serai blockchain by its number. pub async fn block_by_number(&self, block: u64) -> Result { - Self::block_internal(self.call("blockchain/block", &format!(r#"{{ "block": "{block}" }}"#))) - .await + Self::block_internal(self.call("blockchain/block", &format!(r#"{{ "block": {block} }}"#))).await } /// Scope this RPC client to the state as of a specific block. diff --git a/substrate/client/serai/src/validator_sets.rs b/substrate/client/serai/src/validator_sets.rs index cf7fd5e2..54de8d45 100644 --- a/substrate/client/serai/src/validator_sets.rs +++ b/substrate/client/serai/src/validator_sets.rs @@ -100,7 +100,7 @@ impl<'a> ValidatorSets<'a> { .0 .call::>( "validator-sets/session", - &format!(r#" "network": {} "#, rpc_network(network)?), + &format!(r#", "network": {} "#, rpc_network(network)?), ) .await? .map(Session), @@ -114,7 +114,7 @@ impl<'a> ValidatorSets<'a> { .0 .call::>( "validator-sets/current_stake", - &format!(r#" "network": {} "#, rpc_network(network)?), + &format!(r#", "network": {} "#, rpc_network(network)?), ) .await? .map(Amount), @@ -128,7 +128,7 @@ impl<'a> ValidatorSets<'a> { .call::>( "validator-sets/keys", &format!( - r#" "set": {{ "network": {}, "session": {} }} "#, + r#", "set": {{ "network": {}, "session": {} }} "#, rpc_network(set.network)?, set.session.0 ), diff --git a/substrate/core/src/iumt.rs b/substrate/core/src/iumt.rs index 6dc42e12..7b2184b5 100644 --- a/substrate/core/src/iumt.rs +++ b/substrate/core/src/iumt.rs @@ -8,8 +8,8 @@ use serai_abi::primitives::merkle::{UnbalancedMerkleTree, IncrementalUnbalancedM /// `IncrementalUnbalancedMerkleTree`. pub struct IncrementalUnbalancedMerkleTree< T: frame_support::StorageValue>, - const BRANCH_TAG: u8 = 1, - const LEAF_TAG: u8 = 0, + const BRANCH_TAG: u8, + const LEAF_TAG: u8, >(PhantomData); impl< T: frame_support::StorageValue>, diff --git a/substrate/core/src/lib.rs b/substrate/core/src/lib.rs index 40c2ad2c..ed948568 100644 --- a/substrate/core/src/lib.rs +++ b/substrate/core/src/lib.rs @@ -4,9 +4,10 @@ #![cfg_attr(not(feature = "std"), no_std)] use core::marker::PhantomData; - extern crate alloc; +use frame_support::traits::{PreInherents, PostTransactions}; + mod iumt; pub use iumt::*; @@ -15,7 +16,10 @@ pub use iumt::*; pub mod pallet { use alloc::vec::Vec; - use frame_support::pallet_prelude::*; + use frame_support::{ + sp_runtime::traits::{Header, Block}, + pallet_prelude::*, + }; use serai_abi::primitives::{prelude::*, merkle::IncrementalUnbalancedMerkleTree as Iumt}; @@ -28,14 +32,21 @@ pub mod pallet { #[pallet::storage] #[pallet::unbounded] pub(super) type BlocksCommitment = StorageValue<_, Iumt, OptionQuery>; - pub(super) type BlocksCommitmentMerkle = IncrementalUnbalancedMerkleTree>; + pub(super) type BlocksCommitmentMerkle = IncrementalUnbalancedMerkleTree< + BlocksCommitment, + { serai_abi::BLOCK_HEADER_BRANCH_TAG }, + { serai_abi::BLOCK_HEADER_LEAF_TAG }, + >; /// The Merkle tree of all transactions within the current block. #[pallet::storage] #[pallet::unbounded] pub(super) type BlockTransactionsCommitment = StorageValue<_, Iumt, OptionQuery>; - pub(super) type BlockTransactionsCommitmentMerkle = - IncrementalUnbalancedMerkleTree>; + pub(super) type BlockTransactionsCommitmentMerkle = IncrementalUnbalancedMerkleTree< + BlockTransactionsCommitment, + { serai_abi::TRANSACTION_COMMITMENT_BRANCH_TAG }, + { serai_abi::TRANSACTION_COMMITMENT_LEAF_TAG }, + >; /// The hashes of events caused by the current transaction. #[pallet::storage] @@ -99,10 +110,16 @@ pub mod pallet { } /// The code to run on genesis. - pub fn genesis() { + pub fn genesis(config: &impl frame_support::traits::BuildGenesisConfig) { BlocksCommitmentMerkle::::new_expecting_none(); BlockTransactionsCommitmentMerkle::::new_expecting_none(); BlockEventsCommitmentMerkle::::new_expecting_none(); + + Self::start_transaction(); + <_>::build(config); + Self::end_transaction([0; 32]); + + EndOfBlock::::post_transactions(); } /// The code to run when beginning execution of a transaction. @@ -150,7 +167,7 @@ pub use pallet::*; /// The code to run at the start of a block for this pallet. pub struct StartOfBlock(PhantomData); -impl frame_support::traits::PreInherents for StartOfBlock { +impl PreInherents for StartOfBlock { fn pre_inherents() { use frame_support::pallet_prelude::Zero; // `Pallet::genesis` is expected to be used for the genesis block @@ -163,13 +180,30 @@ impl frame_support::traits::PreInherents for StartOfBlock { BlockTransactionsCommitmentMerkle::::new_expecting_none(); BlockEventsCommitmentMerkle::::new_expecting_none(); + + Pallet::::start_transaction(); + + // Other modules' `PreInherents` + + let block_number: sp_core::U256 = frame_system::Pallet::::block_number().into(); + let start_of_block_transaction_hash = block_number.to_big_endian(); + Pallet::::end_transaction(start_of_block_transaction_hash); } } /// The code to run at the end of a block for this pallet. pub struct EndOfBlock(PhantomData); -impl frame_support::traits::PostTransactions for EndOfBlock { +impl PostTransactions for EndOfBlock { fn post_transactions() { + Pallet::::start_transaction(); + + // Other modules' `PostInherents` + + let block_number: sp_core::U256 = frame_system::Pallet::::block_number().into(); + let mut end_of_block_transaction_hash = block_number.to_big_endian(); + end_of_block_transaction_hash[.. 16].copy_from_slice(&[0xff; 16]); + Pallet::::end_transaction(end_of_block_transaction_hash); + use serai_abi::SeraiExecutionDigest; frame_system::Pallet::::deposit_log( frame_support::sp_runtime::generic::DigestItem::Consensus( diff --git a/substrate/node/Cargo.toml b/substrate/node/Cargo.toml index 7b21b1e4..8d2f73b1 100644 --- a/substrate/node/Cargo.toml +++ b/substrate/node/Cargo.toml @@ -62,6 +62,7 @@ jsonrpsee = { version = "0.24", features = ["server"] } sc-transaction-pool = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" } sc-transaction-pool-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" } sc-basic-authorship = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" } +sc-client-db = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" } sc-executor = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" } sc-service = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" } sc-client-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "71fa60b900c8ad068f1b9ce8100508506377fbf5" } diff --git a/substrate/node/src/chain_spec.rs b/substrate/node/src/chain_spec.rs index 94c882c7..9506b4ad 100644 --- a/substrate/node/src/chain_spec.rs +++ b/substrate/node/src/chain_spec.rs @@ -1,7 +1,15 @@ use core::marker::PhantomData; +use std::sync::Arc; -use sp_core::Pair as PairTrait; - +use sp_core::{Decode, storage::Storage, Pair as PairTrait}; +use sp_runtime::{ + traits::{Block as _, Header as _}, + BuildStorage, +}; +use sc_client_db::Backend; +use sc_executor::RuntimeVersionOf; +use sc_chain_spec::{BuildGenesisBlock, GenesisBlockBuilder, ChainSpec as ChainSpecTrait}; +use sc_client_api::BlockImportOperation; use sc_service::ChainType; use rand_core::OsRng; @@ -201,3 +209,45 @@ pub fn bootnode_multiaddrs(id: &str) -> Vec { _ => panic!("unrecognized network ID"), } } + +struct GenesisBlock( + GenesisBlockBuilder, Executor>, + sp_runtime::Digest, +); +impl BuildGenesisBlock for GenesisBlock { + type BlockImportOperation = sc_client_db::BlockImportOperation; + + fn build_genesis_block(self) -> sp_blockchain::Result<(Block, Self::BlockImportOperation)> { + let (genesis_block, op) = self.0.build_genesis_block()?; + + let mut header = genesis_block.header().clone(); + *header.digest_mut() = self.1; + let genesis_block = Block::new(header, genesis_block.extrinsics().to_vec()); + + Ok((genesis_block, op)) + } +} + +pub(super) fn genesis_block( + chain_spec: &dyn ChainSpecTrait, + backend: Arc>, + executor: impl RuntimeVersionOf, +) -> Result< + impl BuildGenesisBlock>, + sc_service::error::Error, +> { + let storage = chain_spec.as_storage_builder().build_storage()?; + let digest = { + let digest_key = [sp_core::twox_128(b"System"), sp_core::twox_128(b"Digest")].concat(); + sp_runtime::Digest::decode( + &mut storage.top.get(&digest_key).expect("System Digest not set").as_slice(), + ) + .expect("failed to decode System Digest") + }; + + let commit_genesis_state = true; + Ok(GenesisBlock( + GenesisBlockBuilder::new_with_storage(storage, commit_genesis_state, backend, executor)?, + digest, + )) +} diff --git a/substrate/node/src/rpc/validator_sets.rs b/substrate/node/src/rpc/validator_sets.rs index b3f9569d..a14c7c05 100644 --- a/substrate/node/src/rpc/validator_sets.rs +++ b/substrate/node/src/rpc/validator_sets.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, ops::Deref, collections::HashSet}; +use std::{sync::Arc, ops::Deref, convert::AsRef, collections::HashSet}; use rand_core::{RngCore, OsRng}; @@ -17,6 +17,16 @@ use jsonrpsee::RpcModule; use super::utils::{Error, block_hash}; +fn network_from_str(network: impl AsRef) -> Result { + Ok(match network.as_ref().to_lowercase().as_str() { + "serai" => NetworkId::Serai, + "bitcoin" => NetworkId::External(ExternalNetworkId::Bitcoin), + "ethereum" => NetworkId::External(ExternalNetworkId::Ethereum), + "monero" => NetworkId::External(ExternalNetworkId::Monero), + _ => Err(Error::InvalidRequest("unrecognized network requested"))?, + }) +} + pub(super) fn network(params: &jsonrpsee::types::params::Params) -> Result { #[derive(sp_core::serde::Deserialize)] #[serde(crate = "sp_core::serde")] @@ -28,13 +38,7 @@ pub(super) fn network(params: &jsonrpsee::types::params::Params) -> Result NetworkId::Serai, - "bitcoin" => NetworkId::External(ExternalNetworkId::Bitcoin), - "ethereum" => NetworkId::External(ExternalNetworkId::Ethereum), - "monero" => NetworkId::External(ExternalNetworkId::Monero), - _ => Err(Error::InvalidRequest("unrecognized network requested"))?, - }) + network_from_str(network.network) } pub(super) fn set(params: &jsonrpsee::types::params::Params) -> Result { @@ -49,15 +53,7 @@ pub(super) fn set(params: &jsonrpsee::types::params::Params) -> Result NetworkId::Serai, - "bitcoin" => NetworkId::External(ExternalNetworkId::Bitcoin), - "ethereum" => NetworkId::External(ExternalNetworkId::Ethereum), - "monero" => NetworkId::External(ExternalNetworkId::Monero), - _ => Err(Error::InvalidRequest("unrecognized network requested"))?, - }; - - Ok(ValidatorSet { network, session: Session(set.session) }) + Ok(ValidatorSet { network: network_from_str(set.network)?, session: Session(set.session) }) } pub(crate) fn module< diff --git a/substrate/node/src/service.rs b/substrate/node/src/service.rs index bf9a3af4..66058390 100644 --- a/substrate/node/src/service.rs +++ b/substrate/node/src/service.rs @@ -95,12 +95,19 @@ pub fn new_partial( config.executor.runtime_cache_size, ); - let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts::( + let (client, backend, keystore_container, task_manager) = { + let telemetry = telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()); + let backend = sc_service::new_db_backend(config.db_config())?; + + sc_service::new_full_parts_with_genesis_builder::( config, - telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), - executor, - )?; + telemetry, + executor.clone(), + backend.clone(), + super::chain_spec::genesis_block(&*config.chain_spec, backend, executor)?, + false, + )? + }; let client = Arc::new(client); let keystore: Arc = diff --git a/substrate/runtime/src/wasm/mod.rs b/substrate/runtime/src/wasm/mod.rs index 976e99e7..6627f3f8 100644 --- a/substrate/runtime/src/wasm/mod.rs +++ b/substrate/runtime/src/wasm/mod.rs @@ -356,14 +356,7 @@ sp_api::impl_runtime_apis! { grandpa: GrandpaConfig { authorities: vec![], _config: PhantomData }, }; - Core::genesis(); - Core::start_transaction(); - ::build(&config); - Core::end_transaction([0; 32]); - - < - serai_core_pallet::EndOfBlock as frame_support::traits::PostTransactions - >::post_transactions(); + Core::genesis(&config); } }