From 784de57065dce40c25eb085470eda72feabd9afa Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Wed, 25 Oct 2023 23:10:41 +0800 Subject: [PATCH 01/12] integrate hotstuff --- Cargo.lock | 98 ++++++++++++++++ Cargo.toml | 5 + crates/node/Cargo.toml | 4 + crates/node/src/chain_spec.rs | 12 +- crates/node/src/commands/run.rs | 8 +- crates/node/src/service.rs | 201 ++++++++++++++++++++++---------- crates/runtime/Cargo.toml | 9 ++ crates/runtime/src/lib.rs | 16 +++ crates/runtime/src/pallets.rs | 13 +++ 9 files changed, 302 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37ca162b7b..9a85bc3606 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4559,6 +4559,61 @@ dependencies = [ "winapi", ] +[[package]] +name = "hotstuff-consensus" +version = "0.1.0" +source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#ec7b9bbc6430ba53ea755359f5089d2a455bf1c8" +dependencies = [ + "array-bytes 6.1.0", + "async-recursion", + "async-trait", + "futures", + "hotstuff-primitives", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-basic-authorship", + "sc-chain-spec", + "sc-client-api", + "sc-consensus", + "sc-consensus-slots", + "sc-network", + "sc-network-gossip", + "sc-service", + "sc-telemetry", + "sc-utils", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto 7.0.0", + "sp-blockchain", + "sp-consensus", + "sp-core 7.0.0", + "sp-inherents", + "sp-keyring", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "tokio", +] + +[[package]] +name = "hotstuff-primitives" +version = "0.1.0" +source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#ec7b9bbc6430ba53ea755359f5089d2a455bf1c8" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto 7.0.0", + "sp-consensus-slots", + "sp-core 7.0.0", + "sp-inherents", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-timestamp", +] + [[package]] name = "http" version = "0.2.9" @@ -6067,6 +6122,8 @@ dependencies = [ "frame-system", "futures", "hex", + "hotstuff-consensus", + "hotstuff-primitives", "jsonrpsee 0.16.3", "log", "madara-runtime", @@ -6133,12 +6190,14 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", + "hotstuff-primitives", "mp-chain-id", "mp-felt", "mp-hashers", "mp-transactions", "pallet-aura", "pallet-grandpa", + "pallet-hotstuff", "pallet-starknet", "pallet-timestamp", "parity-scale-codec", @@ -7528,6 +7587,22 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-hotstuff" +version = "4.0.0-dev" +source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#ec7b9bbc6430ba53ea755359f5089d2a455bf1c8" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hotstuff-primitives", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-runtime 7.0.0", + "sp-std 5.0.0", +] + [[package]] name = "pallet-mmr" version = "4.0.0-dev" @@ -9305,6 +9380,29 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sc-basic-authorship" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e49f6e44820affccaf517fd22af564f4b495d40" +dependencies = [ + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "sc-block-builder", + "sc-client-api", + "sc-proposer-metrics", + "sc-telemetry", + "sc-transaction-pool-api", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core 7.0.0", + "sp-inherents", + "sp-runtime 7.0.0", + "substrate-prometheus-endpoint", +] + [[package]] name = "sc-block-builder" version = "0.10.0-dev" diff --git a/Cargo.toml b/Cargo.toml index f802a09290..d9289e18bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -199,6 +199,11 @@ cairo-lang-casm-contract-class = { git = "https://github.com/keep-starknet-stran cairo-lang-casm = { git = "https://github.com/keep-starknet-strange/cairo.git", branch = "no_std-support-8bbf530", default-features = false } cairo-lang-utils = { git = "https://github.com/keep-starknet-strange/cairo.git", branch = "no_std-support-8bbf530", default-features = false } +# Hotstuff consensus +hotstuff-consensus = { git = "https://github.com/Generative-Labs/hotstuff.git", branch = "polkadot-v0.9.43"} +hotstuff-primitives = { git = "https://github.com/Generative-Labs/hotstuff.git",branch = "polkadot-v0.9.43", default-features = false } +pallet-hotstuff = { git = "https://github.com/Generative-Labs/hotstuff.git", branch = "polkadot-v0.9.43", default-features = false } + # Other third party dependencies anyhow = "1.0.75" flate2 = "1.0.28" diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index cc7355ef82..4a68ef5050 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -98,6 +98,10 @@ reqwest = { workspace = true } serde_json = { workspace = true } url = { workspace = true } +# Hotstuff dependencies +hotstuff-primitives = { workspace = true } +hotstuff-consensus = { workspace = true } + [build-dependencies] substrate-build-script-utils = { workspace = true } diff --git a/crates/node/src/chain_spec.rs b/crates/node/src/chain_spec.rs index 6ff7d0693f..c74a36e171 100644 --- a/crates/node/src/chain_spec.rs +++ b/crates/node/src/chain_spec.rs @@ -1,6 +1,9 @@ use std::path::PathBuf; -use madara_runtime::{AuraConfig, GenesisConfig, GrandpaConfig, SealingMode, SystemConfig, WASM_BINARY}; +use hotstuff_primitives::AuthorityId as HotStuffId; +use madara_runtime::{ + AuraConfig, GenesisConfig, GrandpaConfig, HotstuffConfig, SealingMode, SystemConfig, WASM_BINARY, +}; use mp_felt::Felt252Wrapper; use pallet_starknet::genesis_loader::{GenesisData, GenesisLoader, HexFelt}; use sc_service::{BasePath, ChainType}; @@ -51,8 +54,8 @@ pub fn get_from_seed(seed: &str) -> ::Pu } /// Generate an Aura authority key. -pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { - (get_from_seed::(s), get_from_seed::(s)) +pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId, HotStuffId) { + (get_from_seed::(s), get_from_seed::(s), get_from_seed::(s)) } pub fn development_config(sealing: SealingMode, base_path: BasePath) -> Result { @@ -165,7 +168,7 @@ fn load_genesis(data_path: PathBuf) -> GenesisLoader { fn testnet_genesis( genesis_loader: GenesisLoader, wasm_binary: &[u8], - initial_authorities: Vec<(AuraId, GrandpaId)>, + initial_authorities: Vec<(AuraId, GrandpaId, HotStuffId)>, _enable_println: bool, ) -> GenesisConfig { let starknet_genesis_config: madara_runtime::pallet_starknet::GenesisConfig<_> = genesis_loader.into(); @@ -181,5 +184,6 @@ fn testnet_genesis( grandpa: GrandpaConfig { authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect() }, /// Starknet Genesis configuration. starknet: starknet_genesis_config, + hotstuff: HotstuffConfig { authorities: initial_authorities.iter().map(|x| (x.2.clone())).collect() }, } } diff --git a/crates/node/src/commands/run.rs b/crates/node/src/commands/run.rs index f4549ceba7..a59356f047 100644 --- a/crates/node/src/commands/run.rs +++ b/crates/node/src/commands/run.rs @@ -52,6 +52,9 @@ pub struct ExtendedRunCmd { /// increases the memory footprint of the node. #[clap(long)] pub cache: bool, + + // Choose hotstuff replace grandpa. + pub enable_hotstuff: bool, } impl ExtendedRunCmd { @@ -86,10 +89,13 @@ pub fn run_node(mut cli: Cli) -> Result<()> { None } }; + + let enable_hotstuff = cli.run.enable_hotstuff; + runner.run_node_until_exit(|config| async move { let sealing = cli.run.sealing.map(Into::into).unwrap_or_default(); let cache = cli.run.cache; - service::new_full(config, sealing, da_config, cache).map_err(sc_cli::Error::Service) + service::new_full(config, sealing, da_config, cache, enable_hotstuff).map_err(sc_cli::Error::Service) }) } diff --git a/crates/node/src/service.rs b/crates/node/src/service.rs index 44dc8bd5bf..5654e7d973 100644 --- a/crates/node/src/service.rs +++ b/crates/node/src/service.rs @@ -9,6 +9,7 @@ use futures::channel::mpsc; use futures::future; use futures::future::BoxFuture; use futures::prelude::*; +use hotstuff_consensus::LinkHalf as HotstuffLinkHalf; use madara_runtime::opaque::Block; use madara_runtime::{self, Hash, RuntimeApi, SealingMode, StarknetHasher}; use mc_block_proposer::ProposerFactory; @@ -29,7 +30,7 @@ use prometheus_endpoint::Registry; use sc_client_api::{Backend, BlockBackend, BlockchainEvents, HeaderBackend}; use sc_consensus::BasicQueue; use sc_consensus_aura::{SlotProportion, StartAuraParams}; -use sc_consensus_grandpa::{GrandpaBlockImport, SharedVoterState}; +use sc_consensus_grandpa::{LinkHalf as GrandpaLinkHalf, SharedVoterState}; pub use sc_executor::NativeElseWasmExecutor; use sc_service::error::Error as ServiceError; use sc_service::{new_db_backend, Configuration, TaskManager, WarpSyncParams}; @@ -71,6 +72,15 @@ type FullSelectChain = sc_consensus::LongestChain; type BasicImportQueue = sc_consensus::DefaultImportQueue; type BoxBlockImport = sc_consensus::BoxBlockImport>; +// Link half used in different consensus. +pub enum ConsensusLinkHalf { + Grandpa(GrandpaLinkHalf), + + Hotstuff(HotstuffLinkHalf), + + None, +} + #[allow(clippy::type_complexity)] pub fn new_partial( config: &Configuration, @@ -83,12 +93,7 @@ pub fn new_partial( FullSelectChain, sc_consensus::DefaultImportQueue, mc_transaction_pool::FullPool, - ( - BoxBlockImport, - sc_consensus_grandpa::LinkHalf, - Option, - Arc, - ), + (BoxBlockImport, ConsensusLinkHalf, Option, Arc), >, ServiceError, > @@ -100,9 +105,10 @@ where &Configuration, &TaskManager, Option, - GrandpaBlockImport, + Arc, Arc, - ) -> Result<(BasicImportQueue, BoxBlockImport), ServiceError>, + ) + -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError>, { let telemetry = config .telemetry_endpoints @@ -157,21 +163,14 @@ where client.clone(), ); - let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( - client.clone(), - &client as &Arc<_>, - select_chain.clone(), - telemetry.as_ref().map(|x| x.handle()), - )?; - let madara_backend = Arc::new(MadaraBackend::open(&config.database, &db_config_dir(config), cache_more_things)?); - let (import_queue, block_import) = build_import_queue( + let (import_queue, block_import, link) = build_import_queue( client.clone(), config, &task_manager, telemetry.as_ref().map(|x| x.handle()), - grandpa_block_import, + backend.clone(), madara_backend.clone(), )?; @@ -183,7 +182,7 @@ where keystore_container, select_chain, transaction_pool, - other: (block_import, grandpa_link, telemetry, madara_backend), + other: (block_import, link, telemetry, madara_backend), }) } @@ -193,13 +192,22 @@ pub fn build_aura_grandpa_import_queue( config: &Configuration, task_manager: &TaskManager, telemetry: Option, - grandpa_block_import: GrandpaBlockImport, + backend: Arc, _madara_backend: Arc, -) -> Result<(BasicImportQueue, BoxBlockImport), ServiceError> +) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> where RuntimeApi: ConstructRuntimeApi, RuntimeApi: Send + Sync + 'static, { + let select_chain = sc_consensus::LongestChain::new(backend.clone()); + + let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( + client.clone(), + &client as &Arc<_>, + select_chain.clone(), + telemetry.clone(), + )?; + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; let create_inherent_data_providers = move |_, ()| async move { @@ -225,7 +233,50 @@ where }) .map_err::(Into::into)?; - Ok((import_queue, Box::new(grandpa_block_import))) + Ok((import_queue, Box::new(grandpa_block_import), ConsensusLinkHalf::Grandpa(grandpa_link))) +} + +/// Build the import queue for the template runtime (aura + hotstuff). +pub fn build_aura_hotstuff_import_queue( + client: Arc, + config: &Configuration, + task_manager: &TaskManager, + telemetry: Option, + _backend: Arc, + _madara_backend: Arc, +) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> +where + RuntimeApi: ConstructRuntimeApi, + RuntimeApi: Send + Sync + 'static, +{ + let (hotstuff_block_import, hotstuff_link) = hotstuff_consensus::block_import(client.clone(), &client as &Arc<_>)?; + + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + + let create_inherent_data_providers = move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + Ok((slot, timestamp)) + }; + + let import_queue = + sc_consensus_aura::import_queue::(sc_consensus_aura::ImportQueueParams { + block_import: hotstuff_block_import.clone(), + justification_import: Some(Box::new(hotstuff_block_import.clone())), + client, + create_inherent_data_providers, + spawner: &task_manager.spawn_essential_handle(), + registry: config.prometheus_registry(), + check_for_equivocation: Default::default(), + telemetry, + compatibility_mode: sc_consensus_aura::CompatibilityMode::None, + }) + .map_err::(Into::into)?; + + Ok((import_queue, Box::new(hotstuff_block_import), ConsensusLinkHalf::Hotstuff(hotstuff_link))) } /// Build the import queue for the template runtime (manual seal). @@ -234,9 +285,9 @@ pub fn build_manual_seal_import_queue( config: &Configuration, task_manager: &TaskManager, _telemetry: Option, - _grandpa_block_import: GrandpaBlockImport, + _backend: Arc, _madara_backend: Arc, -) -> Result<(BasicImportQueue, BoxBlockImport), ServiceError> +) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> where RuntimeApi: ConstructRuntimeApi, RuntimeApi: Send + Sync + 'static, @@ -248,6 +299,7 @@ where config.prometheus_registry(), ), Box::new(client), + ConsensusLinkHalf::None, )) } @@ -261,9 +313,13 @@ pub fn new_full( sealing: SealingMode, da_layer: Option<(DaLayer, PathBuf)>, cache_more_things: bool, + enable_hotstuff: bool, ) -> Result { - let build_import_queue = - if sealing.is_default() { build_aura_grandpa_import_queue } else { build_manual_seal_import_queue }; + let build_import_queue = if sealing.is_default() { + if enable_hotstuff { build_aura_hotstuff_import_queue } else { build_aura_grandpa_import_queue } + } else { + build_manual_seal_import_queue + }; let sc_service::PartialComponents { client, @@ -273,7 +329,7 @@ pub fn new_full( keystore_container, select_chain, transaction_pool, - other: (block_import, grandpa_link, mut telemetry, madara_backend), + other: (block_import, link, mut telemetry, madara_backend), } = new_partial(&config, build_import_queue, cache_more_things)?; let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); @@ -283,15 +339,26 @@ pub fn new_full( &config.chain_spec, ); + let hotstuff_protocol_name = hotstuff_consensus::config::standard_name( + &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), + &config.chain_spec, + ); + let warp_sync_params = if sealing.is_default() { - net_config - .add_notification_protocol(sc_consensus_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); - let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( - backend.clone(), - grandpa_link.shared_authority_set().clone(), - Vec::default(), - )); - Some(WarpSyncParams::WithProvider(warp_sync)) + if let ConsensusLinkHalf::Grandpa(link) = &link { + net_config.add_notification_protocol(sc_consensus_grandpa::grandpa_peers_set_config( + grandpa_protocol_name.clone(), + )); + + let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( + backend.clone(), + link.shared_authority_set().clone(), + Vec::default(), + )); + Some(WarpSyncParams::WithProvider(warp_sync)) + } else { + None + } } else { None }; @@ -316,7 +383,7 @@ pub fn new_full( let force_authoring = config.force_authoring; let backoff_authoring_blocks: Option<()> = None; let name = config.network.node_name.clone(); - let enable_grandpa = !config.disable_grandpa && sealing.is_default(); + let enable_grandpa = !config.disable_grandpa && sealing.is_default() && !enable_hotstuff; let prometheus_registry = config.prometheus_registry().cloned(); let starting_block = client.info().best_number; @@ -510,30 +577,46 @@ pub fn new_full( protocol_name: grandpa_protocol_name, }; - // start the full GRANDPA voter - // NOTE: non-authorities could run the GRANDPA observer protocol, but at - // this point the full voter should provide better guarantees of block - // and vote data availability than the observer. The observer has not - // been tested extensively yet and having most nodes in a network run it - // could lead to finality stalls. - let grandpa_config = sc_consensus_grandpa::GrandpaParams { - config: grandpa_config, - link: grandpa_link, - network, - sync: Arc::new(sync_service), - voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), - prometheus_registry, - shared_voter_state: SharedVoterState::empty(), - telemetry: telemetry.as_ref().map(|x| x.handle()), - }; + if let ConsensusLinkHalf::Grandpa(link) = link { + // start the full GRANDPA voter + // NOTE: non-authorities could run the GRANDPA observer protocol, but at + // this point the full voter should provide better guarantees of block + // and vote data availability than the observer. The observer has not + // been tested extensively yet and having most nodes in a network run it + // could lead to finality stalls. + let grandpa_config = sc_consensus_grandpa::GrandpaParams { + config: grandpa_config, + link, + network, + sync: Arc::new(sync_service), + voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), + prometheus_registry, + shared_voter_state: SharedVoterState::empty(), + telemetry: telemetry.as_ref().map(|x| x.handle()), + }; - // the GRANDPA voter task is considered infallible, i.e. - // if it fails we take down the service with it. - task_manager.spawn_essential_handle().spawn_blocking( - "grandpa-voter", - None, - sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, - ); + // the GRANDPA voter task is considered infallible, i.e. + // if it fails we take down the service with it. + task_manager.spawn_essential_handle().spawn_blocking( + "grandpa-voter", + None, + sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, + ); + } + } else { + if let ConsensusLinkHalf::Hotstuff(link) = link { + let (voter, hotstuff_network) = hotstuff_consensus::consensus::start_hotstuff( + network, + link, + Arc::new(sync_service), + hotstuff_protocol_name, + keystore_container.keystore(), + )?; + + task_manager.spawn_essential_handle().spawn_blocking("hotstuff block voter", None, voter); + + task_manager.spawn_essential_handle().spawn_blocking("hotstuff network", None, hotstuff_network); + } } network_starter.start_network(); diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml index 9b8c242cf3..eeb7da5d78 100644 --- a/crates/runtime/Cargo.toml +++ b/crates/runtime/Cargo.toml @@ -61,6 +61,10 @@ blockifier = { workspace = true } starknet-ff = { workspace = true } starknet_api = { workspace = true } +# Hotstuff dependencies +hotstuff-primitives = { workspace = true } +pallet-hotstuff = { workspace = true } + [build-dependencies] substrate-wasm-builder = { workspace = true } @@ -98,6 +102,9 @@ std = [ "scale-info/std", "blockifier/std", "starknet_api/std", + # Hotstuff consensus dependencies + "pallet-hotstuff/std", + "hotstuff-primitives/std", ] try-runtime = [ "pallet-timestamp/try-runtime", @@ -109,6 +116,8 @@ try-runtime = [ "pallet-grandpa/try-runtime", # Madara pallets "pallet-starknet/try-runtime", + # Hotstuff pallet + "pallet-hotstuff/try-runtime", ] default = ["std"] madara-state-root = [] diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 9a6c4e66e7..3a92301691 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -27,10 +27,13 @@ pub use frame_support::weights::{IdentityFee, Weight}; pub use frame_support::{construct_runtime, parameter_types, StorageValue}; pub use frame_system::Call as SystemCall; use frame_system::{EventRecord, Phase}; +use hotstuff_primitives::AuthorityId as HotstuffId; use mp_felt::Felt252Wrapper; use mp_transactions::compute_hash::ComputeTransactionHash; use mp_transactions::{Transaction, TxType, UserTransaction}; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList}; +// Hotstuff consensus authority id. +pub use pallet_hotstuff; /// Import the StarkNet pallet. pub use pallet_starknet; use pallet_starknet::pallet::Error as PalletError; @@ -71,6 +74,8 @@ construct_runtime!( Grandpa: pallet_grandpa, // Include Starknet pallet. Starknet: pallet_starknet, + // Include hotstuff consensus pallet + Hotstuff: pallet_hotstuff, } ); @@ -231,6 +236,17 @@ impl_runtime_apis! { } } + // hotstuff runtime api + impl hotstuff_primitives::HotstuffApi for Runtime { + fn slot_duration() -> hotstuff_primitives::SlotDuration { + hotstuff_primitives::SlotDuration::from_millis(Hotstuff::slot_duration()) + } + + fn authorities() -> Vec { + Hotstuff::authorities().into_inner() + } + } + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account) diff --git a/crates/runtime/src/pallets.rs b/crates/runtime/src/pallets.rs index b619c46702..3d48a7b95a 100644 --- a/crates/runtime/src/pallets.rs +++ b/crates/runtime/src/pallets.rs @@ -10,7 +10,9 @@ pub use frame_support::weights::constants::{ pub use frame_support::weights::{IdentityFee, Weight}; pub use frame_support::{construct_runtime, parameter_types, StorageValue}; pub use frame_system::Call as SystemCall; +use hotstuff_primitives::AuthorityId as HotstuffId; pub use mp_chain_id::SN_GOERLI_CHAIN_ID; +pub use pallet_hotstuff; /// Import the StarkNet pallet. pub use pallet_starknet; pub use pallet_timestamp::Call as TimestampCall; @@ -152,6 +154,17 @@ impl pallet_timestamp::Config for Runtime { type WeightInfo = (); } +/// Hotstuff consensus pallet +impl pallet_hotstuff::Config for Runtime { + type AuthorityId = HotstuffId; + + type DisabledValidators = (); + type MaxAuthorities = ConstU32<32>; + type AllowMultipleBlocksPerSlot = ConstBool; + + type WeightInfo = (); +} + parameter_types! { pub const UnsignedPriority: u64 = 1 << 20; pub const TransactionLongevity: u64 = u64::MAX; From 50fe8229258eb3a171a1a7002970cbfb8ab79041 Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Thu, 26 Oct 2023 20:52:18 +0800 Subject: [PATCH 02/12] add hotstuff key type in runtime --- crates/node/src/commands/run.rs | 3 ++- crates/runtime/src/opaque.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/node/src/commands/run.rs b/crates/node/src/commands/run.rs index a59356f047..55c1f3bc5e 100644 --- a/crates/node/src/commands/run.rs +++ b/crates/node/src/commands/run.rs @@ -53,7 +53,8 @@ pub struct ExtendedRunCmd { #[clap(long)] pub cache: bool, - // Choose hotstuff replace grandpa. + /// Choose hotstuff replace grandpa. + #[clap(long)] pub enable_hotstuff: bool, } diff --git a/crates/runtime/src/opaque.rs b/crates/runtime/src/opaque.rs index 35918bb9b3..d5b1d89e3d 100644 --- a/crates/runtime/src/opaque.rs +++ b/crates/runtime/src/opaque.rs @@ -22,7 +22,7 @@ pub use sp_runtime::{Perbill, Permill}; use sp_std::prelude::*; use super::*; -use crate::{Aura, BlockNumber, Grandpa}; +use crate::{Aura, BlockNumber, Grandpa, Hotstuff}; /// Opaque block header type. pub type Header = generic::Header; /// Opaque block type. @@ -34,5 +34,6 @@ impl_opaque_keys! { pub struct SessionKeys { pub aura: Aura, pub grandpa: Grandpa, + pub hotstuff: Hotstuff, } } From 491bc466a37951f275f14273ebc9ece753c80508 Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Sun, 29 Oct 2023 17:46:08 +0800 Subject: [PATCH 03/12] add madara-hotstuff-runtime crate --- Cargo.lock | 41 ++ Cargo.toml | 9 +- crates/node/Cargo.toml | 5 + crates/node/src/benchmarking.rs | 3 + crates/node/src/chain_spec.rs | 12 +- crates/node/src/command.rs | 3 + crates/node/src/commands/run.rs | 11 +- crates/node/src/service.rs | 311 +++++++----- crates/node/src/starknet.rs | 3 + crates/runtime/madara-hotstuff/Cargo.toml | 131 ++++++ crates/runtime/{ => madara-hotstuff}/build.rs | 0 .../{ => madara-hotstuff}/src/config.rs | 0 crates/runtime/madara-hotstuff/src/lib.rs | 444 ++++++++++++++++++ .../{ => madara-hotstuff}/src/opaque.rs | 3 +- crates/runtime/madara-hotstuff/src/pallets.rs | 175 +++++++ .../src/runtime_tests.rs | 0 .../{ => madara-hotstuff}/src/types.rs | 0 crates/runtime/{ => madara}/Cargo.toml | 0 crates/runtime/madara/build.rs | 5 + crates/runtime/madara/src/config.rs | 115 +++++ crates/runtime/{ => madara}/src/lib.rs | 16 - crates/runtime/madara/src/opaque.rs | 38 ++ crates/runtime/{ => madara}/src/pallets.rs | 13 - crates/runtime/madara/src/runtime_tests.rs | 27 ++ crates/runtime/madara/src/types.rs | 31 ++ 25 files changed, 1232 insertions(+), 164 deletions(-) create mode 100644 crates/runtime/madara-hotstuff/Cargo.toml rename crates/runtime/{ => madara-hotstuff}/build.rs (100%) rename crates/runtime/{ => madara-hotstuff}/src/config.rs (100%) create mode 100644 crates/runtime/madara-hotstuff/src/lib.rs rename crates/runtime/{ => madara-hotstuff}/src/opaque.rs (95%) create mode 100644 crates/runtime/madara-hotstuff/src/pallets.rs rename crates/runtime/{ => madara-hotstuff}/src/runtime_tests.rs (100%) rename crates/runtime/{ => madara-hotstuff}/src/types.rs (100%) rename crates/runtime/{ => madara}/Cargo.toml (100%) create mode 100644 crates/runtime/madara/build.rs create mode 100644 crates/runtime/madara/src/config.rs rename crates/runtime/{ => madara}/src/lib.rs (97%) create mode 100644 crates/runtime/madara/src/opaque.rs rename crates/runtime/{ => madara}/src/pallets.rs (95%) create mode 100644 crates/runtime/madara/src/runtime_tests.rs create mode 100644 crates/runtime/madara/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 9a85bc3606..9b0e654ae7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6126,6 +6126,7 @@ dependencies = [ "hotstuff-primitives", "jsonrpsee 0.16.3", "log", + "madara-hotstuff-runtime", "madara-runtime", "mc-block-proposer", "mc-data-availability", @@ -6178,6 +6179,46 @@ dependencies = [ "url", ] +[[package]] +name = "madara-hotstuff-runtime" +version = "0.4.0" +dependencies = [ + "blockifier", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hotstuff-primitives", + "mp-chain-id", + "mp-felt", + "mp-hashers", + "mp-transactions", + "pallet-aura", + "pallet-hotstuff", + "pallet-starknet", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core 7.0.0", + "sp-inherents", + "sp-offchain", + "sp-runtime 7.0.0", + "sp-session", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "starknet-ff", + "starknet_api", + "substrate-wasm-builder", +] + [[package]] name = "madara-runtime" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index d9289e18bf..1ce0daa485 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,8 @@ resolver = "2" members = [ "crates/node", - "crates/runtime", + "crates/runtime/madara", + "crates/runtime/madara-hotstuff", "crates/pallets/starknet", "crates/primitives/digest-log", "crates/primitives/transactions", @@ -28,7 +29,8 @@ members = [ # We don't want `cargo test` to trigger its tests default-members = [ "crates/node", - "crates/runtime", + "crates/runtime/madara", + "crates/runtime/madara-hotstuff", "crates/pallets/starknet", "crates/primitives/digest-log", "crates/primitives/transactions", @@ -169,7 +171,8 @@ mc-transaction-pool = { path = "crates/client/transaction-pool" } mc-data-availability = { path = "crates/client/data-availability" } # Madara runtime -madara-runtime = { path = "crates/runtime" } +madara-runtime = { path = "crates/runtime/madara" } +madara-hotstuff-runtime = { path = "crates/runtime/madara-hotstuff" } # Starknet dependencies # Cairo Virtual Machine diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 4a68ef5050..a95b7de191 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -75,6 +75,7 @@ frame-benchmarking-cli = { workspace = true } blockifier = { workspace = true } hex = { workspace = true } madara-runtime = { workspace = true } +madara-hotstuff-runtime = { workspace = true } mc-block-proposer = { workspace = true } mc-data-availability = { workspace = true } mc-db = { workspace = true } @@ -120,3 +121,7 @@ disable-transaction-fee = ["madara-runtime/disable-transaction-fee"] # Load sharingan chain-specs during the compilation # This is the way to run a sharingan chain sharingan = [] + +madara-runtime = [] + +madara-hotstuff-runtime = [] diff --git a/crates/node/src/benchmarking.rs b/crates/node/src/benchmarking.rs index c7dd637c10..5ecbba09f8 100644 --- a/crates/node/src/benchmarking.rs +++ b/crates/node/src/benchmarking.rs @@ -23,6 +23,9 @@ use std::sync::Arc; use std::time::Duration; +#[cfg(feature = "madara-hotstuff-runtime")] +use madara_hotstuff_runtime as runtime; +#[cfg(feature = "madara-runtime")] use madara_runtime as runtime; use runtime::SystemCall; use sc_cli::Result; diff --git a/crates/node/src/chain_spec.rs b/crates/node/src/chain_spec.rs index c74a36e171..1806ba8004 100644 --- a/crates/node/src/chain_spec.rs +++ b/crates/node/src/chain_spec.rs @@ -1,9 +1,10 @@ use std::path::PathBuf; use hotstuff_primitives::AuthorityId as HotStuffId; -use madara_runtime::{ - AuraConfig, GenesisConfig, GrandpaConfig, HotstuffConfig, SealingMode, SystemConfig, WASM_BINARY, -}; +#[cfg(feature = "madara-hotstuff-runtime")] +use madara_hotstuff_runtime::{AuraConfig, GenesisConfig, HotstuffConfig, SealingMode, SystemConfig, WASM_BINARY}; +#[cfg(feature = "madara-runtime")] +use madara_runtime::{AuraConfig, GenesisConfig, GrandpaConfig, SealingMode, SystemConfig, WASM_BINARY}; use mp_felt::Felt252Wrapper; use pallet_starknet::genesis_loader::{GenesisData, GenesisLoader, HexFelt}; use sc_service::{BasePath, ChainType}; @@ -42,7 +43,10 @@ pub struct DevGenesisExt { impl sp_runtime::BuildStorage for DevGenesisExt { fn assimilate_storage(&self, storage: &mut Storage) -> Result<(), String> { BasicExternalities::execute_with_storage(storage, || { + #[cfg(feature = "madara-runtime")] madara_runtime::Sealing::set(&self.sealing); + #[cfg(feature = "madara-hotstuff-runtime")] + madara_hotstuff_runtime::Sealing::set(&self.sealing); }); self.genesis_config.assimilate_storage(storage) } @@ -181,9 +185,11 @@ fn testnet_genesis( // Authority-based consensus protocol used for block production aura: AuraConfig { authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect() }, // Deterministic finality mechanism used for block finalization + #[cfg(feature = "madara-runtime")] grandpa: GrandpaConfig { authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect() }, /// Starknet Genesis configuration. starknet: starknet_genesis_config, + #[cfg(feature = "madara-hotstuff-runtime")] hotstuff: HotstuffConfig { authorities: initial_authorities.iter().map(|x| (x.2.clone())).collect() }, } } diff --git a/crates/node/src/command.rs b/crates/node/src/command.rs index 9bad7ddb3a..b0c4fe57ec 100644 --- a/crates/node/src/command.rs +++ b/crates/node/src/command.rs @@ -1,4 +1,7 @@ use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE}; +#[cfg(feature = "madara-hotstuff-runtime")] +use madara_hotstuff_runtime::Block; +#[cfg(feature = "madara-runtime")] use madara_runtime::Block; use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; diff --git a/crates/node/src/commands/run.rs b/crates/node/src/commands/run.rs index 55c1f3bc5e..37cba450df 100644 --- a/crates/node/src/commands/run.rs +++ b/crates/node/src/commands/run.rs @@ -1,5 +1,8 @@ use std::path::PathBuf; +#[cfg(feature = "madara-hotstuff-runtime")] +use madara_hotstuff_runtime::SealingMode; +#[cfg(feature = "madara-runtime")] use madara_runtime::SealingMode; use mc_data_availability::DaLayer; use sc_cli::{Result, RpcMethods, RunCmd, SubstrateCli}; @@ -52,10 +55,6 @@ pub struct ExtendedRunCmd { /// increases the memory footprint of the node. #[clap(long)] pub cache: bool, - - /// Choose hotstuff replace grandpa. - #[clap(long)] - pub enable_hotstuff: bool, } impl ExtendedRunCmd { @@ -91,12 +90,10 @@ pub fn run_node(mut cli: Cli) -> Result<()> { } }; - let enable_hotstuff = cli.run.enable_hotstuff; - runner.run_node_until_exit(|config| async move { let sealing = cli.run.sealing.map(Into::into).unwrap_or_default(); let cache = cli.run.cache; - service::new_full(config, sealing, da_config, cache, enable_hotstuff).map_err(sc_cli::Error::Service) + service::new_full(config, sealing, da_config, cache).map_err(sc_cli::Error::Service) }) } diff --git a/crates/node/src/service.rs b/crates/node/src/service.rs index 5654e7d973..bf73159a1b 100644 --- a/crates/node/src/service.rs +++ b/crates/node/src/service.rs @@ -9,9 +9,12 @@ use futures::channel::mpsc; use futures::future; use futures::future::BoxFuture; use futures::prelude::*; +#[cfg(feature = "madara-hotstuff-runtime")] use hotstuff_consensus::LinkHalf as HotstuffLinkHalf; -use madara_runtime::opaque::Block; -use madara_runtime::{self, Hash, RuntimeApi, SealingMode, StarknetHasher}; +#[cfg(feature = "madara-hotstuff-runtime")] +use madara_hotstuff_runtime::{self, opaque::Block, Hash, RuntimeApi, SealingMode, StarknetHasher, SLOT_DURATION}; +#[cfg(feature = "madara-runtime")] +use madara_runtime::{self, opaque::Block, Hash, RuntimeApi, SealingMode, StarknetHasher, SLOT_DURATION}; use mc_block_proposer::ProposerFactory; use mc_data_availability::avail::config::AvailConfig; use mc_data_availability::avail::AvailClient; @@ -30,10 +33,11 @@ use prometheus_endpoint::Registry; use sc_client_api::{Backend, BlockBackend, BlockchainEvents, HeaderBackend}; use sc_consensus::BasicQueue; use sc_consensus_aura::{SlotProportion, StartAuraParams}; +#[cfg(feature = "madara-runtime")] use sc_consensus_grandpa::{LinkHalf as GrandpaLinkHalf, SharedVoterState}; pub use sc_executor::NativeElseWasmExecutor; use sc_service::error::Error as ServiceError; -use sc_service::{new_db_backend, Configuration, TaskManager, WarpSyncParams}; +use sc_service::{new_db_backend, Configuration, TaskManager}; use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker}; use sp_api::offchain::OffchainStorage; use sp_api::{ConstructRuntimeApi, TransactionFor}; @@ -57,10 +61,16 @@ impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { type ExtendHostFunctions = (); fn dispatch(method: &str, data: &[u8]) -> Option> { + #[cfg(feature = "madara-hotstuff-runtime")] + return madara_hotstuff_runtime::api::dispatch(method, data); + #[cfg(feature = "madara-runtime")] madara_runtime::api::dispatch(method, data) } fn native_version() -> sc_executor::NativeVersion { + #[cfg(feature = "madara-hotstuff-runtime")] + return madara_hotstuff_runtime::native_version(); + #[cfg(feature = "madara-runtime")] madara_runtime::native_version() } } @@ -74,9 +84,10 @@ type BoxBlockImport = sc_consensus::BoxBlockImport), - - Hotstuff(HotstuffLinkHalf), + #[cfg(feature = "madara-runtime")] + Link(GrandpaLinkHalf), + #[cfg(feature = "madara-hotstuff-runtime")] + Link(HotstuffLinkHalf), None, } @@ -187,28 +198,124 @@ where } /// Build the import queue for the template runtime (aura + grandpa). -pub fn build_aura_grandpa_import_queue( +// pub fn build_aura_grandpa_import_queue( +// client: Arc, +// config: &Configuration, +// task_manager: &TaskManager, +// telemetry: Option, +// backend: Arc, +// _madara_backend: Arc, +// ) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> +// where +// RuntimeApi: ConstructRuntimeApi, +// RuntimeApi: Send + Sync + 'static, +// { +// let select_chain = sc_consensus::LongestChain::new(backend.clone()); + +// let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( +// client.clone(), +// &client as &Arc<_>, +// select_chain.clone(), +// telemetry.clone(), +// )?; + +// let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + +// let create_inherent_data_providers = move |_, ()| async move { +// let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); +// let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( +// *timestamp, +// slot_duration, +// ); +// Ok((slot, timestamp)) +// }; + +// let import_queue = +// sc_consensus_aura::import_queue::(sc_consensus_aura::ImportQueueParams { +// block_import: grandpa_block_import.clone(), +// justification_import: Some(Box::new(grandpa_block_import.clone())), +// client, +// create_inherent_data_providers, +// spawner: &task_manager.spawn_essential_handle(), +// registry: config.prometheus_registry(), +// check_for_equivocation: Default::default(), +// telemetry, +// compatibility_mode: sc_consensus_aura::CompatibilityMode::None, +// }) +// .map_err::(Into::into)?; + +// Ok((import_queue, Box::new(grandpa_block_import), ConsensusLinkHalf::Link(grandpa_link))) +// } + +// /// Build the import queue for the template runtime (aura + hotstuff). +// pub fn build_aura_hotstuff_import_queue( +// client: Arc, +// config: &Configuration, +// task_manager: &TaskManager, +// telemetry: Option, +// _backend: Arc, +// _madara_backend: Arc, +// ) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> +// where +// RuntimeApi: ConstructRuntimeApi, +// RuntimeApi: Send + Sync + 'static, +// { +// let (hotstuff_block_import, hotstuff_link) = hotstuff_consensus::block_import(client.clone(), +// &client as &Arc<_>)?; + +// let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + +// let create_inherent_data_providers = move |_, ()| async move { +// let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); +// let slot = +// sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( +// *timestamp, slot_duration, +// ); +// Ok((slot, timestamp)) +// }; + +// let import_queue = +// sc_consensus_aura::import_queue::(sc_consensus_aura::ImportQueueParams { block_import: +// hotstuff_block_import.clone(), justification_import: +// Some(Box::new(hotstuff_block_import.clone())), client, +// create_inherent_data_providers, +// spawner: &task_manager.spawn_essential_handle(), +// registry: config.prometheus_registry(), +// check_for_equivocation: Default::default(), +// telemetry, +// compatibility_mode: sc_consensus_aura::CompatibilityMode::None, +// }) +// .map_err::(Into::into)?; + +// Ok((import_queue, Box::new(hotstuff_block_import), +// ConsensusLinkHalf::Hotstuff(hotstuff_link))) } + +/// Build the import queue for the template runtime (aura + hotstuff). +pub fn build_common_import_queue( client: Arc, config: &Configuration, task_manager: &TaskManager, telemetry: Option, - backend: Arc, + #[allow(unused_variables)] backend: Arc, _madara_backend: Arc, ) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> where RuntimeApi: ConstructRuntimeApi, RuntimeApi: Send + Sync + 'static, { - let select_chain = sc_consensus::LongestChain::new(backend.clone()); + #[cfg(feature = "madara-hotstuff-runtime")] + let (block_import, link) = hotstuff_consensus::block_import(client.clone(), &client as &Arc<_>)?; - let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( + #[cfg(feature = "madara-runtime")] + let (block_import, link) = sc_consensus_grandpa::block_import( client.clone(), &client as &Arc<_>, - select_chain.clone(), + sc_consensus::LongestChain::new(backend.clone()), telemetry.clone(), )?; - let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + let slot_duration: hotstuff_primitives::SlotDuration = sc_consensus_aura::slot_duration(&*client)?; let create_inherent_data_providers = move |_, ()| async move { let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); @@ -221,8 +328,8 @@ where let import_queue = sc_consensus_aura::import_queue::(sc_consensus_aura::ImportQueueParams { - block_import: grandpa_block_import.clone(), - justification_import: Some(Box::new(grandpa_block_import.clone())), + block_import: block_import.clone(), + justification_import: Some(Box::new(block_import.clone())), client, create_inherent_data_providers, spawner: &task_manager.spawn_essential_handle(), @@ -233,50 +340,7 @@ where }) .map_err::(Into::into)?; - Ok((import_queue, Box::new(grandpa_block_import), ConsensusLinkHalf::Grandpa(grandpa_link))) -} - -/// Build the import queue for the template runtime (aura + hotstuff). -pub fn build_aura_hotstuff_import_queue( - client: Arc, - config: &Configuration, - task_manager: &TaskManager, - telemetry: Option, - _backend: Arc, - _madara_backend: Arc, -) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> -where - RuntimeApi: ConstructRuntimeApi, - RuntimeApi: Send + Sync + 'static, -{ - let (hotstuff_block_import, hotstuff_link) = hotstuff_consensus::block_import(client.clone(), &client as &Arc<_>)?; - - let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - - let create_inherent_data_providers = move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *timestamp, - slot_duration, - ); - Ok((slot, timestamp)) - }; - - let import_queue = - sc_consensus_aura::import_queue::(sc_consensus_aura::ImportQueueParams { - block_import: hotstuff_block_import.clone(), - justification_import: Some(Box::new(hotstuff_block_import.clone())), - client, - create_inherent_data_providers, - spawner: &task_manager.spawn_essential_handle(), - registry: config.prometheus_registry(), - check_for_equivocation: Default::default(), - telemetry, - compatibility_mode: sc_consensus_aura::CompatibilityMode::None, - }) - .map_err::(Into::into)?; - - Ok((import_queue, Box::new(hotstuff_block_import), ConsensusLinkHalf::Hotstuff(hotstuff_link))) + Ok((import_queue, Box::new(block_import), ConsensusLinkHalf::Link(link))) } /// Build the import queue for the template runtime (manual seal). @@ -313,13 +377,9 @@ pub fn new_full( sealing: SealingMode, da_layer: Option<(DaLayer, PathBuf)>, cache_more_things: bool, - enable_hotstuff: bool, ) -> Result { - let build_import_queue = if sealing.is_default() { - if enable_hotstuff { build_aura_hotstuff_import_queue } else { build_aura_grandpa_import_queue } - } else { - build_manual_seal_import_queue - }; + let build_import_queue = + if sealing.is_default() { build_common_import_queue } else { build_manual_seal_import_queue }; let sc_service::PartialComponents { client, @@ -332,20 +392,24 @@ pub fn new_full( other: (block_import, link, mut telemetry, madara_backend), } = new_partial(&config, build_import_queue, cache_more_things)?; + #[allow(unused_mut)] let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); + #[cfg(feature = "madara-runtime")] let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), &config.chain_spec, ); + #[cfg(feature = "madara-hotstuff-runtime")] let hotstuff_protocol_name = hotstuff_consensus::config::standard_name( &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), &config.chain_spec, ); let warp_sync_params = if sealing.is_default() { - if let ConsensusLinkHalf::Grandpa(link) = &link { + #[cfg(feature = "madara-runtime")] + if let ConsensusLinkHalf::Link(link) = &link { net_config.add_notification_protocol(sc_consensus_grandpa::grandpa_peers_set_config( grandpa_protocol_name.clone(), )); @@ -355,10 +419,12 @@ pub fn new_full( link.shared_authority_set().clone(), Vec::default(), )); - Some(WarpSyncParams::WithProvider(warp_sync)) + Some(sc_service::WarpSyncParams::WithProvider(warp_sync)) } else { None } + #[cfg(feature = "madara-hotstuff-runtime")] + None } else { None }; @@ -382,8 +448,9 @@ pub fn new_full( let role = config.role.clone(); let force_authoring = config.force_authoring; let backoff_authoring_blocks: Option<()> = None; + #[cfg(feature = "madara-runtime")] let name = config.network.node_name.clone(); - let enable_grandpa = !config.disable_grandpa && sealing.is_default() && !enable_hotstuff; + let enable_grandpa = !config.disable_grandpa && sealing.is_default(); let prometheus_registry = config.prometheus_registry().cloned(); let starting_block = client.info().best_number; @@ -561,61 +628,65 @@ pub fn new_full( } if enable_grandpa { - // if the node isn't actively participating in consensus then it doesn't - // need a keystore, regardless of which protocol we use below. - let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; - - let grandpa_config = sc_consensus_grandpa::Config { - // FIXME #1578 make this available through chainspec - gossip_duration: Duration::from_millis(333), - justification_period: 512, - name: Some(name), - observer_enabled: false, - keystore, - local_role: role, - telemetry: telemetry.as_ref().map(|x| x.handle()), - protocol_name: grandpa_protocol_name, - }; - - if let ConsensusLinkHalf::Grandpa(link) = link { - // start the full GRANDPA voter - // NOTE: non-authorities could run the GRANDPA observer protocol, but at - // this point the full voter should provide better guarantees of block - // and vote data availability than the observer. The observer has not - // been tested extensively yet and having most nodes in a network run it - // could lead to finality stalls. - let grandpa_config = sc_consensus_grandpa::GrandpaParams { - config: grandpa_config, - link, - network, - sync: Arc::new(sync_service), - voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), - prometheus_registry, - shared_voter_state: SharedVoterState::empty(), + #[cfg(feature = "madara-runtime")] + { + // if the node isn't actively participating in consensus then it doesn't + // need a keystore, regardless of which protocol we use below. + let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; + + let grandpa_config = sc_consensus_grandpa::Config { + // FIXME #1578 make this available through chainspec + gossip_duration: Duration::from_millis(333), + justification_period: 512, + name: Some(name), + observer_enabled: false, + keystore, + local_role: role, telemetry: telemetry.as_ref().map(|x| x.handle()), + protocol_name: grandpa_protocol_name, }; - // the GRANDPA voter task is considered infallible, i.e. - // if it fails we take down the service with it. - task_manager.spawn_essential_handle().spawn_blocking( - "grandpa-voter", - None, - sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, - ); + if let ConsensusLinkHalf::Link(link) = link { + // start the full GRANDPA voter + // NOTE: non-authorities could run the GRANDPA observer protocol, but at + // this point the full voter should provide better guarantees of block + // and vote data availability than the observer. The observer has not + // been tested extensively yet and having most nodes in a network run it + // could lead to finality stalls. + let grandpa_config = sc_consensus_grandpa::GrandpaParams { + config: grandpa_config, + link, + network, + sync: Arc::new(sync_service), + voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), + prometheus_registry, + shared_voter_state: SharedVoterState::empty(), + telemetry: telemetry.as_ref().map(|x| x.handle()), + }; + + // the GRANDPA voter task is considered infallible, i.e. + // if it fails we take down the service with it. + task_manager.spawn_essential_handle().spawn_blocking( + "grandpa-voter", + None, + sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, + ); + } } - } else { - if let ConsensusLinkHalf::Hotstuff(link) = link { - let (voter, hotstuff_network) = hotstuff_consensus::consensus::start_hotstuff( - network, - link, - Arc::new(sync_service), - hotstuff_protocol_name, - keystore_container.keystore(), - )?; - - task_manager.spawn_essential_handle().spawn_blocking("hotstuff block voter", None, voter); - - task_manager.spawn_essential_handle().spawn_blocking("hotstuff network", None, hotstuff_network); + #[cfg(feature = "madara-hotstuff-runtime")] + { + if let ConsensusLinkHalf::Link(link) = link { + let (voter, hotstuff_network) = hotstuff_consensus::consensus::start_hotstuff( + network, + link, + Arc::new(sync_service), + hotstuff_protocol_name, + keystore_container.keystore(), + )?; + + task_manager.spawn_essential_handle().spawn_blocking("hotstuff block voter", None, voter); + task_manager.spawn_essential_handle().spawn_blocking("hotstuff network", None, hotstuff_network); + } } } @@ -658,7 +729,7 @@ where inherent_data: &mut sp_inherents::InherentData, ) -> Result<(), sp_inherents::Error> { TIMESTAMP.with(|x| { - *x.borrow_mut() += madara_runtime::SLOT_DURATION; + *x.borrow_mut() += SLOT_DURATION; inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, &*x.borrow()) }) } @@ -729,6 +800,6 @@ type ChainOpsResult = Result< pub fn new_chain_ops(config: &mut Configuration, cache_more_things: bool) -> ChainOpsResult { config.keystore = sc_service::config::KeystoreConfig::InMemory; let sc_service::PartialComponents { client, backend, import_queue, task_manager, other, .. } = - new_partial::<_>(config, build_aura_grandpa_import_queue, cache_more_things)?; + new_partial::<_>(config, build_common_import_queue, cache_more_things)?; Ok((client, backend, import_queue, task_manager, other.3)) } diff --git a/crates/node/src/starknet.rs b/crates/node/src/starknet.rs index d419b3174e..701388e034 100644 --- a/crates/node/src/starknet.rs +++ b/crates/node/src/starknet.rs @@ -1,5 +1,8 @@ use std::path::PathBuf; +#[cfg(feature = "madara-hotstuff-runtime")] +use madara_hotstuff_runtime::opaque::Block; +#[cfg(feature = "madara-runtime")] use madara_runtime::opaque::Block; use sc_service::Configuration; diff --git a/crates/runtime/madara-hotstuff/Cargo.toml b/crates/runtime/madara-hotstuff/Cargo.toml new file mode 100644 index 0000000000..b4fd8293c3 --- /dev/null +++ b/crates/runtime/madara-hotstuff/Cargo.toml @@ -0,0 +1,131 @@ +[package] +name = "madara-hotstuff-runtime" +version.workspace = true +edition.workspace = true +description = "Madara runtime with hotstuff consensus." +authors = [ + "Abdelhamid Bakhta <@abdelhamidbakhta>", + "Substrate DevHub ", +] +homepage = "https://github.com/keep-starknet-strange/madara" +license = "MIT" +publish = false +repository = "https://github.com/keep-starknet-strange/madara" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true, features = [] } +scale-info = { workspace = true, features = [] } +serde = { workspace = true } + +sp-api = { workspace = true } +sp-block-builder = { workspace = true } +sp-consensus-aura = { workspace = true } +sp-inherents = { workspace = true } +sp-offchain = { workspace = true } +sp-session = { workspace = true } +sp-transaction-pool = { workspace = true } +sp-version = { workspace = true } + +frame-executive = { workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +frame-try-runtime = { workspace = true, optional = true } +pallet-aura = { workspace = true } +pallet-timestamp = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Used for the node RPCs +frame-system-rpc-runtime-api = { workspace = true } + +# Used for runtime benchmarking +frame-benchmarking = { workspace = true, optional = true } +frame-system-benchmarking = { workspace = true, optional = true } + +# Madara Local Dependencies +# Madara Pallets +pallet-starknet = { workspace = true } + +# Madara Primitives +mp-chain-id = { workspace = true } +mp-felt = { workspace = true } +mp-hashers = { workspace = true } +mp-transactions = { workspace = true } +# Starknet dependencies +blockifier = { workspace = true } +starknet-ff = { workspace = true } +starknet_api = { workspace = true } + +# Hotstuff dependencies +hotstuff-primitives = { workspace = true } +pallet-hotstuff = { workspace = true } + +[build-dependencies] +substrate-wasm-builder = { workspace = true } + +[features] +std = [ + # Madara pallets + "pallet-starknet/std", + # Frame dependencies + "frame-try-runtime?/std", + "frame-system-benchmarking?/std", + "frame-benchmarking?/std", + "frame-executive/std", + "frame-support/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "frame-try-runtime/std", + # Frame pallets dependencies + "pallet-aura/std", + "pallet-timestamp/std", + # Substrate primitives dependencies + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + # 3rd party dependencies + "parity-scale-codec/std", + "scale-info/std", + "blockifier/std", + "starknet_api/std", + # Hotstuff consensus dependencies + "pallet-hotstuff/std", + "hotstuff-primitives/std", +] +try-runtime = [ + "pallet-timestamp/try-runtime", + "frame-try-runtime/try-runtime", + "frame-executive/try-runtime", + "frame-system/try-runtime", + "frame-support/try-runtime", + "pallet-aura/try-runtime", + # Madara pallets + "pallet-starknet/try-runtime", + # Hotstuff pallet + "pallet-hotstuff/try-runtime", +] +default = ["std"] +madara-state-root = [] +disable-transaction-fee = [] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + # Madara pallets + "pallet-starknet/runtime-benchmarks", +] diff --git a/crates/runtime/build.rs b/crates/runtime/madara-hotstuff/build.rs similarity index 100% rename from crates/runtime/build.rs rename to crates/runtime/madara-hotstuff/build.rs diff --git a/crates/runtime/src/config.rs b/crates/runtime/madara-hotstuff/src/config.rs similarity index 100% rename from crates/runtime/src/config.rs rename to crates/runtime/madara-hotstuff/src/config.rs diff --git a/crates/runtime/madara-hotstuff/src/lib.rs b/crates/runtime/madara-hotstuff/src/lib.rs new file mode 100644 index 0000000000..e5f8c7df72 --- /dev/null +++ b/crates/runtime/madara-hotstuff/src/lib.rs @@ -0,0 +1,444 @@ +//! L2 validity rollup, settling on Ethereum or as a L3 application-specific rollup, settling on +//! public Starknet L2. +//! For now this is the same because we don't support yet validity proofs and state updates to +//! another layer. +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +/// Runtime modules. +mod config; +pub mod opaque; +mod pallets; +mod runtime_tests; +mod types; + +use blockifier::execution::contract_class::ContractClass; +pub use config::*; +pub use frame_support::traits::{ConstU128, ConstU32, ConstU64, ConstU8, KeyOwnerProofSystem, Randomness, StorageInfo}; +pub use frame_support::weights::constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, +}; +pub use frame_support::weights::{IdentityFee, Weight}; +pub use frame_support::{construct_runtime, parameter_types, StorageValue}; +pub use frame_system::Call as SystemCall; +use frame_system::{EventRecord, Phase}; +use hotstuff_primitives::AuthorityId as HotstuffId; +use mp_felt::Felt252Wrapper; +use mp_transactions::compute_hash::ComputeTransactionHash; +use mp_transactions::{Transaction, TxType, UserTransaction}; +// Hotstuff consensus authority id. +pub use pallet_hotstuff; +/// Import the StarkNet pallet. +pub use pallet_starknet; +use pallet_starknet::pallet::Error as PalletError; +use pallet_starknet::runtime_api::StarknetTransactionExecutionError; +use pallet_starknet::Call::{consume_l1_message, declare, deploy_account, invoke}; +use pallet_starknet::{Config, Event}; +pub use pallet_timestamp::Call as TimestampCall; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::crypto::KeyTypeId; +use sp_core::OpaqueMetadata; +use sp_runtime::traits::{BlakeTwo256, Block as BlockT}; +use sp_runtime::transaction_validity::{TransactionSource, TransactionValidity}; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +use sp_runtime::{generic, ApplyExtrinsicResult, DispatchError}; +pub use sp_runtime::{Perbill, Permill}; +use sp_std::prelude::*; +use sp_version::RuntimeVersion; +use starknet_api::api_core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; +use starknet_api::hash::StarkFelt; +use starknet_api::state::StorageKey; +use starknet_api::transaction::{Calldata, Event as StarknetEvent, TransactionHash}; +/// Import the types. +pub use types::*; + +// Create the runtime by composing the FRAME pallets that were previously configured. +construct_runtime!( + pub struct Runtime + where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Timestamp: pallet_timestamp, + Aura: pallet_aura, + // Include Starknet pallet. + Starknet: pallet_starknet, + // Include hotstuff consensus pallet + Hotstuff: pallet_hotstuff, + } +); + +/// The address format for describing accounts. +pub type Address = sp_runtime::MultiAddress; +/// Block header type as expected by this runtime. +pub type Header = generic::Header; +/// Block type as expected by this runtime. +pub type Block = generic::Block; + +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; +/// Executive: handles dispatch to the various modules. +pub type Executive = + frame_executive::Executive, Runtime, AllPalletsWithSystem>; + +#[cfg(feature = "runtime-benchmarks")] +#[macro_use] +extern crate frame_benchmarking; + +#[cfg(feature = "runtime-benchmarks")] +mod benches { + define_benchmarks!( + [frame_benchmarking, BaselineBench::] + [frame_system, SystemBench::] + [pallet_balances, Balances] + [pallet_timestamp, Timestamp] + ); +} + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block); + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().into_inner() + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + opaque::SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + opaque::SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + // hotstuff runtime api + impl hotstuff_primitives::HotstuffApi for Runtime { + fn slot_duration() -> hotstuff_primitives::SlotDuration { + hotstuff_primitives::SlotDuration::from_millis(Hotstuff::slot_duration()) + } + + fn authorities() -> Vec { + Hotstuff::authorities().into_inner() + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl pallet_starknet::runtime_api::StarknetRuntimeApi for Runtime { + + fn get_storage_at(address: ContractAddress, key: StorageKey) -> Result { + Starknet::get_storage_at(address, key) + } + + fn call(address: ContractAddress, function_selector: EntryPointSelector, calldata: Calldata) -> Result, DispatchError> { + Starknet::call_contract(address, function_selector, calldata) + } + + fn nonce(address: ContractAddress) -> Nonce{ + Starknet::nonce(address) + } + + fn contract_class_hash_by_address(address: ContractAddress) -> ClassHash { + Starknet::contract_class_hash_by_address(address) + } + + fn contract_class_by_class_hash(class_hash: ClassHash) -> Option { + Starknet::contract_class_by_class_hash(class_hash) + } + + fn chain_id() -> Felt252Wrapper { + Starknet::chain_id() + } + + fn estimate_fee(transaction: UserTransaction) -> Result<(u64, u64), DispatchError> { + Starknet::estimate_fee(transaction) + } + + fn get_starknet_events_and_their_associated_tx_hash(block_extrinsics: Vec<::Extrinsic>, chain_id: Felt252Wrapper) -> Vec<(Felt252Wrapper, StarknetEvent)> { + System::read_events_no_consensus().filter_map(|event_record| { + let (phase, event) = match *event_record { + EventRecord { event: RuntimeEvent::Starknet(Event::StarknetEvent(event)), phase, .. } => (phase, event), + _ => return None, + }; + + let index = match phase { + Phase::ApplyExtrinsic(idx) => {idx}, + _ => return None + + }; + let extrinsic = &block_extrinsics[index as usize]; + let tx_hash = match &extrinsic.function { + RuntimeCall::Starknet( invoke { transaction }) => transaction.compute_hash::<::SystemHash>(chain_id, false), + RuntimeCall::Starknet( declare { transaction, .. }) => transaction.compute_hash::<::SystemHash>(chain_id, false), + RuntimeCall::Starknet( deploy_account { transaction }) => transaction.compute_hash::<::SystemHash>(chain_id, false), + RuntimeCall::Starknet( consume_l1_message { transaction, .. }) => transaction.compute_hash::<::SystemHash>(chain_id, false), + _ => return None, + }; + + Some((tx_hash, event)) + }).collect() + } + + fn extrinsic_filter(xts: Vec<::Extrinsic>) -> Vec { + xts.into_iter().filter_map(|xt| match xt.function { + RuntimeCall::Starknet( invoke { transaction }) => Some(Transaction::Invoke(transaction)), + RuntimeCall::Starknet( declare { transaction, .. }) => Some(Transaction::Declare(transaction)), + RuntimeCall::Starknet( deploy_account { transaction }) => Some(Transaction::DeployAccount(transaction)), + RuntimeCall::Starknet( consume_l1_message { transaction, .. }) => Some(Transaction::L1Handler(transaction)), + _ => None + }).collect::>() + } + + fn get_events_for_tx_hash(extrinsics: Vec<::Extrinsic>, chain_id: Felt252Wrapper, tx_hash: Felt252Wrapper) -> Option<(TxType, Vec)> { + // Find our tx and it's index + let (tx_index, tx) = extrinsics.into_iter().enumerate().find(|(_, xt)| { + let computed_tx_hash = match &xt.function { + RuntimeCall::Starknet( invoke { transaction }) => transaction.compute_hash::<::SystemHash>(chain_id, false), + RuntimeCall::Starknet( declare { transaction, .. }) => transaction.compute_hash::<::SystemHash>(chain_id, false), + RuntimeCall::Starknet( deploy_account { transaction }) => transaction.compute_hash::<::SystemHash>(chain_id, false), + RuntimeCall::Starknet( consume_l1_message { transaction, .. }) => transaction.compute_hash::<::SystemHash>(chain_id, false), + _ => return false + }; + + computed_tx_hash == tx_hash + })?; + + // Compute it's tx type + let tx_type = match tx.function { + RuntimeCall::Starknet( invoke { .. }) => TxType::Invoke, + RuntimeCall::Starknet( declare { .. }) => TxType::Declare, + RuntimeCall::Starknet( deploy_account { .. }) => TxType::DeployAccount, + RuntimeCall::Starknet( consume_l1_message { .. }) => TxType::L1Handler, + _ => panic!("The previous match made sure that at this point tx is one of those starknet calls"), + }; + + + // Skip all the events that are not related to our tx + let event_iter = System::read_events_no_consensus().filter_map(|event| { + match *event { + EventRecord { event: RuntimeEvent::Starknet(Event::StarknetEvent(event)), phase, .. } => Some((phase, event)), + _ => None, + } + }).skip_while(|(phase, _)| { + let index = match phase { + Phase::ApplyExtrinsic(idx) => *idx, + _ => return true + }; + + tx_index as u32 != index + }); + + // Collect all the events related to our tx + // Event from the same transaction are stored one after another + // so we can use take_while rather and early exit rather than filtering + let events = event_iter.take_while(|(phase, _)| { + let index = match phase { + Phase::ApplyExtrinsic(idx) => *idx, + _ => panic!("The previous iteration made sure at this point phase is of ApplyExtrinsic variant"), + }; + + tx_index as u32 == index + }).map(|(_, event)| event).collect(); + + Some((tx_type, events)) + } + + fn get_tx_execution_outcome(tx_hash: TransactionHash) -> Option> { + Starknet::tx_revert_error(tx_hash).map(|s| s.into_bytes()) + } + } + + impl pallet_starknet::runtime_api::ConvertTransactionRuntimeApi for Runtime { + fn convert_transaction(transaction: UserTransaction) -> Result { + let call = match transaction { + UserTransaction::Declare(tx, contract_class) => { + pallet_starknet::Call::declare { transaction: tx, contract_class } + } + UserTransaction::DeployAccount(tx) => { + pallet_starknet::Call::deploy_account { transaction: tx } + } + UserTransaction::Invoke(tx) => { + pallet_starknet::Call::invoke { transaction: tx } + } + }; + + Ok(UncheckedExtrinsic::new_unsigned(call.into())) + } + + fn convert_error(error: DispatchError) -> StarknetTransactionExecutionError { + if error == PalletError::::ContractNotFound.into() { + return StarknetTransactionExecutionError::ContractNotFound; + } + if error == PalletError::::ClassHashAlreadyDeclared.into() { + return StarknetTransactionExecutionError::ClassAlreadyDeclared; + } + if error == PalletError::::ContractClassHashUnknown.into() { + return StarknetTransactionExecutionError::ClassHashNotFound; + } + if error == PalletError::::InvalidContractClass.into() { + return StarknetTransactionExecutionError::InvalidContractClass; + } + + StarknetTransactionExecutionError::ContractError + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{baseline, Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + use frame_system_benchmarking::Pallet as SystemBench; + use baseline::Pallet as BaselineBench; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + + (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{baseline, Benchmarking, BenchmarkBatch, TrackedStorageKey}; + + use frame_system_benchmarking::Pallet as SystemBench; + use baseline::Pallet as BaselineBench; + + impl frame_system_benchmarking::Config for Runtime {} + impl baseline::Config for Runtime {} + + use frame_support::traits::WhitelistedStorageKeys; + let whitelist: Vec = AllPalletsWithSystem::whitelisted_storage_keys(); + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + add_benchmarks!(params, batches); + + Ok(batches) + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade() -> (Weight, Weight) { + // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to + // have a backtrace here. If any of the pre/post migration checks fail, we shall stop + // right here and right now. + let weight = Executive::try_runtime_upgrade().unwrap(); + (weight, BlockWeights::get().max_block) + } + + fn execute_block( + block: Block, + state_root_check: bool, + select: frame_try_runtime::TryStateSelect + ) -> Weight { + // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to + // have a backtrace here. + Executive::try_execute_block(block, state_root_check, select).expect("execute-block failed") + } + } +} diff --git a/crates/runtime/src/opaque.rs b/crates/runtime/madara-hotstuff/src/opaque.rs similarity index 95% rename from crates/runtime/src/opaque.rs rename to crates/runtime/madara-hotstuff/src/opaque.rs index d5b1d89e3d..ad6dfd820c 100644 --- a/crates/runtime/src/opaque.rs +++ b/crates/runtime/madara-hotstuff/src/opaque.rs @@ -22,7 +22,7 @@ pub use sp_runtime::{Perbill, Permill}; use sp_std::prelude::*; use super::*; -use crate::{Aura, BlockNumber, Grandpa, Hotstuff}; +use crate::{Aura, BlockNumber, Hotstuff}; /// Opaque block header type. pub type Header = generic::Header; /// Opaque block type. @@ -33,7 +33,6 @@ pub type BlockId = generic::BlockId; impl_opaque_keys! { pub struct SessionKeys { pub aura: Aura, - pub grandpa: Grandpa, pub hotstuff: Hotstuff, } } diff --git a/crates/runtime/madara-hotstuff/src/pallets.rs b/crates/runtime/madara-hotstuff/src/pallets.rs new file mode 100644 index 0000000000..344a54cb78 --- /dev/null +++ b/crates/runtime/madara-hotstuff/src/pallets.rs @@ -0,0 +1,175 @@ +//! Configuration of the pallets used in the runtime. +//! The pallets used in the runtime are configured here. +//! This file is used to generate the `construct_runtime!` macro. +pub use frame_support::traits::{ + ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, KeyOwnerProofSystem, OnTimestampSet, Randomness, StorageInfo, +}; +pub use frame_support::weights::constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, +}; +pub use frame_support::weights::{IdentityFee, Weight}; +pub use frame_support::{construct_runtime, parameter_types, StorageValue}; +pub use frame_system::Call as SystemCall; +use hotstuff_primitives::AuthorityId as HotstuffId; +pub use mp_chain_id::SN_GOERLI_CHAIN_ID; +pub use pallet_hotstuff; +/// Import the StarkNet pallet. +pub use pallet_starknet; +pub use pallet_timestamp::Call as TimestampCall; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_runtime::generic; +use sp_runtime::traits::{AccountIdLookup, BlakeTwo256}; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; +use sp_std::marker::PhantomData; + +use crate::*; + +// Configure FRAME pallets to include in runtime. + +// -------------------------------------- +// CUSTOM PALLETS +// -------------------------------------- + +/// Configure the Starknet pallet in pallets/starknet. +impl pallet_starknet::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SystemHash = StarknetHasher; + type TimestampProvider = Timestamp; + type UnsignedPriority = UnsignedPriority; + type TransactionLongevity = TransactionLongevity; + #[cfg(not(feature = "disable-transaction-fee"))] + type DisableTransactionFee = ConstBool; + #[cfg(feature = "disable-transaction-fee")] + type DisableTransactionFee = ConstBool; + type DisableNonceValidation = ConstBool; + type InvokeTxMaxNSteps = InvokeTxMaxNSteps; + type ValidateMaxNSteps = ValidateMaxNSteps; + type ProtocolVersion = ProtocolVersion; + type ChainId = ChainId; + type MaxRecursionDepth = MaxRecursionDepth; +} + +/// -------------------------------------- +/// FRAME SYSTEM PALLET +/// -------------------------------------- + +/// Configuration of `frame_system` pallet. +impl frame_system::Config for Runtime { + /// The basic call filter to use in dispatchable. + type BaseCallFilter = frame_support::traits::Everything; + /// Block & extrinsics weights: base values and limits. + type BlockWeights = BlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = BlockLength; + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = AccountIdLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// The weight of database operations that the runtime can invoke. + type DbWeight = RocksDbWeight; + /// Version of the runtime. + type Version = Version; + /// Converts a module to the index of the module in `construct_runtime!`. + /// + /// This type is being generated by `construct_runtime!`. + type PalletInfo = PalletInfo; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The data to be stored in an account. + type AccountData = (); + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = SS58Prefix; + /// The set code logic, just the default since we're not a parachain. + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +// -------------------------------------- +// CONSENSUS RELATED FRAME PALLETS +// -------------------------------------- +// Notes: +// Aura is the consensus algorithm used for block production. +// Hotstuff is the consensus algorithm used for block finalization. +// We want to support multiple flavors of consensus algorithms. +// Specifically we want to implement some proposals defined in the Starknet community forum. +// For more information see: https://community.starknet.io/t/starknet-decentralized-protocol-i-introduction/2671 +// You can also follow this issue on github: https://github.com/keep-starknet-strange/madara/issues/83 + +/// Authority-based consensus protocol used for block production. +/// TODO: Comment and explain the rationale behind the configuration items. +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<32>; +} + +/// -------------------------------------- +/// OTHER 3RD PARTY FRAME PALLETS +/// -------------------------------------- + +/// Timestamp manipulation. +/// For instance, we need it to set the timestamp of the Starknet block. +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = ConsensusOnTimestampSet; + type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; + type WeightInfo = (); +} + +/// Hotstuff consensus pallet +impl pallet_hotstuff::Config for Runtime { + type AuthorityId = HotstuffId; + + type DisabledValidators = (); + type MaxAuthorities = ConstU32<32>; + type AllowMultipleBlocksPerSlot = ConstBool; + + type WeightInfo = (); +} + +parameter_types! { + pub const UnsignedPriority: u64 = 1 << 20; + pub const TransactionLongevity: u64 = u64::MAX; + pub const InvokeTxMaxNSteps: u32 = 1_000_000; + pub const ValidateMaxNSteps: u32 = 1_000_000; + pub const ProtocolVersion: u8 = 0; + pub const ChainId: Felt252Wrapper = SN_GOERLI_CHAIN_ID; + pub const MaxRecursionDepth: u32 = 50; +} + +/// Implement the OnTimestampSet trait to override the default Aura. +/// This is needed to suppress Aura validations in case of non-default sealing. +pub struct ConsensusOnTimestampSet(PhantomData); +impl OnTimestampSet for ConsensusOnTimestampSet { + fn on_timestamp_set(moment: T::Moment) { + if Sealing::get() != SealingMode::Default { + return; + } + as OnTimestampSet>::on_timestamp_set(moment) + } +} diff --git a/crates/runtime/src/runtime_tests.rs b/crates/runtime/madara-hotstuff/src/runtime_tests.rs similarity index 100% rename from crates/runtime/src/runtime_tests.rs rename to crates/runtime/madara-hotstuff/src/runtime_tests.rs diff --git a/crates/runtime/src/types.rs b/crates/runtime/madara-hotstuff/src/types.rs similarity index 100% rename from crates/runtime/src/types.rs rename to crates/runtime/madara-hotstuff/src/types.rs diff --git a/crates/runtime/Cargo.toml b/crates/runtime/madara/Cargo.toml similarity index 100% rename from crates/runtime/Cargo.toml rename to crates/runtime/madara/Cargo.toml diff --git a/crates/runtime/madara/build.rs b/crates/runtime/madara/build.rs new file mode 100644 index 0000000000..5327cc8899 --- /dev/null +++ b/crates/runtime/madara/build.rs @@ -0,0 +1,5 @@ +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new().with_current_project().export_heap_base().import_memory().build() +} diff --git a/crates/runtime/madara/src/config.rs b/crates/runtime/madara/src/config.rs new file mode 100644 index 0000000000..9e568f326d --- /dev/null +++ b/crates/runtime/madara/src/config.rs @@ -0,0 +1,115 @@ +//! Configuration of the runtime. +pub use frame_support::traits::{ConstU128, ConstU32, ConstU64, ConstU8, KeyOwnerProofSystem, Randomness, StorageInfo}; +pub use frame_support::weights::constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, +}; +pub use frame_support::weights::{IdentityFee, Weight}; +pub use frame_support::{construct_runtime, parameter_types, StorageValue}; +pub use frame_system::Call as SystemCall; +use parity_scale_codec::{Decode, Encode}; +use serde::{Deserialize, Serialize}; +use sp_core::RuntimeDebug; +use sp_runtime::create_runtime_str; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +use crate::{BlockNumber, RUNTIME_API_VERSIONS}; + +/// This determines the average expected block time that we are targeting. +/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. +/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked +/// up by `pallet_aura` to implement `fn slot_duration()`. +/// +/// Change this to adjust the block time. +/// a.k.a `BLOCK_TIME` +pub const MILLISECS_PER_BLOCK: u64 = 6000; + +// NOTE: Currently it is not possible to change the slot duration after the chain has started. +// Attempting to do so will brick block production. +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + +// To learn more about runtime versioning, see: +// https://docs.substrate.io/main-docs/build/upgrade#runtime-versioning +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("madara"), + impl_name: create_runtime_str!("madara"), + authoring_version: 1, + // The version of the runtime specification. A full node will not attempt to use its native + // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, + // `spec_version`, and `authoring_version` are the same between Wasm and native. + // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use + // the compatible custom types. + spec_version: 100, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } +} + +// Time is measured by number of blocks. +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// Existential deposit. +pub const EXISTENTIAL_DEPOSIT: u128 = 500; + +parameter_types! { + pub const BlockHashCount: BlockNumber = 2400; + pub const Version: RuntimeVersion = VERSION; + /// We allow for 2 seconds of compute with a 6 second average block time. + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::with_sensible_defaults( + Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), + NORMAL_DISPATCH_RATIO, + ); + pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength + ::max_with_normal_ratio(20 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub const SS58Prefix: u8 = 42; +} + +/// The current sealing mode being used. This is needed for the runtime to adjust its behavior +/// accordingly, e.g. suppress Aura validations in `OnTimestampSet` for manual or instant sealing. +#[derive(Default, Clone, PartialEq, Decode, Encode, RuntimeDebug, Deserialize, Serialize)] +pub enum SealingMode { + #[default] + Default, + Manual, + Instant { + finalize: bool, + }, +} + +impl SealingMode { + pub fn is_default(&self) -> bool { + matches!(self, SealingMode::Default) + } +} + +#[cfg(feature = "std")] +impl std::fmt::Display for SealingMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SealingMode::Default => write!(f, "Default"), + SealingMode::Manual => write!(f, "Manual"), + SealingMode::Instant { finalize } => write!(f, "Instant (finalize: {})", finalize), + } + } +} + +// This storage item will be used to check if we are in the manual sealing mode +parameter_types! { + pub storage Sealing: SealingMode = SealingMode::default(); +} diff --git a/crates/runtime/src/lib.rs b/crates/runtime/madara/src/lib.rs similarity index 97% rename from crates/runtime/src/lib.rs rename to crates/runtime/madara/src/lib.rs index 3a92301691..9a6c4e66e7 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/madara/src/lib.rs @@ -27,13 +27,10 @@ pub use frame_support::weights::{IdentityFee, Weight}; pub use frame_support::{construct_runtime, parameter_types, StorageValue}; pub use frame_system::Call as SystemCall; use frame_system::{EventRecord, Phase}; -use hotstuff_primitives::AuthorityId as HotstuffId; use mp_felt::Felt252Wrapper; use mp_transactions::compute_hash::ComputeTransactionHash; use mp_transactions::{Transaction, TxType, UserTransaction}; use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList}; -// Hotstuff consensus authority id. -pub use pallet_hotstuff; /// Import the StarkNet pallet. pub use pallet_starknet; use pallet_starknet::pallet::Error as PalletError; @@ -74,8 +71,6 @@ construct_runtime!( Grandpa: pallet_grandpa, // Include Starknet pallet. Starknet: pallet_starknet, - // Include hotstuff consensus pallet - Hotstuff: pallet_hotstuff, } ); @@ -236,17 +231,6 @@ impl_runtime_apis! { } } - // hotstuff runtime api - impl hotstuff_primitives::HotstuffApi for Runtime { - fn slot_duration() -> hotstuff_primitives::SlotDuration { - hotstuff_primitives::SlotDuration::from_millis(Hotstuff::slot_duration()) - } - - fn authorities() -> Vec { - Hotstuff::authorities().into_inner() - } - } - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account) diff --git a/crates/runtime/madara/src/opaque.rs b/crates/runtime/madara/src/opaque.rs new file mode 100644 index 0000000000..35918bb9b3 --- /dev/null +++ b/crates/runtime/madara/src/opaque.rs @@ -0,0 +1,38 @@ +// A few exports that help ease life for downstream crates. +pub use frame_support::traits::{ConstU128, ConstU32, ConstU64, ConstU8, KeyOwnerProofSystem, Randomness, StorageInfo}; +pub use frame_support::weights::constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, +}; +pub use frame_support::weights::{IdentityFee, Weight}; +pub use frame_support::{construct_runtime, parameter_types, StorageValue}; +pub use frame_system::Call as SystemCall; +/// Import the StarkNet pallet. +pub use pallet_starknet; +pub use pallet_timestamp::Call as TimestampCall; +use sp_runtime::traits::BlakeTwo256; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; +use sp_runtime::{generic, impl_opaque_keys}; +pub use sp_runtime::{Perbill, Permill}; +use sp_std::prelude::*; + +use super::*; +use crate::{Aura, BlockNumber, Grandpa}; +/// Opaque block header type. +pub type Header = generic::Header; +/// Opaque block type. +pub type Block = generic::Block; +/// Opaque block identifier type. +pub type BlockId = generic::BlockId; + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + pub grandpa: Grandpa, + } +} diff --git a/crates/runtime/src/pallets.rs b/crates/runtime/madara/src/pallets.rs similarity index 95% rename from crates/runtime/src/pallets.rs rename to crates/runtime/madara/src/pallets.rs index 3d48a7b95a..b619c46702 100644 --- a/crates/runtime/src/pallets.rs +++ b/crates/runtime/madara/src/pallets.rs @@ -10,9 +10,7 @@ pub use frame_support::weights::constants::{ pub use frame_support::weights::{IdentityFee, Weight}; pub use frame_support::{construct_runtime, parameter_types, StorageValue}; pub use frame_system::Call as SystemCall; -use hotstuff_primitives::AuthorityId as HotstuffId; pub use mp_chain_id::SN_GOERLI_CHAIN_ID; -pub use pallet_hotstuff; /// Import the StarkNet pallet. pub use pallet_starknet; pub use pallet_timestamp::Call as TimestampCall; @@ -154,17 +152,6 @@ impl pallet_timestamp::Config for Runtime { type WeightInfo = (); } -/// Hotstuff consensus pallet -impl pallet_hotstuff::Config for Runtime { - type AuthorityId = HotstuffId; - - type DisabledValidators = (); - type MaxAuthorities = ConstU32<32>; - type AllowMultipleBlocksPerSlot = ConstBool; - - type WeightInfo = (); -} - parameter_types! { pub const UnsignedPriority: u64 = 1 << 20; pub const TransactionLongevity: u64 = u64::MAX; diff --git a/crates/runtime/madara/src/runtime_tests.rs b/crates/runtime/madara/src/runtime_tests.rs new file mode 100644 index 0000000000..97d70dd07a --- /dev/null +++ b/crates/runtime/madara/src/runtime_tests.rs @@ -0,0 +1,27 @@ +//! Tests for the runtime. + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use sp_core::hexdisplay::HexDisplay; + + use crate::sp_api_hidden_includes_construct_runtime::hidden_include::traits::WhitelistedStorageKeys; + use crate::*; + #[test] + fn check_whitelist() { + let whitelist: HashSet = AllPalletsWithSystem::whitelisted_storage_keys() + .iter() + .map(|e| HexDisplay::from(&e.key).to_string()) + .collect(); + + // Block Number + assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac")); + // Execution Phase + assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a")); + // Event Count + assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850")); + // System Events + assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7")); + } +} diff --git a/crates/runtime/madara/src/types.rs b/crates/runtime/madara/src/types.rs new file mode 100644 index 0000000000..37e6c5f467 --- /dev/null +++ b/crates/runtime/madara/src/types.rs @@ -0,0 +1,31 @@ +//! Common types used in the runtime. +//! This file is the canonical source of truth for the types used in the runtime. + +use sp_runtime::traits::{IdentifyAccount, Verify}; +use sp_runtime::MultiSignature; + +/// An index to a block. +pub type BlockNumber = u32; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Balance of an account. +pub type Balance = u128; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; + +/// The maximum amount of steps allowed for an invoke transaction. (?) +pub type InvokeTxMaxNSteps = u32; +/// The maximum amount of steps allowed for validation. (?) +pub type ValidateMaxNSteps = u32; + +pub type StarknetHasher = mp_hashers::pedersen::PedersenHasher; From 97410f0b80de2621ab1595a744b6b22ceb39bd19 Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Sun, 29 Oct 2023 19:34:42 +0800 Subject: [PATCH 04/12] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9440a38c99..b8887b60ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ madara node - feat(cache-option): add an option to enable aggressive caching in command-line parameters +- feat: Added a `madara-hotstuff-runtime` runtime that replaces the Grandpa pallet with the HotStuff pallet +- feat: Added two features, `madara-hotstuff-runtime` and `madara-runtime`, which are used to select between HotStuff and Grandpa consensus at compile-time. Running `cargo build --features madara-hotstuff-runtime` will use HotStuff consensus to replace Grandpa consensus, while using `cargo build --features madara-runtime` will use the original runtime with Grandpa consensus ## v0.4.0 From 58d10ec264affd79a3854d39bc5f5762460ed683 Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Sun, 29 Oct 2023 19:42:50 +0800 Subject: [PATCH 05/12] lint CHANGELOG.md --- CHANGELOG.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8887b60ee..1b4d5a8da8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,8 +44,14 @@ madara node - feat(cache-option): add an option to enable aggressive caching in command-line parameters -- feat: Added a `madara-hotstuff-runtime` runtime that replaces the Grandpa pallet with the HotStuff pallet -- feat: Added two features, `madara-hotstuff-runtime` and `madara-runtime`, which are used to select between HotStuff and Grandpa consensus at compile-time. Running `cargo build --features madara-hotstuff-runtime` will use HotStuff consensus to replace Grandpa consensus, while using `cargo build --features madara-runtime` will use the original runtime with Grandpa consensus +- feat: Added a `madara-hotstuff-runtime` runtime that replaces the Grandpa + pallet with the HotStuff pallet +- feat: Added two features, `madara-hotstuff-runtime` and `madara-runtime`, + which are used to select between HotStuff and Grandpa consensus at + compile-time. Running `cargo build --features madara-hotstuff-runtime` will + use HotStuff consensus to replace Grandpa consensus, while using + `cargo build --features madara-runtime` will use the original runtime with + Grandpa consensus ## v0.4.0 From d4c7ed598455972086620be4d3dc7287c653f422 Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Sun, 29 Oct 2023 19:55:06 +0800 Subject: [PATCH 06/12] fix lint --- Cargo.toml | 4 ++-- crates/node/Cargo.toml | 4 ++-- crates/runtime/madara-hotstuff/Cargo.toml | 2 +- crates/runtime/madara/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ce0daa485..1a9f7fb82e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -203,8 +203,8 @@ cairo-lang-casm = { git = "https://github.com/keep-starknet-strange/cairo.git", cairo-lang-utils = { git = "https://github.com/keep-starknet-strange/cairo.git", branch = "no_std-support-8bbf530", default-features = false } # Hotstuff consensus -hotstuff-consensus = { git = "https://github.com/Generative-Labs/hotstuff.git", branch = "polkadot-v0.9.43"} -hotstuff-primitives = { git = "https://github.com/Generative-Labs/hotstuff.git",branch = "polkadot-v0.9.43", default-features = false } +hotstuff-consensus = { git = "https://github.com/Generative-Labs/hotstuff.git", branch = "polkadot-v0.9.43" } +hotstuff-primitives = { git = "https://github.com/Generative-Labs/hotstuff.git", branch = "polkadot-v0.9.43", default-features = false } pallet-hotstuff = { git = "https://github.com/Generative-Labs/hotstuff.git", branch = "polkadot-v0.9.43", default-features = false } # Other third party dependencies diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index a95b7de191..64737d12df 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -74,8 +74,8 @@ frame-benchmarking-cli = { workspace = true } # Starknet blockifier = { workspace = true } hex = { workspace = true } -madara-runtime = { workspace = true } madara-hotstuff-runtime = { workspace = true } +madara-runtime = { workspace = true } mc-block-proposer = { workspace = true } mc-data-availability = { workspace = true } mc-db = { workspace = true } @@ -100,8 +100,8 @@ serde_json = { workspace = true } url = { workspace = true } # Hotstuff dependencies -hotstuff-primitives = { workspace = true } hotstuff-consensus = { workspace = true } +hotstuff-primitives = { workspace = true } [build-dependencies] substrate-build-script-utils = { workspace = true } diff --git a/crates/runtime/madara-hotstuff/Cargo.toml b/crates/runtime/madara-hotstuff/Cargo.toml index b4fd8293c3..165dfb6b12 100644 --- a/crates/runtime/madara-hotstuff/Cargo.toml +++ b/crates/runtime/madara-hotstuff/Cargo.toml @@ -61,7 +61,7 @@ starknet-ff = { workspace = true } starknet_api = { workspace = true } # Hotstuff dependencies -hotstuff-primitives = { workspace = true } +hotstuff-primitives = { workspace = true } pallet-hotstuff = { workspace = true } [build-dependencies] diff --git a/crates/runtime/madara/Cargo.toml b/crates/runtime/madara/Cargo.toml index eeb7da5d78..c1804ff9fe 100644 --- a/crates/runtime/madara/Cargo.toml +++ b/crates/runtime/madara/Cargo.toml @@ -62,7 +62,7 @@ starknet-ff = { workspace = true } starknet_api = { workspace = true } # Hotstuff dependencies -hotstuff-primitives = { workspace = true } +hotstuff-primitives = { workspace = true } pallet-hotstuff = { workspace = true } [build-dependencies] From 5abe0224f73ee8afdb3397da6a64a33175ef3f34 Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Sun, 29 Oct 2023 19:56:12 +0800 Subject: [PATCH 07/12] update github workflow rust build features --- .github/workflows/rust-build-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust-build-test.yml b/.github/workflows/rust-build-test.yml index c3ebac95f0..14945712bd 100644 --- a/.github/workflows/rust-build-test.yml +++ b/.github/workflows/rust-build-test.yml @@ -34,7 +34,7 @@ jobs: - name: Build the project run: | - cargo build --release --workspace + cargo build --release --features madara-runtime --workspace - name: Run integration tests - run: cargo test --release + run: cargo test --features madara-runtime --release From 4b6c155edf737c0976d7a656f1e6adbfb0b342a6 Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Thu, 2 Nov 2023 19:46:07 +0800 Subject: [PATCH 08/12] improve runtime compile features --- Cargo.lock | 6 +- crates/node/Cargo.toml | 4 +- crates/node/src/benchmarking.rs | 4 +- crates/node/src/chain_spec.rs | 8 +- crates/node/src/command.rs | 4 +- crates/node/src/commands/run.rs | 4 +- crates/node/src/service.rs | 149 ++++++++------------------------ crates/node/src/starknet.rs | 4 +- 8 files changed, 50 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b0e654ae7..6c07aa1437 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4562,7 +4562,7 @@ dependencies = [ [[package]] name = "hotstuff-consensus" version = "0.1.0" -source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#ec7b9bbc6430ba53ea755359f5089d2a455bf1c8" +source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#28e2dd5e1bad3424f599968eb07e362114582819" dependencies = [ "array-bytes 6.1.0", "async-recursion", @@ -4599,7 +4599,7 @@ dependencies = [ [[package]] name = "hotstuff-primitives" version = "0.1.0" -source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#ec7b9bbc6430ba53ea755359f5089d2a455bf1c8" +source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#28e2dd5e1bad3424f599968eb07e362114582819" dependencies = [ "async-trait", "parity-scale-codec", @@ -7631,7 +7631,7 @@ dependencies = [ [[package]] name = "pallet-hotstuff" version = "4.0.0-dev" -source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#ec7b9bbc6430ba53ea755359f5089d2a455bf1c8" +source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#28e2dd5e1bad3424f599968eb07e362114582819" dependencies = [ "frame-benchmarking", "frame-support", diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 64737d12df..f7fc5b71c7 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -122,6 +122,4 @@ disable-transaction-fee = ["madara-runtime/disable-transaction-fee"] # This is the way to run a sharingan chain sharingan = [] -madara-runtime = [] - -madara-hotstuff-runtime = [] +with-hotstuff-runtime = [] diff --git a/crates/node/src/benchmarking.rs b/crates/node/src/benchmarking.rs index 5ecbba09f8..9cd475078a 100644 --- a/crates/node/src/benchmarking.rs +++ b/crates/node/src/benchmarking.rs @@ -23,9 +23,9 @@ use std::sync::Arc; use std::time::Duration; -#[cfg(feature = "madara-hotstuff-runtime")] +#[cfg(feature = "with-hotstuff-runtime")] use madara_hotstuff_runtime as runtime; -#[cfg(feature = "madara-runtime")] +#[cfg(not(feature = "with-hotstuff-runtime"))] use madara_runtime as runtime; use runtime::SystemCall; use sc_cli::Result; diff --git a/crates/node/src/chain_spec.rs b/crates/node/src/chain_spec.rs index 1806ba8004..741bdeaf71 100644 --- a/crates/node/src/chain_spec.rs +++ b/crates/node/src/chain_spec.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use hotstuff_primitives::AuthorityId as HotStuffId; -#[cfg(feature = "madara-hotstuff-runtime")] +#[cfg(feature = "with-hotstuff-runtime")] use madara_hotstuff_runtime::{AuraConfig, GenesisConfig, HotstuffConfig, SealingMode, SystemConfig, WASM_BINARY}; -#[cfg(feature = "madara-runtime")] +#[cfg(not(feature = "with-hotstuff-runtime"))] use madara_runtime::{AuraConfig, GenesisConfig, GrandpaConfig, SealingMode, SystemConfig, WASM_BINARY}; use mp_felt::Felt252Wrapper; use pallet_starknet::genesis_loader::{GenesisData, GenesisLoader, HexFelt}; @@ -185,11 +185,11 @@ fn testnet_genesis( // Authority-based consensus protocol used for block production aura: AuraConfig { authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect() }, // Deterministic finality mechanism used for block finalization - #[cfg(feature = "madara-runtime")] + #[cfg(not(feature = "with-hotstuff-runtime"))] grandpa: GrandpaConfig { authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect() }, /// Starknet Genesis configuration. starknet: starknet_genesis_config, - #[cfg(feature = "madara-hotstuff-runtime")] + #[cfg(feature = "with-hotstuff-runtime")] hotstuff: HotstuffConfig { authorities: initial_authorities.iter().map(|x| (x.2.clone())).collect() }, } } diff --git a/crates/node/src/command.rs b/crates/node/src/command.rs index b0c4fe57ec..ff232bb88b 100644 --- a/crates/node/src/command.rs +++ b/crates/node/src/command.rs @@ -1,7 +1,7 @@ use frame_benchmarking_cli::{BenchmarkCmd, ExtrinsicFactory, SUBSTRATE_REFERENCE_HARDWARE}; -#[cfg(feature = "madara-hotstuff-runtime")] +#[cfg(feature = "with-hotstuff-runtime")] use madara_hotstuff_runtime::Block; -#[cfg(feature = "madara-runtime")] +#[cfg(not(feature = "with-hotstuff-runtime"))] use madara_runtime::Block; use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; diff --git a/crates/node/src/commands/run.rs b/crates/node/src/commands/run.rs index 37cba450df..e8ccf9f6b5 100644 --- a/crates/node/src/commands/run.rs +++ b/crates/node/src/commands/run.rs @@ -1,8 +1,8 @@ use std::path::PathBuf; -#[cfg(feature = "madara-hotstuff-runtime")] +#[cfg(feature = "with-hotstuff-runtime")] use madara_hotstuff_runtime::SealingMode; -#[cfg(feature = "madara-runtime")] +#[cfg(not(feature = "with-hotstuff-runtime"))] use madara_runtime::SealingMode; use mc_data_availability::DaLayer; use sc_cli::{Result, RpcMethods, RunCmd, SubstrateCli}; diff --git a/crates/node/src/service.rs b/crates/node/src/service.rs index bf73159a1b..21cb91316a 100644 --- a/crates/node/src/service.rs +++ b/crates/node/src/service.rs @@ -9,11 +9,11 @@ use futures::channel::mpsc; use futures::future; use futures::future::BoxFuture; use futures::prelude::*; -#[cfg(feature = "madara-hotstuff-runtime")] +#[cfg(feature = "with-hotstuff-runtime")] use hotstuff_consensus::LinkHalf as HotstuffLinkHalf; -#[cfg(feature = "madara-hotstuff-runtime")] +#[cfg(feature = "with-hotstuff-runtime")] use madara_hotstuff_runtime::{self, opaque::Block, Hash, RuntimeApi, SealingMode, StarknetHasher, SLOT_DURATION}; -#[cfg(feature = "madara-runtime")] +#[cfg(not(feature = "with-hotstuff-runtime"))] use madara_runtime::{self, opaque::Block, Hash, RuntimeApi, SealingMode, StarknetHasher, SLOT_DURATION}; use mc_block_proposer::ProposerFactory; use mc_data_availability::avail::config::AvailConfig; @@ -33,7 +33,7 @@ use prometheus_endpoint::Registry; use sc_client_api::{Backend, BlockBackend, BlockchainEvents, HeaderBackend}; use sc_consensus::BasicQueue; use sc_consensus_aura::{SlotProportion, StartAuraParams}; -#[cfg(feature = "madara-runtime")] +#[cfg(not(feature = "with-hotstuff-runtime"))] use sc_consensus_grandpa::{LinkHalf as GrandpaLinkHalf, SharedVoterState}; pub use sc_executor::NativeElseWasmExecutor; use sc_service::error::Error as ServiceError; @@ -52,6 +52,25 @@ use crate::starknet::{db_config_dir, MadaraBackend}; // Our native executor instance. pub struct ExecutorDispatch; +#[cfg(feature = "with-hotstuff-runtime")] +impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { + /// Only enable the benchmarking host functions when we actually want to benchmark. + #[cfg(feature = "runtime-benchmarks")] + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + /// Otherwise we only use the default Substrate host functions. + #[cfg(not(feature = "runtime-benchmarks"))] + type ExtendHostFunctions = (); + + fn dispatch(method: &str, data: &[u8]) -> Option> { + madara_hotstuff_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + madara_hotstuff_runtime::native_version() + } +} + +#[cfg(not(feature = "with-hotstuff-runtime"))] impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { /// Only enable the benchmarking host functions when we actually want to benchmark. #[cfg(feature = "runtime-benchmarks")] @@ -61,16 +80,10 @@ impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { type ExtendHostFunctions = (); fn dispatch(method: &str, data: &[u8]) -> Option> { - #[cfg(feature = "madara-hotstuff-runtime")] - return madara_hotstuff_runtime::api::dispatch(method, data); - #[cfg(feature = "madara-runtime")] madara_runtime::api::dispatch(method, data) } fn native_version() -> sc_executor::NativeVersion { - #[cfg(feature = "madara-hotstuff-runtime")] - return madara_hotstuff_runtime::native_version(); - #[cfg(feature = "madara-runtime")] madara_runtime::native_version() } } @@ -84,9 +97,9 @@ type BoxBlockImport = sc_consensus::BoxBlockImport), - #[cfg(feature = "madara-hotstuff-runtime")] + #[cfg(feature = "with-hotstuff-runtime")] Link(HotstuffLinkHalf), None, @@ -197,100 +210,6 @@ where }) } -/// Build the import queue for the template runtime (aura + grandpa). -// pub fn build_aura_grandpa_import_queue( -// client: Arc, -// config: &Configuration, -// task_manager: &TaskManager, -// telemetry: Option, -// backend: Arc, -// _madara_backend: Arc, -// ) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> -// where -// RuntimeApi: ConstructRuntimeApi, -// RuntimeApi: Send + Sync + 'static, -// { -// let select_chain = sc_consensus::LongestChain::new(backend.clone()); - -// let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( -// client.clone(), -// &client as &Arc<_>, -// select_chain.clone(), -// telemetry.clone(), -// )?; - -// let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - -// let create_inherent_data_providers = move |_, ()| async move { -// let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); -// let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( -// *timestamp, -// slot_duration, -// ); -// Ok((slot, timestamp)) -// }; - -// let import_queue = -// sc_consensus_aura::import_queue::(sc_consensus_aura::ImportQueueParams { -// block_import: grandpa_block_import.clone(), -// justification_import: Some(Box::new(grandpa_block_import.clone())), -// client, -// create_inherent_data_providers, -// spawner: &task_manager.spawn_essential_handle(), -// registry: config.prometheus_registry(), -// check_for_equivocation: Default::default(), -// telemetry, -// compatibility_mode: sc_consensus_aura::CompatibilityMode::None, -// }) -// .map_err::(Into::into)?; - -// Ok((import_queue, Box::new(grandpa_block_import), ConsensusLinkHalf::Link(grandpa_link))) -// } - -// /// Build the import queue for the template runtime (aura + hotstuff). -// pub fn build_aura_hotstuff_import_queue( -// client: Arc, -// config: &Configuration, -// task_manager: &TaskManager, -// telemetry: Option, -// _backend: Arc, -// _madara_backend: Arc, -// ) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> -// where -// RuntimeApi: ConstructRuntimeApi, -// RuntimeApi: Send + Sync + 'static, -// { -// let (hotstuff_block_import, hotstuff_link) = hotstuff_consensus::block_import(client.clone(), -// &client as &Arc<_>)?; - -// let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - -// let create_inherent_data_providers = move |_, ()| async move { -// let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); -// let slot = -// sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( -// *timestamp, slot_duration, -// ); -// Ok((slot, timestamp)) -// }; - -// let import_queue = -// sc_consensus_aura::import_queue::(sc_consensus_aura::ImportQueueParams { block_import: -// hotstuff_block_import.clone(), justification_import: -// Some(Box::new(hotstuff_block_import.clone())), client, -// create_inherent_data_providers, -// spawner: &task_manager.spawn_essential_handle(), -// registry: config.prometheus_registry(), -// check_for_equivocation: Default::default(), -// telemetry, -// compatibility_mode: sc_consensus_aura::CompatibilityMode::None, -// }) -// .map_err::(Into::into)?; - -// Ok((import_queue, Box::new(hotstuff_block_import), -// ConsensusLinkHalf::Hotstuff(hotstuff_link))) } - /// Build the import queue for the template runtime (aura + hotstuff). pub fn build_common_import_queue( client: Arc, @@ -304,10 +223,10 @@ where RuntimeApi: ConstructRuntimeApi, RuntimeApi: Send + Sync + 'static, { - #[cfg(feature = "madara-hotstuff-runtime")] + #[cfg(feature = "with-hotstuff-runtime")] let (block_import, link) = hotstuff_consensus::block_import(client.clone(), &client as &Arc<_>)?; - #[cfg(feature = "madara-runtime")] + #[cfg(not(feature = "with-hotstuff-runtime"))] let (block_import, link) = sc_consensus_grandpa::block_import( client.clone(), &client as &Arc<_>, @@ -395,20 +314,20 @@ pub fn new_full( #[allow(unused_mut)] let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); - #[cfg(feature = "madara-runtime")] + #[cfg(not(feature = "with-hotstuff-runtime"))] let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), &config.chain_spec, ); - #[cfg(feature = "madara-hotstuff-runtime")] + #[cfg(feature = "with-hotstuff-runtime")] let hotstuff_protocol_name = hotstuff_consensus::config::standard_name( &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), &config.chain_spec, ); let warp_sync_params = if sealing.is_default() { - #[cfg(feature = "madara-runtime")] + #[cfg(not(feature = "with-hotstuff-runtime"))] if let ConsensusLinkHalf::Link(link) = &link { net_config.add_notification_protocol(sc_consensus_grandpa::grandpa_peers_set_config( grandpa_protocol_name.clone(), @@ -423,7 +342,7 @@ pub fn new_full( } else { None } - #[cfg(feature = "madara-hotstuff-runtime")] + #[cfg(feature = "with-hotstuff-runtime")] None } else { None @@ -448,7 +367,7 @@ pub fn new_full( let role = config.role.clone(); let force_authoring = config.force_authoring; let backoff_authoring_blocks: Option<()> = None; - #[cfg(feature = "madara-runtime")] + #[cfg(not(feature = "with-hotstuff-runtime"))] let name = config.network.node_name.clone(); let enable_grandpa = !config.disable_grandpa && sealing.is_default(); let prometheus_registry = config.prometheus_registry().cloned(); @@ -628,7 +547,7 @@ pub fn new_full( } if enable_grandpa { - #[cfg(feature = "madara-runtime")] + #[cfg(not(feature = "with-hotstuff-runtime"))] { // if the node isn't actively participating in consensus then it doesn't // need a keystore, regardless of which protocol we use below. @@ -673,7 +592,7 @@ pub fn new_full( ); } } - #[cfg(feature = "madara-hotstuff-runtime")] + #[cfg(feature = "with-hotstuff-runtime")] { if let ConsensusLinkHalf::Link(link) = link { let (voter, hotstuff_network) = hotstuff_consensus::consensus::start_hotstuff( diff --git a/crates/node/src/starknet.rs b/crates/node/src/starknet.rs index 701388e034..b23a0452ab 100644 --- a/crates/node/src/starknet.rs +++ b/crates/node/src/starknet.rs @@ -1,8 +1,8 @@ use std::path::PathBuf; -#[cfg(feature = "madara-hotstuff-runtime")] +#[cfg(not(feature = "with-hotstuff-runtime"))] use madara_hotstuff_runtime::opaque::Block; -#[cfg(feature = "madara-runtime")] +#[cfg(feature = "with-hotstuff-runtime")] use madara_runtime::opaque::Block; use sc_service::Configuration; From 4bbc00e0630ee388de0139d903af26d022a0ef5b Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Thu, 2 Nov 2023 21:59:55 +0800 Subject: [PATCH 09/12] lint changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5993411498..05d9490535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,7 +62,6 @@ `cargo build --features madara-runtime` will use the original runtime with Grandpa consensus - ## v0.4.0 - chore: release v0.4.0 From bc046d6c171dfd66cfb05fbef0544267923f8400 Mon Sep 17 00:00:00 2001 From: jerrybaoo Date: Tue, 21 Nov 2023 09:31:39 +0800 Subject: [PATCH 10/12] update hotstuff dependency --- Cargo.lock | 8 ++++---- Cargo.toml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33c58e0237..199f2ea747 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4594,7 +4594,7 @@ dependencies = [ [[package]] name = "hotstuff-consensus" version = "0.1.0" -source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#28e2dd5e1bad3424f599968eb07e362114582819" +source = "git+https://github.com/Generative-Labs/Substrate-HotStuff.git#2563de81e5e6e375566082a2043fb18a6d78fea9" dependencies = [ "array-bytes 6.1.0", "async-recursion", @@ -4631,7 +4631,7 @@ dependencies = [ [[package]] name = "hotstuff-primitives" version = "0.1.0" -source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#28e2dd5e1bad3424f599968eb07e362114582819" +source = "git+https://github.com/Generative-Labs/Substrate-HotStuff.git#2563de81e5e6e375566082a2043fb18a6d78fea9" dependencies = [ "async-trait", "parity-scale-codec", @@ -7688,8 +7688,8 @@ dependencies = [ [[package]] name = "pallet-hotstuff" -version = "4.0.0-dev" -source = "git+https://github.com/Generative-Labs/hotstuff.git?branch=polkadot-v0.9.43#28e2dd5e1bad3424f599968eb07e362114582819" +version = "0.1.0" +source = "git+https://github.com/Generative-Labs/Substrate-HotStuff.git#2563de81e5e6e375566082a2043fb18a6d78fea9" dependencies = [ "frame-benchmarking", "frame-support", diff --git a/Cargo.toml b/Cargo.toml index 683cae06cd..f9f2a0c09a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -209,9 +209,9 @@ cairo-lang-casm = { git = "https://github.com/keep-starknet-strange/cairo.git", cairo-lang-utils = { git = "https://github.com/keep-starknet-strange/cairo.git", branch = "no_std-support-8bbf530", default-features = false } # Hotstuff consensus -hotstuff-consensus = { git = "https://github.com/Generative-Labs/hotstuff.git", branch = "polkadot-v0.9.43" } -hotstuff-primitives = { git = "https://github.com/Generative-Labs/hotstuff.git", branch = "polkadot-v0.9.43", default-features = false } -pallet-hotstuff = { git = "https://github.com/Generative-Labs/hotstuff.git", branch = "polkadot-v0.9.43", default-features = false } +hotstuff-consensus = { git = "https://github.com/Generative-Labs/Substrate-HotStuff.git" } +hotstuff-primitives = { git = "https://github.com/Generative-Labs/Substrate-HotStuff.git", default-features = false } +pallet-hotstuff = { git = "https://github.com/Generative-Labs/Substrate-HotStuff.git", default-features = false } # Other third party dependencies anyhow = "1.0.75" From b1b150c1e0cd0cca73b3863a5d711d7344b8e8e9 Mon Sep 17 00:00:00 2001 From: jerrybaoo <61228234+jerrybaoo@users.noreply.github.com> Date: Tue, 5 Dec 2023 21:40:16 +0800 Subject: [PATCH 11/12] fix compile --- Cargo.lock | 3 +- crates/node/src/chain_spec.rs | 4 +- crates/node/src/service.rs | 73 +++++++++---------- crates/runtime/madara-hotstuff/Cargo.toml | 1 + crates/runtime/madara-hotstuff/src/lib.rs | 56 +++++++------- crates/runtime/madara-hotstuff/src/pallets.rs | 10 +-- crates/runtime/madara/Cargo.toml | 8 -- 7 files changed, 73 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c0a72f912..0e35acb503 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6538,6 +6538,7 @@ dependencies = [ "pallet-aura", "pallet-hotstuff", "pallet-starknet", + "pallet-starknet-runtime-api", "pallet-timestamp", "parity-scale-codec", "scale-info", @@ -6570,14 +6571,12 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "hotstuff-primitives", "mp-chain-id", "mp-felt", "mp-hashers", "mp-transactions", "pallet-aura", "pallet-grandpa", - "pallet-hotstuff", "pallet-starknet", "pallet-starknet-runtime-api", "pallet-timestamp", diff --git a/crates/node/src/chain_spec.rs b/crates/node/src/chain_spec.rs index 4d67efb1d2..d974f37009 100644 --- a/crates/node/src/chain_spec.rs +++ b/crates/node/src/chain_spec.rs @@ -2,7 +2,9 @@ use std::path::PathBuf; use hotstuff_primitives::AuthorityId as HotStuffId; #[cfg(feature = "with-hotstuff-runtime")] -use madara_hotstuff_runtime::{AuraConfig, GenesisConfig, HotstuffConfig, SealingMode, SystemConfig, WASM_BINARY}; +use madara_hotstuff_runtime::{ + AuraConfig, HotstuffConfig, RuntimeGenesisConfig, SealingMode, SystemConfig, WASM_BINARY, +}; #[cfg(not(feature = "with-hotstuff-runtime"))] use madara_runtime::{AuraConfig, GrandpaConfig, RuntimeGenesisConfig, SealingMode, SystemConfig, WASM_BINARY}; use mp_felt::Felt252Wrapper; diff --git a/crates/node/src/service.rs b/crates/node/src/service.rs index c45e529be9..2a1c9cf0bd 100644 --- a/crates/node/src/service.rs +++ b/crates/node/src/service.rs @@ -15,7 +15,6 @@ use hotstuff_consensus::LinkHalf as HotstuffLinkHalf; use madara_hotstuff_runtime::{self, opaque::Block, Hash, RuntimeApi, SealingMode, StarknetHasher, SLOT_DURATION}; #[cfg(not(feature = "with-hotstuff-runtime"))] use madara_runtime::{self, opaque::Block, Hash, RuntimeApi, SealingMode, StarknetHasher, SLOT_DURATION}; -use mc_block_proposer::ProposerFactory; use mc_commitment_state_diff::{log_commitment_state_diff, CommitmentStateDiffWorker}; use mc_data_availability::avail::config::AvailConfig; use mc_data_availability::avail::AvailClient; @@ -98,6 +97,7 @@ type BoxBlockImport = sc_consensus::BoxBlockImport; /// The minimum period of blocks on which justifications will be /// imported and generated. +#[cfg(not(feature = "with-hotstuff-runtime"))] const GRANDPA_JUSTIFICATION_PERIOD: u32 = 512; // Link half used in different consensus. @@ -120,14 +120,9 @@ pub fn new_partial( FullClient, FullBackend, FullSelectChain, - sc_consensus::DefaultImportQueue, + sc_consensus::DefaultImportQueue, sc_transaction_pool::FullPool, - ( - BoxBlockImport, - ConsensusLinkHalf, - Option, - Arc, - ), + (BoxBlockImport, ConsensusLinkHalf, Option, Arc), >, ServiceError, > @@ -141,7 +136,7 @@ where Option, Arc, Arc, - ) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError>,, + ) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError>, { let telemetry = config .telemetry_endpoints @@ -227,7 +222,7 @@ pub fn build_common_import_queue( telemetry: Option, #[allow(unused_variables)] backend: Arc, _madara_backend: Arc, -) -> -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> +) -> Result<(BasicImportQueue, BoxBlockImport, ConsensusLinkHalf), ServiceError> where RuntimeApi: ConstructRuntimeApi, RuntimeApi: Send + Sync + 'static, @@ -238,6 +233,7 @@ where #[cfg(not(feature = "with-hotstuff-runtime"))] let (block_import, link) = sc_consensus_grandpa::block_import( client.clone(), + GRANDPA_JUSTIFICATION_PERIOD, &client as &Arc<_>, sc_consensus::LongestChain::new(backend.clone()), telemetry.clone(), @@ -595,35 +591,36 @@ pub fn new_full( // need a keystore, regardless of which protocol we use below. let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; - let grandpa_config = sc_consensus_grandpa::Config { - // FIXME #1578 make this available through chainspec - gossip_duration: Duration::from_millis(333), - justification_generation_period: GRANDPA_JUSTIFICATION_PERIOD, - name: Some(name), - observer_enabled: false, - keystore, - local_role: role, - telemetry: telemetry.as_ref().map(|x| x.handle()), - protocol_name: grandpa_protocol_name, - }; + let grandpa_config = sc_consensus_grandpa::Config { + // FIXME #1578 make this available through chainspec + gossip_duration: Duration::from_millis(333), + justification_generation_period: GRANDPA_JUSTIFICATION_PERIOD, + name: Some(name), + observer_enabled: false, + keystore, + local_role: role, + telemetry: telemetry.as_ref().map(|x| x.handle()), + protocol_name: grandpa_protocol_name, + }; - // start the full GRANDPA voter - // NOTE: non-authorities could run the GRANDPA observer protocol, but at - // this point the full voter should provide better guarantees of block - // and vote data availability than the observer. The observer has not - // been tested extensively yet and having most nodes in a network run it - // could lead to finality stalls. - let grandpa_config = sc_consensus_grandpa::GrandpaParams { - config: grandpa_config, - link: grandpa_link, - network, - sync: Arc::new(sync_service), - voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), - prometheus_registry, - shared_voter_state: SharedVoterState::empty(), - telemetry: telemetry.as_ref().map(|x| x.handle()), - offchain_tx_pool_factory: OffchainTransactionPoolFactory::new(transaction_pool.clone()), - }; + if let ConsensusLinkHalf::Link(link) = link { + // start the full GRANDPA voter + // NOTE: non-authorities could run the GRANDPA observer protocol, but at + // this point the full voter should provide better guarantees of block + // and vote data availability than the observer. The observer has not + // been tested extensively yet and having most nodes in a network run it + // could lead to finality stalls. + let grandpa_config = sc_consensus_grandpa::GrandpaParams { + config: grandpa_config, + link, + network, + sync: Arc::new(sync_service), + voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), + prometheus_registry, + shared_voter_state: SharedVoterState::empty(), + telemetry: telemetry.as_ref().map(|x| x.handle()), + offchain_tx_pool_factory: OffchainTransactionPoolFactory::new(transaction_pool.clone()), + }; // the GRANDPA voter task is considered infallible, i.e. // if it fails we take down the service with it. diff --git a/crates/runtime/madara-hotstuff/Cargo.toml b/crates/runtime/madara-hotstuff/Cargo.toml index 165dfb6b12..31bea0d1cf 100644 --- a/crates/runtime/madara-hotstuff/Cargo.toml +++ b/crates/runtime/madara-hotstuff/Cargo.toml @@ -49,6 +49,7 @@ frame-system-benchmarking = { workspace = true, optional = true } # Madara Local Dependencies # Madara Pallets pallet-starknet = { workspace = true } +pallet-starknet-runtime-api = { workspace = true } # Madara Primitives mp-chain-id = { workspace = true } diff --git a/crates/runtime/madara-hotstuff/src/lib.rs b/crates/runtime/madara-hotstuff/src/lib.rs index 1a69ff1877..31c5b55482 100644 --- a/crates/runtime/madara-hotstuff/src/lib.rs +++ b/crates/runtime/madara-hotstuff/src/lib.rs @@ -30,15 +30,15 @@ use frame_system::{EventRecord, Phase}; use hotstuff_primitives::AuthorityId as HotstuffId; use mp_felt::Felt252Wrapper; use mp_transactions::compute_hash::ComputeTransactionHash; -use mp_transactions::{Transaction, TxType, UserTransaction}; +use mp_transactions::{Transaction, UserTransaction}; // Hotstuff consensus authority id. pub use pallet_hotstuff; /// Import the StarkNet pallet. pub use pallet_starknet; use pallet_starknet::pallet::Error as PalletError; -use pallet_starknet::runtime_api::StarknetTransactionExecutionError; use pallet_starknet::Call::{consume_l1_message, declare, deploy_account, invoke}; use pallet_starknet::{Config, Event}; +use pallet_starknet_runtime_api::StarknetTransactionExecutionError; pub use pallet_timestamp::Call as TimestampCall; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; @@ -61,12 +61,7 @@ pub use types::*; // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( - pub struct Runtime - where - Block = Block, - NodeBlock = opaque::Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { + pub struct Runtime { System: frame_system, Timestamp: pallet_timestamp, Aura: pallet_aura, @@ -221,7 +216,7 @@ impl_runtime_apis! { } } - impl pallet_starknet::runtime_api::StarknetRuntimeApi for Runtime { + impl pallet_starknet_runtime_api::StarknetRuntimeApi for Runtime { fn get_storage_at(address: ContractAddress, key: StorageKey) -> Result { Starknet::get_storage_at(address, key) @@ -247,8 +242,12 @@ impl_runtime_apis! { Starknet::chain_id() } - fn estimate_fee(transaction: UserTransaction, is_query: bool) -> Result<(u64, u64), DispatchError> { - Starknet::estimate_fee(transaction, is_query) + fn is_transaction_fee_disabled() -> bool { + Starknet::is_transaction_fee_disabled() + } + + fn estimate_fee(transactions: Vec) -> Result, DispatchError> { + Starknet::estimate_fee(transactions) } fn get_starknet_events_and_their_associated_tx_hash(block_extrinsics: Vec<::Extrinsic>, chain_id: Felt252Wrapper) -> Vec<(Felt252Wrapper, StarknetEvent)> { @@ -286,7 +285,7 @@ impl_runtime_apis! { }).collect::>() } - fn get_events_for_tx_hash(extrinsics: Vec<::Extrinsic>, chain_id: Felt252Wrapper, tx_hash: Felt252Wrapper) -> Option<(TxType, Vec)> { + fn get_index_and_tx_for_tx_hash(extrinsics: Vec<::Extrinsic>, chain_id: Felt252Wrapper, tx_hash: Felt252Wrapper) -> Option<(u32, Transaction)> { // Find our tx and it's index let (tx_index, tx) = extrinsics.into_iter().enumerate().find(|(_, xt)| { let computed_tx_hash = match &xt.function { @@ -299,16 +298,19 @@ impl_runtime_apis! { computed_tx_hash == tx_hash })?; - - // Compute it's tx type - let tx_type = match tx.function { - RuntimeCall::Starknet( invoke { .. }) => TxType::Invoke, - RuntimeCall::Starknet( declare { .. }) => TxType::Declare, - RuntimeCall::Starknet( deploy_account { .. }) => TxType::DeployAccount, - RuntimeCall::Starknet( consume_l1_message { .. }) => TxType::L1Handler, - _ => panic!("The previous match made sure that at this point tx is one of those starknet calls"), + let transaction = match tx.function { + RuntimeCall::Starknet( invoke { transaction }) => Transaction::Invoke(transaction), + RuntimeCall::Starknet( declare { transaction, .. }) => Transaction::Declare(transaction), + RuntimeCall::Starknet( deploy_account { transaction }) => Transaction::DeployAccount(transaction), + RuntimeCall::Starknet( consume_l1_message { transaction, .. }) => Transaction::L1Handler(transaction), + _ => unreachable!("The previous match made sure that at this point tx is one of those starknet calls"), }; + let tx_index = u32::try_from(tx_index).expect("unexpected number of transactions"); + Some((tx_index, transaction)) + } + + fn get_events_for_tx_by_index(tx_index: u32) -> Option> { // Skip all the events that are not related to our tx let event_iter = System::read_events_no_consensus().filter_map(|event| { @@ -322,7 +324,7 @@ impl_runtime_apis! { _ => return true }; - tx_index as u32 != index + tx_index != index }); // Collect all the events related to our tx @@ -334,22 +336,22 @@ impl_runtime_apis! { _ => panic!("The previous iteration made sure at this point phase is of ApplyExtrinsic variant"), }; - tx_index as u32 == index + tx_index == index }).map(|(_, event)| event).collect(); - Some((tx_type, events)) + Some(events) } fn get_tx_execution_outcome(tx_hash: TransactionHash) -> Option> { Starknet::tx_revert_error(tx_hash).map(|s| s.into_bytes()) } - fn get_block_context() -> pallet_starknet::runtime_api::BlockContext { - Starknet::get_block_context().into() - } + fn get_block_context() -> pallet_starknet_runtime_api::BlockContext { + Starknet::get_block_context().into() + } } - impl pallet_starknet::runtime_api::ConvertTransactionRuntimeApi for Runtime { + impl pallet_starknet_runtime_api::ConvertTransactionRuntimeApi for Runtime { fn convert_transaction(transaction: UserTransaction) -> Result { let call = match transaction { UserTransaction::Declare(tx, contract_class) => { diff --git a/crates/runtime/madara-hotstuff/src/pallets.rs b/crates/runtime/madara-hotstuff/src/pallets.rs index 344a54cb78..dbff8ffccf 100644 --- a/crates/runtime/madara-hotstuff/src/pallets.rs +++ b/crates/runtime/madara-hotstuff/src/pallets.rs @@ -17,7 +17,6 @@ pub use pallet_hotstuff; pub use pallet_starknet; pub use pallet_timestamp::Call as TimestampCall; use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_runtime::generic; use sp_runtime::traits::{AccountIdLookup, BlakeTwo256}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -70,15 +69,13 @@ impl frame_system::Config for Runtime { /// The lookup mechanism to get account ID from whatever is passed in dispatchers. type Lookup = AccountIdLookup; /// The index type for storing how many extrinsics an account has signed. - type Index = Index; - /// The index type for blocks. - type BlockNumber = BlockNumber; + type Nonce = Index; /// The type for hashing blocks and tries. type Hash = Hash; /// The hashing algorithm used. type Hashing = BlakeTwo256; - /// The header type. - type Header = generic::Header; + /// The Block type. + type Block = Block; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; /// The ubiquitous origin type. @@ -125,6 +122,7 @@ impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = ConstU32<32>; + type AllowMultipleBlocksPerSlot = ConstBool; } /// -------------------------------------- diff --git a/crates/runtime/madara/Cargo.toml b/crates/runtime/madara/Cargo.toml index c0a00854fd..f2ac61533b 100644 --- a/crates/runtime/madara/Cargo.toml +++ b/crates/runtime/madara/Cargo.toml @@ -62,9 +62,6 @@ blockifier = { workspace = true } starknet-ff = { workspace = true } starknet_api = { workspace = true } -# Hotstuff dependencies -hotstuff-primitives = { workspace = true } -pallet-hotstuff = { workspace = true } [build-dependencies] substrate-wasm-builder = { workspace = true } @@ -103,9 +100,6 @@ std = [ "scale-info/std", "blockifier/std", "starknet_api/std", - # Hotstuff consensus dependencies - "pallet-hotstuff/std", - "hotstuff-primitives/std", ] try-runtime = [ "pallet-timestamp/try-runtime", @@ -117,8 +111,6 @@ try-runtime = [ "pallet-grandpa/try-runtime", # Madara pallets "pallet-starknet/try-runtime", - # Hotstuff pallet - "pallet-hotstuff/try-runtime", ] default = ["std"] madara-state-root = [] From 8940bd67004593a04351c13e707b310f88b024ad Mon Sep 17 00:00:00 2001 From: jerrybaoo <61228234+jerrybaoo@users.noreply.github.com> Date: Wed, 6 Dec 2023 00:10:09 +0800 Subject: [PATCH 12/12] fix hotsutff consensus --- crates/node/src/service.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/node/src/service.rs b/crates/node/src/service.rs index 2a1c9cf0bd..c8227d3554 100644 --- a/crates/node/src/service.rs +++ b/crates/node/src/service.rs @@ -348,7 +348,12 @@ pub fn new_full( None } #[cfg(feature = "with-hotstuff-runtime")] - None + { + net_config.add_notification_protocol(hotstuff_consensus::config::hotstuff_peers_set_config( + hotstuff_protocol_name.clone(), + )); + None + } } else { None };