From 8fdb8d8b6ad0ef9bd6decb50d09861cfa44d39bc Mon Sep 17 00:00:00 2001 From: Dariia Porechna <35382599+dariolina@users.noreply.github.com> Date: Thu, 29 Aug 2024 19:11:24 +0200 Subject: [PATCH] Add history seeding pallet --- Cargo.lock | 16 +++ crates/pallet-history-seeding/Cargo.toml | 38 +++++++ crates/pallet-history-seeding/README.md | 5 + crates/pallet-history-seeding/src/lib.rs | 100 +++++++++++++++++ crates/pallet-history-seeding/src/tests.rs | 106 ++++++++++++++++++ .../src/chain_spec.rs | 9 +- crates/subspace-node/src/chain_spec.rs | 20 +++- crates/subspace-runtime/Cargo.toml | 2 + crates/subspace-runtime/src/lib.rs | 7 ++ 9 files changed, 296 insertions(+), 7 deletions(-) create mode 100644 crates/pallet-history-seeding/Cargo.toml create mode 100644 crates/pallet-history-seeding/README.md create mode 100644 crates/pallet-history-seeding/src/lib.rs create mode 100644 crates/pallet-history-seeding/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index cf4a0dec15..bdfb185b13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7856,6 +7856,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-history-seeding" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-sudo", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-messenger" version = "0.1.0" @@ -12945,6 +12960,7 @@ dependencies = [ "pallet-collective", "pallet-democracy", "pallet-domains", + "pallet-history-seeding", "pallet-messenger", "pallet-mmr", "pallet-offences-subspace", diff --git a/crates/pallet-history-seeding/Cargo.toml b/crates/pallet-history-seeding/Cargo.toml new file mode 100644 index 0000000000..b5541da102 --- /dev/null +++ b/crates/pallet-history-seeding/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "pallet-history-seeding" +version = "0.1.0" +edition = "2021" +description = "A pallet for seeding history of the network" +authors = ["Dariia Porechna "] +repository = "https://github.com/autonomys/subspace" +license = "Apache-2.0" +readme = "README.md" +include = [ + "/src", + "/Cargo.toml", + "/README.md", +] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.2", default-features = false, features = ["derive"] } +frame-support = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } +frame-system = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } +sp-std = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } + +[dev-dependencies] +pallet-sudo = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631", features = ["std"] } +sp-core = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } +sp-io = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } +sp-runtime = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "frame-support/std", + "frame-system/std", + "sp-std/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/crates/pallet-history-seeding/README.md b/crates/pallet-history-seeding/README.md new file mode 100644 index 0000000000..54855ad78f --- /dev/null +++ b/crates/pallet-history-seeding/README.md @@ -0,0 +1,5 @@ +# Pallet History Seeding + +The history seeding pallet allows an authorized account to add remarks to the blockchain, which can be used to seed historical data or important information into the chain's history. The authorized account for seeding can be set by root. + +License: Apache-2.0 diff --git a/crates/pallet-history-seeding/src/lib.rs b/crates/pallet-history-seeding/src/lib.rs new file mode 100644 index 0000000000..5ba67df9a2 --- /dev/null +++ b/crates/pallet-history-seeding/src/lib.rs @@ -0,0 +1,100 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::pallet_prelude::*; +use frame_support::traits::{BuildGenesisConfig, Get}; + +#[cfg(test)] +mod tests; + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_system::pallet_prelude::*; + use frame_system::{RawOrigin, WeightInfo}; + use scale_info::prelude::vec::Vec; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// History was seeded. [who, remark_size] + HistorySeeded { who: T::AccountId, remark_size: u32 }, + } + + #[pallet::error] + pub enum Error { + /// The sender is not authorized to seed history + NotAuthorized, + } + + #[pallet::storage] + #[pallet::getter(fn history_seeder)] + pub type HistorySeeder = StorageValue<_, T::AccountId, OptionQuery>; + + #[pallet::call] + impl Pallet { + /// Seed history with a remark + /// TODO: add proper weight + #[pallet::call_index(0)] + #[pallet::weight((::SystemWeightInfo::remark(remark.len() as u32) + T::DbWeight::get().reads(1), Pays::No))] + pub fn seed_history(origin: OriginFor, remark: Vec) -> DispatchResult { + let who = ensure_signed(origin.clone())?; + + // Ensure the sender is the authorized history seeder + ensure!( + Some(who.clone()) == Self::history_seeder(), + Error::::NotAuthorized + ); + + // Add the remark to the block + frame_system::Pallet::::remark( + RawOrigin::Signed(who.clone()).into(), + remark.clone(), + ) + .map_err(|e| e.error)?; + + // Emit an event + Self::deposit_event(Event::HistorySeeded { + who, + remark_size: remark.len() as u32, + }); + + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(T::DbWeight::get().writes(1))] + pub fn set_history_seeder( + origin: OriginFor, + new_seeder: T::AccountId, + ) -> DispatchResult { + ensure_root(origin)?; + HistorySeeder::::put(new_seeder); + Ok(()) + } + } + + #[derive(frame_support::DefaultNoBound)] + #[pallet::genesis_config] + pub struct GenesisConfig { + pub history_seeder: Option, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + if let Some(ref seeder) = self.history_seeder { + HistorySeeder::::put(seeder); + } + } + } +} diff --git a/crates/pallet-history-seeding/src/tests.rs b/crates/pallet-history-seeding/src/tests.rs new file mode 100644 index 0000000000..ba465e88f6 --- /dev/null +++ b/crates/pallet-history-seeding/src/tests.rs @@ -0,0 +1,106 @@ +use super::*; +use crate::{self as pallet_history_seeding}; +use frame_support::{assert_noop, assert_ok, construct_runtime, derive_impl}; +use frame_system as system; +use sp_runtime::BuildStorage; + +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub struct Test { + System: frame_system, + Sudo: pallet_sudo, + HistorySeeding: pallet_history_seeding, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; +} + +impl pallet_sudo::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WeightInfo = (); +} + +impl pallet::Config for Test { + type RuntimeEvent = RuntimeEvent; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = system::GenesisConfig::::default() + .build_storage() + .unwrap(); + t.into() +} + +#[test] +fn genesis_config_works() { + new_test_ext().execute_with(|| { + let genesis_config = pallet::GenesisConfig:: { + history_seeder: Some(1), + }; + genesis_config.build(); + assert_eq!(HistorySeeding::history_seeder(), Some(1)); + }); +} + +#[test] +fn set_history_seeder_works() { + new_test_ext().execute_with(|| { + assert_ok!(HistorySeeding::set_history_seeder(RuntimeOrigin::root(), 1)); + assert_eq!(HistorySeeding::history_seeder(), Some(1)); + + // Ensure only root can set the history seeder + assert_noop!( + HistorySeeding::set_history_seeder(RuntimeOrigin::signed(1), 2), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +#[test] +fn seed_history_works() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + + // Set the history seeder + assert_ok!(HistorySeeding::set_history_seeder(RuntimeOrigin::root(), 1)); + + // Seed history + let remark = vec![1, 2, 3]; + assert_ok!(HistorySeeding::seed_history( + RuntimeOrigin::signed(1), + remark.clone() + )); + + // Check if the event was emitted + System::assert_has_event(RuntimeEvent::HistorySeeding(Event::HistorySeeded { + who: 1, + remark_size: 3, + })); + + // Ensure unauthorized account cannot seed history + assert_noop!( + HistorySeeding::seed_history(RuntimeOrigin::signed(2), remark), + Error::::NotAuthorized + ); + }); +} + +#[test] +fn seed_history_fails_when_no_seeder_set() { + new_test_ext().execute_with(|| { + let remark = vec![1, 2, 3]; + assert_noop!( + HistorySeeding::seed_history(RuntimeOrigin::signed(1), remark.clone()), + Error::::NotAuthorized + ); + assert_noop!( + HistorySeeding::seed_history(RuntimeOrigin::root(), remark), + sp_runtime::DispatchError::BadOrigin + ); + }); +} diff --git a/crates/subspace-malicious-operator/src/chain_spec.rs b/crates/subspace-malicious-operator/src/chain_spec.rs index 8085f356e7..b90bc008d0 100644 --- a/crates/subspace-malicious-operator/src/chain_spec.rs +++ b/crates/subspace-malicious-operator/src/chain_spec.rs @@ -14,8 +14,8 @@ use std::marker::PhantomData; use std::num::NonZeroU32; use subspace_runtime::{ AllowAuthoringBy, CouncilConfig, DemocracyConfig, DomainsConfig, EnableRewardsAt, - MaxDomainBlockSize, MaxDomainBlockWeight, RewardsConfig, RuntimeConfigsConfig, SubspaceConfig, - VestingConfig, + HistorySeedingConfig, MaxDomainBlockSize, MaxDomainBlockWeight, RewardsConfig, + RuntimeConfigsConfig, SubspaceConfig, VestingConfig, }; use subspace_runtime_primitives::{ AccountId, Balance, BlockNumber, CouncilDemocracyConfigParams, SSC, @@ -261,7 +261,7 @@ fn subspace_genesis_config( raw_genesis_storage: genesis_domain_params.raw_genesis_storage, // Domain config, mainly for placeholder the concrete value TBD - owner_account_id: sudo_account, + owner_account_id: sudo_account.clone(), domain_name: genesis_domain_params.domain_name, max_block_size: MaxDomainBlockSize::get(), max_block_weight: MaxDomainBlockWeight::get(), @@ -274,6 +274,9 @@ fn subspace_genesis_config( initial_balances: genesis_domain_params.initial_balances, }], }, + history_seeding: HistorySeedingConfig { + history_seeder: Some(sudo_account), + }, } } diff --git a/crates/subspace-node/src/chain_spec.rs b/crates/subspace-node/src/chain_spec.rs index 66ccff9de1..9f7bc7dbbd 100644 --- a/crates/subspace-node/src/chain_spec.rs +++ b/crates/subspace-node/src/chain_spec.rs @@ -33,8 +33,9 @@ use std::num::NonZeroU32; use subspace_core_primitives::PotKey; use subspace_runtime::{ AllowAuthoringBy, BalancesConfig, CouncilConfig, DemocracyConfig, DomainsConfig, - EnableRewardsAt, MaxDomainBlockSize, MaxDomainBlockWeight, RewardsConfig, RuntimeConfigsConfig, - RuntimeGenesisConfig, SubspaceConfig, SudoConfig, SystemConfig, VestingConfig, WASM_BINARY, + EnableRewardsAt, HistorySeedingConfig, MaxDomainBlockSize, MaxDomainBlockWeight, RewardsConfig, + RuntimeConfigsConfig, RuntimeGenesisConfig, SubspaceConfig, SudoConfig, SystemConfig, + VestingConfig, WASM_BINARY, }; use subspace_runtime_primitives::time::MILLISECS_PER_BLOCK; use subspace_runtime_primitives::{ @@ -206,10 +207,14 @@ pub fn gemini_3h_compiled() -> Result { ]), genesis_domains: vec![ evm_chain_spec::get_genesis_domain(SpecId::Gemini, sudo_account.clone())?, - auto_id_chain_spec::get_genesis_domain(SpecId::Gemini, sudo_account)?, + auto_id_chain_spec::get_genesis_domain( + SpecId::Gemini, + sudo_account.clone(), + )?, ], }, CouncilDemocracyConfigParams::::production_params(), + sudo_account.clone(), )?) .map_err(|error| format!("Failed to serialize genesis config: {error}"))?, ) @@ -316,10 +321,11 @@ pub fn devnet_config_compiled() -> Result { ]), genesis_domains: vec![auto_id_chain_spec::get_genesis_domain( SpecId::DevNet, - sudo_account, + sudo_account.clone(), )?], }, CouncilDemocracyConfigParams::::fast_params(), + sudo_account.clone(), )?) .map_err(|error| format!("Failed to serialize genesis config: {error}"))?, ) @@ -330,6 +336,7 @@ pub fn devnet_config_compiled() -> Result { pub fn dev_config() -> Result { let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; let sudo_account = get_account_id_from_seed("Alice"); + let history_seeder = get_account_id_from_seed("Bob"); Ok(GenericChainSpec::builder(wasm_binary, None) .with_name("Subspace development") @@ -380,6 +387,7 @@ pub fn dev_config() -> Result { )?], }, CouncilDemocracyConfigParams::::fast_params(), + history_seeder, )?) .map_err(|error| format!("Failed to serialize genesis config: {error}"))?, )) @@ -395,6 +403,7 @@ fn subspace_genesis_config( genesis_params: GenesisParams, genesis_domain_params: GenesisDomainParams, council_democracy_config_params: CouncilDemocracyConfigParams, + history_seeder_account: AccountId, ) -> Result { let GenesisParams { enable_rewards_at, @@ -469,6 +478,9 @@ fn subspace_genesis_config( .then_some(genesis_domain_params.permissioned_action_allowed_by), genesis_domains, }, + history_seeding: HistorySeedingConfig { + history_seeder: Some(history_seeder_account), + }, }) } diff --git a/crates/subspace-runtime/Cargo.toml b/crates/subspace-runtime/Cargo.toml index 5212e0e1bd..f42e37d9f6 100644 --- a/crates/subspace-runtime/Cargo.toml +++ b/crates/subspace-runtime/Cargo.toml @@ -29,6 +29,7 @@ pallet-balances = { default-features = false, git = "https://github.com/subspace pallet-collective = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } pallet-democracy = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } pallet-domains = { version = "0.1.0", default-features = false, path = "../pallet-domains" } +pallet-history-seeding = { version = "0.1.0", default-features = false,path = "../pallet-history-seeding" } pallet-messenger = { version = "0.1.0", path = "../../domains/pallets/messenger", default-features = false } pallet-mmr = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "5626154d0781ac9a6ffd5a6207ed237f425ae631" } pallet-offences-subspace = { version = "0.1.0", default-features = false, path = "../pallet-offences-subspace" } @@ -94,6 +95,7 @@ std = [ "pallet-collective/std", "pallet-democracy/std", "pallet-domains/std", + "pallet-history-seeding/std", "pallet-messenger/std", "pallet-mmr/std", "pallet-offences-subspace/std", diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 4453eb6250..65cbb3b0a6 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -588,6 +588,10 @@ impl pallet_democracy::Config for Runtime { type WeightInfo = pallet_democracy::weights::SubstrateWeight; } +impl pallet_history_seeding::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + parameter_types! { pub const SelfChainId: ChainId = ChainId::Consensus; } @@ -966,6 +970,9 @@ construct_runtime!( Democracy: pallet_democracy = 83, Preimage: pallet_preimage = 84, + // history seeding + HistorySeeding: pallet_history_seeding = 91, + // Reserve some room for other pallets as we'll remove sudo pallet eventually. Sudo: pallet_sudo = 100, }