diff --git a/substrate/node/src/chain_spec.rs b/substrate/node/src/chain_spec.rs index a68ff28e..94c882c7 100644 --- a/substrate/node/src/chain_spec.rs +++ b/substrate/node/src/chain_spec.rs @@ -20,7 +20,7 @@ use serai_abi::{ }, SubstrateBlock as Block, }; -use serai_runtime::*; +use serai_runtime::GenesisConfig; pub type ChainSpec = sc_service::GenericChainSpec; @@ -66,95 +66,32 @@ fn wasm_binary(dev: bool) -> Vec { } log::info!("using built-in wasm"); - WASM_BINARY.ok_or("compiled in wasm not available").unwrap().to_vec() + serai_runtime::WASM_BINARY.ok_or("compiled in wasm not available").unwrap().to_vec() } -fn devnet_genesis( - validators: &[&'static str], - endowed_accounts: Vec, -) -> RuntimeGenesisConfig { - let validators = validators - .iter() - .map(|name| (insecure_account_from_name(name), insecure_embedded_elliptic_curve_keys(name))) - .collect::>(); - - RuntimeGenesisConfig { - system: SystemConfig { _config: PhantomData }, - - coins: CoinsConfig { - accounts: endowed_accounts - .into_iter() - .map(|address| (address.into(), Balance { coin: Coin::Serai, amount: Amount(1 << 60) })) - .collect(), - _instance: PhantomData, - }, - - liquidity_tokens: LiquidityTokensConfig { accounts: vec![], _instance: PhantomData }, - - validator_sets: ValidatorSetsConfig { - participants: validators - .iter() - .map(|validator| (validator.0.into(), validator.1.clone())) - .collect(), - }, - signals: SignalsConfig::default(), - babe: BabeConfig { - // We leave this empty as `serai-validator-sets-pallet` initializes the authorities - authorities: vec![], - epoch_config: BABE_GENESIS_EPOCH_CONFIG, - _config: PhantomData, - }, - grandpa: GrandpaConfig { authorities: vec![], _config: PhantomData }, +fn devnet_genesis(validators: &[&'static str], endowed_accounts: Vec) -> GenesisConfig { + GenesisConfig { + validators: validators + .iter() + .map(|name| (insecure_account_from_name(name), insecure_embedded_elliptic_curve_keys(name))) + .collect(), + coins: endowed_accounts + .into_iter() + .map(|address| (address, Balance { coin: Coin::Serai, amount: Amount(1 << 60) })) + .collect(), } } /* -fn testnet_genesis(validators: Vec<&'static str>) -> RuntimeGenesisConfig { - let validators = validators - .into_iter() - .map(|validator| Public::decode(&mut hex::decode(validator).unwrap().as_slice()).unwrap()) - .collect::>(); - let key_shares = NETWORKS - .iter() - .map(|network| match network { - NetworkId::Serai => (NetworkId::Serai, Amount(50_000 * 10_u64.pow(8))), - NetworkId::External(ExternalNetworkId::Bitcoin) => { - (NetworkId::External(ExternalNetworkId::Bitcoin), Amount(1_000_000 * 10_u64.pow(8))) - } - NetworkId::External(ExternalNetworkId::Ethereum) => { - (NetworkId::External(ExternalNetworkId::Ethereum), Amount(1_000_000 * 10_u64.pow(8))) - } - NetworkId::External(ExternalNetworkId::Monero) => { - (NetworkId::External(ExternalNetworkId::Monero), Amount(100_000 * 10_u64.pow(8))) - } - }) - .collect::>(); - - assert_eq!(validators.iter().collect::>().len(), validators.len()); - - RuntimeGenesisConfig { - system: SystemConfig { _config: PhantomData }, - - coins: CoinsConfig { - accounts: validators - .iter() - .map(|a| (*a, Balance { coin: Coin::Serai, amount: Amount(5_000_000 * 10_u64.pow(8)) })) - .collect(), - }, - - validator_sets: ValidatorSetsConfig { - participants: validators.clone(), - }, - signals: SignalsConfig::default(), - babe: BabeConfig { - authorities: validators.iter().map(|validator| ((*validator).into(), 1)).collect(), - epoch_config: BABE_GENESIS_EPOCH_CONFIG, - _config: PhantomData, - }, - grandpa: GrandpaConfig { - authorities: validators.into_iter().map(|validator| (validator.into(), 1)).collect(), - _config: PhantomData, - }, +fn testnet_genesis(validators: &[&'static str]) -> GenesisConfig { + GenesisConfig { + validators: validators + .iter() + .map(|name| { + (insecure_account_from_name(name), insecure_embedded_elliptic_curve_keys(name)) + }) + .collect(), + coins: vec![], } } */ @@ -164,7 +101,7 @@ fn genesis( id: &'static str, chain_type: ChainType, protocol_id: &'static str, - config: &RuntimeGenesisConfig, + config: &GenesisConfig, ) -> ChainSpec { use sp_core::{ Encode, diff --git a/substrate/node/src/rpc/blockchain.rs b/substrate/node/src/rpc/blockchain.rs index fc4c578f..375b7820 100644 --- a/substrate/node/src/rpc/blockchain.rs +++ b/substrate/node/src/rpc/blockchain.rs @@ -10,7 +10,6 @@ use sp_api::ProvideRuntimeApi; use sc_client_api::BlockBackend; use serai_abi::{primitives::prelude::*, SubstrateBlock as Block}; -use serai_runtime::*; use jsonrpsee::RpcModule; diff --git a/substrate/node/src/rpc/mod.rs b/substrate/node/src/rpc/mod.rs index cc856784..d9131edb 100644 --- a/substrate/node/src/rpc/mod.rs +++ b/substrate/node/src/rpc/mod.rs @@ -8,7 +8,6 @@ use sp_block_builder::BlockBuilder; use sp_api::ProvideRuntimeApi; use serai_abi::{primitives::prelude::*, SubstrateBlock as Block}; -use serai_runtime::*; use tokio::sync::RwLock; diff --git a/substrate/node/src/rpc/p2p_validators.rs b/substrate/node/src/rpc/p2p_validators.rs index f07037d4..f25f89b2 100644 --- a/substrate/node/src/rpc/p2p_validators.rs +++ b/substrate/node/src/rpc/p2p_validators.rs @@ -7,18 +7,14 @@ use sp_blockchain::{Error as BlockchainError, HeaderBackend}; use sp_api::ProvideRuntimeApi; use serai_abi::{primitives::prelude::*, SubstrateBlock as Block}; -use serai_runtime::*; +use serai_runtime::SeraiApi; use tokio::sync::RwLock; use jsonrpsee::RpcModule; pub(crate) fn module< - C: 'static - + Send - + Sync - + HeaderBackend - + ProvideRuntimeApi>, + C: 'static + Send + Sync + HeaderBackend + ProvideRuntimeApi>, >( id: String, client: Arc, @@ -62,7 +58,7 @@ pub(crate) fn module< let mut returned_addresses = authority_discovery .write() .await - .get_addresses_by_authority_id(validator.into()) + .get_addresses_by_authority_id(sp_core::sr25519::Public::from(validator).into()) .await .unwrap_or_else(HashSet::new) .into_iter() diff --git a/substrate/runtime/Cargo.toml b/substrate/runtime/Cargo.toml index e6223a19..26872f31 100644 --- a/substrate/runtime/Cargo.toml +++ b/substrate/runtime/Cargo.toml @@ -20,20 +20,23 @@ workspace = true [dependencies] scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } -sp-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } -sp-inherents = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } sp-version = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } sp-runtime = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } + sp-api = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } +sp-transaction-pool = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } +sp-inherents = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } +sp-block-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } sp-consensus-babe = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } sp-consensus-grandpa = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } -sp-block-builder = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } -sp-transaction-pool = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } sp-authority-discovery = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } serai-abi = { path = "../abi", default-features = false, features = ["substrate"] } +[target.'cfg(target_family = "wasm")'.dependencies] +sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } +sp-session = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } + frame-system = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } frame-support = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } frame-executive = { git = "https://github.com/serai-dex/patch-polkadot-sdk", rev = "726437c7bbff34ec322483dac2b657e126c22233", default-features = false } @@ -57,13 +60,14 @@ std = [ "sp-core/std", "sp-session/std", + "sp-version/std", "sp-runtime/std", "sp-api/std", + "sp-transaction-pool/std", + "sp-block-builder/std", "sp-consensus-babe/std", "sp-consensus-grandpa/std", - "sp-block-builder/std", - "sp-transaction-pool/std", "sp-authority-discovery/std", "serai-abi/std", diff --git a/substrate/runtime/src/lib.rs b/substrate/runtime/src/lib.rs index a9f47653..37a8c8c2 100644 --- a/substrate/runtime/src/lib.rs +++ b/substrate/runtime/src/lib.rs @@ -1,736 +1,181 @@ -#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +use alloc::vec::Vec; +use serai_abi::primitives::{ + crypto::{Public, SignedEmbeddedEllipticCurveKeys}, + network_id::NetworkId, + balance::Balance, +}; + #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -extern crate alloc; +#[cfg(target_family = "wasm")] +mod wasm; -use alloc::{borrow::Cow, vec::Vec}; - -use sp_core::{ConstU32, ConstU64, sr25519::Public}; -use sp_runtime::{Perbill, Weight}; -use sp_version::RuntimeVersion; - -use serai_abi::{ - primitives::{ - network_id::{ExternalNetworkId, NetworkId}, - balance::{Amount, ExternalBalance}, - validator_sets::ValidatorSet, - address::SeraiAddress, - }, - SubstrateHeader as Header, SubstrateBlock, -}; - -use serai_coins_pallet::{CoinsInstance, LiquidityTokensInstance}; - -type Block = SubstrateBlock; - -/// The lookup for a SeraiAddress -> Public. -pub struct Lookup; -impl sp_runtime::traits::StaticLookup for Lookup { - type Source = SeraiAddress; - type Target = Public; - fn lookup(source: SeraiAddress) -> Result { - Ok(source.into()) - } - fn unlookup(source: Public) -> SeraiAddress { - source.into() - } +/// The genesis configuration for Serai. +#[derive(scale::Encode, scale::Decode)] +pub struct GenesisConfig { + /// The genesis validators for the network. + pub validators: Vec<(Public, Vec)>, + /// The accounts to start with balances, intended solely for testing purposes. + pub coins: Vec<(Public, Balance)>, } -// TODO: Remove -#[sp_version::runtime_version] -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: Cow::Borrowed("serai"), - impl_name: Cow::Borrowed("core"), - authoring_version: 0, - spec_version: 0, - impl_version: 0, - apis: RUNTIME_API_VERSIONS, - transaction_version: 0, - system_version: 0, -}; - -frame_support::parameter_types! { - pub const Version: RuntimeVersion = VERSION; - - // TODO - pub BlockLength: frame_system::limits::BlockLength = - frame_system::limits::BlockLength::max_with_normal_ratio( - 100 * 1024, - Perbill::from_percent(75), - ); - // TODO - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::with_sensible_defaults( - Weight::from_parts( - 2u64 * frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - u64::MAX, - ), - Perbill::from_percent(75), - ); -} - -#[frame_support::runtime] -mod runtime { - use super::*; - - #[runtime::runtime] - #[runtime::derive(RuntimeCall, RuntimeEvent, RuntimeError, RuntimeOrigin)] - pub struct Runtime; - - #[runtime::pallet_index(0)] - pub type System = frame_system::Pallet; - - #[runtime::pallet_index(1)] - pub type Core = serai_core_pallet::Pallet; - - #[runtime::pallet_index(2)] - pub type Coins = serai_coins_pallet::Pallet; - - #[runtime::pallet_index(3)] - pub type ValidatorSets = serai_validator_sets_pallet::Pallet; - - #[runtime::pallet_index(4)] - pub type Signals = serai_signals_pallet::Pallet; - - #[runtime::pallet_index(5)] - pub type LiquidityTokens = serai_coins_pallet::Pallet; - - #[runtime::pallet_index(0xfe)] - pub type Babe = pallet_babe::Pallet; - - #[runtime::pallet_index(0xff)] - pub type Grandpa = pallet_grandpa::Pallet; -} - -impl frame_system::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = BlockLength; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type RuntimeTask = (); - type Nonce = u32; - type Hash = ::Hash; - type Hashing = sp_runtime::traits::BlakeTwo256; - type AccountId = sp_core::sr25519::Public; - type Lookup = Lookup; - type Block = Block; - // Don't track old block hashes within the System pallet - // We use not a number -> hash index, but a hash -> () index, in our own pallet - type BlockHashCount = ConstU64<1>; - type DbWeight = frame_support::weights::constants::RocksDbWeight; - type Version = Version; - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - // We use the default weights as we never expose/call any of these methods - type SystemWeightInfo = (); - // We also don't use the provided extensions framework - type ExtensionsWeightInfo = (); - // We don't invoke any hooks on-set-code as we don't perform upgrades via the blockchain yet via - // nodes, ensuring everyone who upgrades consents to the rules they upgrade to - type OnSetCode = (); - type MaxConsumers = ConstU32<{ u32::MAX }>; - // No migrations set - type SingleBlockMigrations = (); - type MultiBlockMigrator = (); - - type PreInherents = serai_core_pallet::StartOfBlock; - type PostInherents = (); - type PostTransactions = serai_core_pallet::EndOfBlock; -} - -impl serai_core_pallet::Config for Runtime {} - -impl serai_coins_pallet::Config for Runtime { - type AllowMint = serai_coins_pallet::AlwaysAllowMint; // TODO -} - -#[doc(hidden)] -pub struct EconomicSecurity; // TODO -impl serai_abi::economic_security::EconomicSecurity for EconomicSecurity { - fn achieved_economic_security(_network: ExternalNetworkId) -> bool { - false - } - fn sri_value(_balance: ExternalBalance) -> Amount { - Amount(0) - } -} -impl serai_validator_sets_pallet::Config for Runtime { - type ShouldEndSession = Babe; - type EconomicSecurity = EconomicSecurity; -} -impl serai_signals_pallet::Config for Runtime { - type RetirementValidityDuration = ConstU64<0>; // TODO - type RetirementLockInDuration = ConstU64<1>; // TODO -} -impl serai_coins_pallet::Config for Runtime { - type AllowMint = serai_coins_pallet::AlwaysAllowMint; -} - -/* - `pallet-babe` requires we implement `pallet-timestamp` for the associated constants. It does not - actually require we offer the timestamp pallet however, and we don't as we follow our methodology - (using the block header for timestamps, not an inherent transaction). - - TODO: Set timestamp when executing a block. -*/ -impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = Babe; - // TODO - type MinimumPeriod = ConstU64<{ (6 * 1000) / 2 }>; - type WeightInfo = (); -} - -#[doc(hidden)] -pub struct GetCurrentSessionForSubstrate; -impl pallet_session::GetCurrentSessionForSubstrate for GetCurrentSessionForSubstrate { - fn get() -> u32 { - serai_validator_sets_pallet::Pallet::::current_session(NetworkId::Serai) - .map(|session| session.0) - .unwrap_or(0) - } -} -impl pallet_session::Config for Runtime { - type Session = GetCurrentSessionForSubstrate; -} - -type MaxAuthorities = - ConstU32<{ serai_abi::primitives::validator_sets::KeyShares::MAX_PER_SET_U32 }>; -impl pallet_babe::Config for Runtime { - // TODO - type EpochDuration = ConstU64<{ (7 * 24 * 60 * 60 * 1000) / (6 * 1000) }>; - - type ExpectedBlockTime = ConstU64<{ 6 * 1000 }>; // TODO - type EpochChangeTrigger = pallet_babe::ExternalTrigger; - - type WeightInfo = (); - type MaxAuthorities = MaxAuthorities; - type MaxNominators = ConstU32<1>; - - // TODO: https://github.com/serai-dex/serai/issues/657 - type DisabledValidators = (); - type KeyOwnerProof = sp_session::MembershipProof; - type EquivocationReportSystem = (); -} -impl pallet_grandpa::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - - type WeightInfo = (); - type MaxAuthorities = MaxAuthorities; - type MaxNominators = ConstU32<1>; - - // TODO: https://github.com/serai-dex/serai/issues/657 - type MaxSetIdSessionEntries = ConstU64<0>; - type KeyOwnerProof = sp_session::MembershipProof; - type EquivocationReportSystem = (); -} - -impl From> for RuntimeOrigin { - fn from(signer: Option) -> Self { - match signer { - None => RuntimeOrigin::none(), - Some(signer) => RuntimeOrigin::signed(signer.into()), - } - } -} - -impl From for RuntimeCall { - fn from(call: serai_abi::Call) -> Self { - match call { - serai_abi::Call::Coins(call) => { - use serai_abi::coins::Call; - use serai_coins_pallet::Call as Scall; - RuntimeCall::Coins(match call { - Call::transfer { to, coins } => Scall::transfer { to: to.into(), coins }, - Call::burn { coins } => Scall::burn { coins }, - Call::burn_with_instruction { instruction } => { - Scall::burn_with_instruction { instruction } - } - }) - } - serai_abi::Call::ValidatorSets(call) => { - use serai_abi::validator_sets::Call; - use serai_validator_sets_pallet::Call as Scall; - RuntimeCall::ValidatorSets(match call { - Call::set_keys { network, key_pair, signature_participants, signature } => { - Scall::set_keys { network, key_pair, signature_participants, signature } - } - Call::report_slashes { network, slashes, signature } => { - Scall::report_slashes { network, slashes, signature } - } - Call::set_embedded_elliptic_curve_keys { keys } => { - Scall::set_embedded_elliptic_curve_keys { keys } - } - Call::allocate { network, amount } => Scall::allocate { network, amount }, - Call::deallocate { network, amount } => Scall::deallocate { network, amount }, - Call::claim_deallocation { deallocation } => Scall::claim_deallocation { - network: deallocation.network, - session: deallocation.session, - }, - }) - } - serai_abi::Call::Signals(call) => { - use serai_abi::signals::Call; - use serai_signals_pallet::Call as Scall; - RuntimeCall::Signals(match call { - Call::register_retirement_signal { in_favor_of } => { - Scall::register_retirement_signal { in_favor_of } - } - Call::revoke_retirement_signal { was_in_favor_of } => { - Scall::revoke_retirement_signal { retirement_signal: was_in_favor_of } - } - Call::favor { signal, with_network } => Scall::favor { signal, with_network }, - Call::revoke_favor { signal, with_network } => { - Scall::revoke_favor { signal, with_network } - } - Call::stand_against { signal, with_network } => { - Scall::stand_against { signal, with_network } - } - }) - } - serai_abi::Call::Dex(call) => { - use serai_abi::dex::Call; - match call { - Call::transfer_liquidity { to, liquidity_tokens } => { - RuntimeCall::LiquidityTokens(serai_coins_pallet::Call::transfer { - to: to.into(), - coins: liquidity_tokens.into(), - }) - } - Call::add_liquidity { .. } | - Call::remove_liquidity { .. } | - Call::swap_exact { .. } | - Call::swap_for_exact { .. } => todo!("TODO"), - } - } - serai_abi::Call::GenesisLiquidity(call) => { - use serai_abi::genesis_liquidity::Call; - match call { - Call::oraclize_values { .. } | Call::remove_liquidity { .. } => todo!("TODO"), - } - } - serai_abi::Call::InInstructions(call) => { - use serai_abi::in_instructions::Call; - match call { - Call::execute_batch { .. } => todo!("TODO"), - } - } - } - } -} - -type Executive = frame_executive::Executive; - sp_api::decl_runtime_apis! { - #[api_version(1)] pub trait GenesisApi { - fn build(genesis: RuntimeGenesisConfig); + fn build(genesis: GenesisConfig); } - #[api_version(1)] pub trait SeraiApi { fn validators(network_id: NetworkId) -> Vec; } } -const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); -pub const BABE_GENESIS_EPOCH_CONFIG: sp_consensus_babe::BabeEpochConfiguration = - sp_consensus_babe::BabeEpochConfiguration { - c: PRIMARY_PROBABILITY, - allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryPlainSlots, +// We stub `impl_runtime_apis` to generate the `RuntimeApi` object the node needs +#[cfg(not(target_family = "wasm"))] +mod apis { + use alloc::borrow::Cow; + use serai_abi::{SubstrateHeader as Header, SubstrateBlock as Block}; + + #[sp_version::runtime_version] + pub const VERSION: sp_version::RuntimeVersion = sp_version::RuntimeVersion { + spec_name: Cow::Borrowed("serai"), + impl_name: Cow::Borrowed("core"), + authoring_version: 0, + // Use the highest possible value so the node doesn't attempt to use this in place of the WASM + spec_version: 0xffffffff, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 0, + system_version: 0, }; -sp_api::impl_runtime_apis! { - impl crate::GenesisApi for Runtime { - fn build(genesis: RuntimeGenesisConfig) { - Core::genesis(); - Core::start_transaction(); - ::build(&genesis); - Core::end_transaction([0; 32]); - < - serai_core_pallet::EndOfBlock as frame_support::traits::PostTransactions - >::post_transactions(); - } - } - - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - VERSION - } - fn initialize_block(header: &Header) -> sp_runtime::ExtrinsicInclusionMode { - Executive::initialize_block(header) - } - fn execute_block(block: Block) { - Executive::execute_block(block); - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic( - extrinsic: ::Extrinsic, - ) -> sp_runtime::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: sp_runtime::transaction_validity::TransactionSource, - tx: ::Extrinsic, - block_hash: ::Hash, - ) -> sp_runtime::transaction_validity::TransactionValidity { - Executive::validate_transaction(source, tx, block_hash) - } - } - - impl sp_consensus_babe::BabeApi for Runtime { - fn configuration() -> sp_consensus_babe::BabeConfiguration { - use frame_support::traits::Get; - - let epoch_config = Babe::epoch_config().unwrap_or(BABE_GENESIS_EPOCH_CONFIG); - sp_consensus_babe::BabeConfiguration { - slot_duration: Babe::slot_duration(), - epoch_length: ::EpochDuration::get(), - c: epoch_config.c, - authorities: Babe::authorities().to_vec(), - randomness: Babe::randomness(), - allowed_slots: epoch_config.allowed_slots, + /// A `struct` representing the runtime as necessary to define the available APIs. + pub struct Runtime; + sp_api::impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> sp_version::RuntimeVersion { + VERSION + } + fn initialize_block(header: &Header) -> sp_runtime::ExtrinsicInclusionMode { + unimplemented!("runtime is only implemented when WASM") + } + fn execute_block(block: Block) { + unimplemented!("runtime is only implemented when WASM") } } - fn current_epoch_start() -> sp_consensus_babe::Slot { - Babe::current_epoch_start() - } - - fn current_epoch() -> sp_consensus_babe::Epoch { - Babe::current_epoch() - } - - fn next_epoch() -> sp_consensus_babe::Epoch { - Babe::next_epoch() - } - - // TODO: Revisit - fn generate_key_ownership_proof( - _slot: sp_consensus_babe::Slot, - _authority_id: pallet_babe::AuthorityId, - ) -> Option { - None - } - - // TODO: Revisit - fn submit_report_equivocation_unsigned_extrinsic( - equivocation_proof: sp_consensus_babe::EquivocationProof
, - _: sp_consensus_babe::OpaqueKeyOwnershipProof, - ) -> Option<()> { - None - } - } - - impl sp_consensus_grandpa::GrandpaApi for Runtime { - fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { - Grandpa::grandpa_authorities() - } - - fn current_set_id() -> sp_consensus_grandpa::SetId { - Grandpa::current_set_id() - } - - // TODO: Revisit - fn generate_key_ownership_proof( - _set_id: sp_consensus_grandpa::SetId, - _authority_id: pallet_grandpa::AuthorityId, - ) -> Option { - None - } - - // TODO: Revisit - fn submit_report_equivocation_unsigned_extrinsic( - equivocation_proof: sp_consensus_grandpa::EquivocationProof< - ::Hash, - u64, - >, - _: sp_consensus_grandpa::OpaqueKeyOwnershipProof, - ) -> Option<()> { - None - } - } - - impl sp_authority_discovery::AuthorityDiscoveryApi for Runtime { - fn authorities() -> Vec { - // Converts to `[u8; 32]` so it can be hashed - let mut all = alloc::collections::BTreeSet::<[u8; 32]>::new(); - for network in NetworkId::all() { - for participant in - >::validators(network) { - all.insert(participant.into()); - } + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic( + extrinsic: ::Extrinsic, + ) -> sp_runtime::ApplyExtrinsicResult { + unimplemented!("runtime is only implemented when WASM") } - all - .into_iter() - .map(|id| sp_authority_discovery::AuthorityId::from(sp_core::sr25519::Public::from(id))) - .collect() - } - } - impl crate::SeraiApi for Runtime { - fn validators(network: NetworkId) -> Vec { - // Returning the latest-decided, not latest and active, means the active set - // may fail to peer find if there isn't sufficient overlap. If a large amount reboot, - // forcing some validators to successfully peer find in order for the threshold to become - // online again, this may cause a liveness failure. - // - // This is assumed not to matter in real life, yet an interesting note. - let Some(session) = ValidatorSets::latest_decided_session(network) else { - return alloc::vec![] - }; - ValidatorSets::selected_validators(ValidatorSet { network, session }) - .map(|validator| validator.0) - .collect() + fn finalize_block() -> Header { + unimplemented!("runtime is only implemented when WASM") + } + + fn inherent_extrinsics( + data: sp_inherents::InherentData, + ) -> Vec<::Extrinsic> { + unimplemented!("runtime is only implemented when WASM") + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + unimplemented!("runtime is only implemented when WASM") + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: sp_runtime::transaction_validity::TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> sp_runtime::transaction_validity::TransactionValidity { + unimplemented!("runtime is only implemented when WASM") + } + } + + impl sp_consensus_babe::BabeApi for Runtime { + fn configuration() -> sp_consensus_babe::BabeConfiguration { + unimplemented!("runtime is only implemented when WASM") + } + + fn current_epoch_start() -> sp_consensus_babe::Slot { + unimplemented!("runtime is only implemented when WASM") + } + + fn current_epoch() -> sp_consensus_babe::Epoch { + unimplemented!("runtime is only implemented when WASM") + } + + fn next_epoch() -> sp_consensus_babe::Epoch { + unimplemented!("runtime is only implemented when WASM") + } + + fn generate_key_ownership_proof( + _slot: sp_consensus_babe::Slot, + _authority_id: sp_consensus_babe::AuthorityId, + ) -> Option { + unimplemented!("runtime is only implemented when WASM") + } + + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: sp_consensus_babe::EquivocationProof
, + _: sp_consensus_babe::OpaqueKeyOwnershipProof, + ) -> Option<()> { + unimplemented!("runtime is only implemented when WASM") + } + } + + impl sp_consensus_grandpa::GrandpaApi for Runtime { + fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { + unimplemented!("runtime is only implemented when WASM") + } + + fn current_set_id() -> sp_consensus_grandpa::SetId { + unimplemented!("runtime is only implemented when WASM") + } + + fn generate_key_ownership_proof( + _set_id: sp_consensus_grandpa::SetId, + _authority_id: sp_consensus_grandpa::AuthorityId, + ) -> Option { + unimplemented!("runtime is only implemented when WASM") + } + + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: sp_consensus_grandpa::EquivocationProof< + ::Hash, + u64, + >, + _: sp_consensus_grandpa::OpaqueKeyOwnershipProof, + ) -> Option<()> { + unimplemented!("runtime is only implemented when WASM") + } + } + + impl sp_authority_discovery::AuthorityDiscoveryApi for Runtime { + fn authorities() -> Vec { + unimplemented!("runtime is only implemented when WASM") + } + } + + impl crate::SeraiApi for Runtime { + fn validators( + network: serai_abi::primitives::network_id::NetworkId + ) -> Vec { + unimplemented!("runtime is only implemented when WASM") + } } } } - -#[derive(Clone, Default, PartialEq, Eq, Debug)] -struct Context; -impl serai_abi::TransactionContext for Context { - // TODO - const SIGNED_WEIGHT: Weight = Weight::zero(); - - type RuntimeCall = RuntimeCall; - - /// The implicit context to verify transactions with. - fn implicit_context() -> serai_abi::ImplicitContext { - serai_abi::ImplicitContext { - genesis: System::block_hash(0).into(), - protocol_id: [0; 32], // TODO via build script - } - } - - /// If a block is present in the blockchain. - fn block_is_present_in_blockchain(&self, hash: &serai_abi::primitives::BlockHash) -> bool { - serai_core_pallet::Pallet::::block_exists(hash) - } - /// The time embedded into the current block. - fn current_time(&self) -> Option { - todo!("TODO") - } - /// Get the next nonce for an account. - fn next_nonce(&self, signer: &SeraiAddress) -> u32 { - serai_core_pallet::Pallet::::next_nonce(signer) - } - /// If the signer can pay the SRI fee. - fn can_pay_fee( - &self, - signer: &SeraiAddress, - fee: serai_abi::primitives::balance::Amount, - ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { - use serai_abi::primitives::coin::Coin; - if serai_coins_pallet::Pallet::::balance(signer, Coin::Serai) >= fee { - Ok(()) - } else { - Err(sp_runtime::transaction_validity::TransactionValidityError::Invalid( - sp_runtime::transaction_validity::InvalidTransaction::Payment, - )) - } - } - - fn start_transaction(&self) { - Core::start_transaction() - } - fn consume_next_nonce(&self, signer: &SeraiAddress) { - serai_core_pallet::Pallet::::consume_next_nonce(signer) - } - /// Have the transaction pay its SRI fee. - fn pay_fee( - &self, - signer: &SeraiAddress, - fee: serai_abi::primitives::balance::Amount, - ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { - use serai_abi::primitives::{coin::*, balance::*}; - serai_coins_pallet::Pallet::::burn( - RuntimeOrigin::signed(Public::from(*signer)), - Balance { coin: Coin::Serai, amount: fee }, - ) - .map_err(|_| { - sp_runtime::transaction_validity::TransactionValidityError::Invalid( - sp_runtime::transaction_validity::InvalidTransaction::Payment, - ) - }) - } - fn end_transaction(&self, transaction_hash: [u8; 32]) { - Core::end_transaction(transaction_hash); - } -} - -/* TODO -use validator_sets::MembershipProof; - -const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); - -parameter_types! { - pub const Version: RuntimeVersion = VERSION; - - pub const SS58Prefix: u8 = 42; // TODO: Remove for Bech32m - - // 1 MB block size limit - pub BlockLength: system::limits::BlockLength = - system::limits::BlockLength::max_with_normal_ratio(BLOCK_SIZE, NORMAL_DISPATCH_RATIO); - pub BlockWeights: system::limits::BlockWeights = - system::limits::BlockWeights::with_sensible_defaults( - Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), - NORMAL_DISPATCH_RATIO, - ); -} - -impl timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = Babe; - type MinimumPeriod = ConstU64<{ (TARGET_BLOCK_TIME * 1000) / 2 }>; - type WeightInfo = (); -} - -impl coins::Config for Runtime { - type AllowMint = ValidatorSets; -} - -impl dex::Config for Runtime { - type LPFee = ConstU32<3>; // 0.3% - type MintMinLiquidity = ConstU64<10000>; - - type MaxSwapPathLength = ConstU32<3>; // coin1 -> SRI -> coin2 - - type MedianPriceWindowLength = ConstU16<{ MEDIAN_PRICE_WINDOW_LENGTH }>; - - type WeightInfo = dex::weights::SubstrateWeight; -} - -pub struct IdentityValidatorIdOf; -impl Convert> for IdentityValidatorIdOf { - fn convert(key: PublicKey) -> Option { - Some(key) - } -} - -impl signals::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - // 1 week - #[allow(clippy::cast_possible_truncation)] - type RetirementValidityDuration = ConstU32<{ (7 * 24 * 60 * 60) / (TARGET_BLOCK_TIME as u32) }>; - // 2 weeks - #[allow(clippy::cast_possible_truncation)] - type RetirementLockInDuration = ConstU32<{ (2 * 7 * 24 * 60 * 60) / (TARGET_BLOCK_TIME as u32) }>; -} - -impl in_instructions::Config for Runtime { - type RuntimeEvent = RuntimeEvent; -} - -impl genesis_liquidity::Config for Runtime { - type RuntimeEvent = RuntimeEvent; -} - -impl emissions::Config for Runtime { - type RuntimeEvent = RuntimeEvent; -} - -impl economic_security::Config for Runtime { - type RuntimeEvent = RuntimeEvent; -} - -// for publishing equivocation evidences. -impl frame_system::offchain::SendTransactionTypes for Runtime -where - RuntimeCall: From, -{ - type Extrinsic = Transaction; - type OverarchingCall = RuntimeCall; -} - -// for validating equivocation evidences. -// The following runtime construction doesn't actually implement the pallet as doing so is -// unnecessary -// TODO: Replace the requirement on Config for a requirement on FindAuthor directly -impl pallet_authorship::Config for Runtime { - type FindAuthor = ValidatorSets; - type EventHandler = (); -} - -/// Longevity of an offence report. -pub type ReportLongevity = ::EpochDuration; - -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - -#[cfg(feature = "runtime-benchmarks")] -mod benches { - define_benchmarks!( - [frame_benchmarking, BaselineBench::] - - [system, SystemBench::] - - [pallet_timestamp, Timestamp] - - [balances, Balances] - - [babe, Babe] - [grandpa, Grandpa] - ); -} - -sp_api::impl_runtime_apis! { - impl validator_sets::ValidatorSetsApi for Runtime { - fn external_network_key(network: ExternalNetworkId) -> Option> { - ValidatorSets::external_network_key(network) - } - } - - impl dex::DexApi for Runtime { - fn quote_price_exact_tokens_for_tokens( - coin1: Coin, - coin2: Coin, - amount: SubstrateAmount, - include_fee: bool - ) -> Option { - Dex::quote_price_exact_tokens_for_tokens(coin1, coin2, amount, include_fee) - } - - fn quote_price_tokens_for_exact_tokens( - coin1: Coin, - coin2: Coin, - amount: SubstrateAmount, - include_fee: bool - ) -> Option { - Dex::quote_price_tokens_for_exact_tokens(coin1, coin2, amount, include_fee) - } - - fn get_reserves(coin1: Coin, coin2: Coin) -> Option<(SubstrateAmount, SubstrateAmount)> { - Dex::get_reserves(&coin1, &coin2).ok() - } - } -} -*/ +#[cfg(not(target_family = "wasm"))] +pub use apis::RuntimeApi; diff --git a/substrate/runtime/src/wasm/mod.rs b/substrate/runtime/src/wasm/mod.rs new file mode 100644 index 00000000..4bcc244c --- /dev/null +++ b/substrate/runtime/src/wasm/mod.rs @@ -0,0 +1,744 @@ +use core::marker::PhantomData; +use alloc::{borrow::Cow, vec, vec::Vec}; + +use sp_core::{ConstU32, ConstU64, sr25519::Public}; +use sp_runtime::{Perbill, Weight}; +use sp_version::RuntimeVersion; + +use serai_abi::{ + primitives::{ + network_id::{ExternalNetworkId, NetworkId}, + balance::{Amount, ExternalBalance}, + validator_sets::ValidatorSet, + address::SeraiAddress, + }, + SubstrateHeader as Header, SubstrateBlock, +}; + +use serai_coins_pallet::{CoinsInstance, LiquidityTokensInstance}; + +type Block = SubstrateBlock; + +/// The lookup for a SeraiAddress -> Public. +pub struct Lookup; +impl sp_runtime::traits::StaticLookup for Lookup { + type Source = SeraiAddress; + type Target = Public; + fn lookup(source: SeraiAddress) -> Result { + Ok(source.into()) + } + fn unlookup(source: Public) -> SeraiAddress { + source.into() + } +} + +// TODO: Remove +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: Cow::Borrowed("serai"), + impl_name: Cow::Borrowed("core"), + authoring_version: 0, + spec_version: 0, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 0, + system_version: 0, +}; + +frame_support::parameter_types! { + pub const Version: RuntimeVersion = VERSION; + + // TODO + pub BlockLength: frame_system::limits::BlockLength = + frame_system::limits::BlockLength::max_with_normal_ratio( + 100 * 1024, + Perbill::from_percent(75), + ); + // TODO + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::with_sensible_defaults( + Weight::from_parts( + 2u64 * frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, + u64::MAX, + ), + Perbill::from_percent(75), + ); +} + +#[frame_support::runtime] +mod runtime { + use super::*; + + #[runtime::runtime] + #[runtime::derive(RuntimeCall, RuntimeEvent, RuntimeError, RuntimeOrigin)] + pub struct Runtime; + + #[runtime::pallet_index(0)] + pub type System = frame_system::Pallet; + + #[runtime::pallet_index(1)] + pub type Core = serai_core_pallet::Pallet; + + #[runtime::pallet_index(2)] + pub type Coins = serai_coins_pallet::Pallet; + + #[runtime::pallet_index(3)] + pub type ValidatorSets = serai_validator_sets_pallet::Pallet; + + #[runtime::pallet_index(4)] + pub type Signals = serai_signals_pallet::Pallet; + + #[runtime::pallet_index(5)] + pub type LiquidityTokens = serai_coins_pallet::Pallet; + + #[runtime::pallet_index(0xfe)] + pub type Babe = pallet_babe::Pallet; + + #[runtime::pallet_index(0xff)] + pub type Grandpa = pallet_grandpa::Pallet; +} + +impl frame_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = BlockLength; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeTask = (); + type Nonce = u32; + type Hash = ::Hash; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = sp_core::sr25519::Public; + type Lookup = Lookup; + type Block = Block; + // Don't track old block hashes within the System pallet + // We use not a number -> hash index, but a hash -> () index, in our own pallet + type BlockHashCount = ConstU64<1>; + type DbWeight = frame_support::weights::constants::RocksDbWeight; + type Version = Version; + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + // We use the default weights as we never expose/call any of these methods + type SystemWeightInfo = (); + // We also don't use the provided extensions framework + type ExtensionsWeightInfo = (); + // We don't invoke any hooks on-set-code as we don't perform upgrades via the blockchain yet via + // nodes, ensuring everyone who upgrades consents to the rules they upgrade to + type OnSetCode = (); + type MaxConsumers = ConstU32<{ u32::MAX }>; + // No migrations set + type SingleBlockMigrations = (); + type MultiBlockMigrator = (); + + type PreInherents = serai_core_pallet::StartOfBlock; + type PostInherents = (); + type PostTransactions = serai_core_pallet::EndOfBlock; +} + +impl serai_core_pallet::Config for Runtime {} + +impl serai_coins_pallet::Config for Runtime { + type AllowMint = serai_coins_pallet::AlwaysAllowMint; // TODO +} + +#[doc(hidden)] +pub struct EconomicSecurity; // TODO +impl serai_abi::economic_security::EconomicSecurity for EconomicSecurity { + fn achieved_economic_security(_network: ExternalNetworkId) -> bool { + false + } + fn sri_value(_balance: ExternalBalance) -> Amount { + Amount(0) + } +} +impl serai_validator_sets_pallet::Config for Runtime { + type ShouldEndSession = Babe; + type EconomicSecurity = EconomicSecurity; +} +impl serai_signals_pallet::Config for Runtime { + type RetirementValidityDuration = ConstU64<0>; // TODO + type RetirementLockInDuration = ConstU64<1>; // TODO +} +impl serai_coins_pallet::Config for Runtime { + type AllowMint = serai_coins_pallet::AlwaysAllowMint; +} + +/* + `pallet-babe` requires we implement `pallet-timestamp` for the associated constants. It does not + actually require we offer the timestamp pallet however, and we don't as we follow our methodology + (using the block header for timestamps, not an inherent transaction). + + TODO: Set timestamp when executing a block. +*/ +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = Babe; + // TODO + type MinimumPeriod = ConstU64<{ (6 * 1000) / 2 }>; + type WeightInfo = (); +} + +#[doc(hidden)] +pub struct GetCurrentSessionForSubstrate; +impl pallet_session::GetCurrentSessionForSubstrate for GetCurrentSessionForSubstrate { + fn get() -> u32 { + serai_validator_sets_pallet::Pallet::::current_session(NetworkId::Serai) + .map(|session| session.0) + .unwrap_or(0) + } +} +impl pallet_session::Config for Runtime { + type Session = GetCurrentSessionForSubstrate; +} + +type MaxAuthorities = + ConstU32<{ serai_abi::primitives::validator_sets::KeyShares::MAX_PER_SET_U32 }>; +impl pallet_babe::Config for Runtime { + // TODO + type EpochDuration = ConstU64<{ (7 * 24 * 60 * 60 * 1000) / (6 * 1000) }>; + + type ExpectedBlockTime = ConstU64<{ 6 * 1000 }>; // TODO + type EpochChangeTrigger = pallet_babe::ExternalTrigger; + + type WeightInfo = (); + type MaxAuthorities = MaxAuthorities; + type MaxNominators = ConstU32<1>; + + // TODO: https://github.com/serai-dex/serai/issues/657 + type DisabledValidators = (); + type KeyOwnerProof = sp_session::MembershipProof; + type EquivocationReportSystem = (); +} +impl pallet_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + + type WeightInfo = (); + type MaxAuthorities = MaxAuthorities; + type MaxNominators = ConstU32<1>; + + // TODO: https://github.com/serai-dex/serai/issues/657 + type MaxSetIdSessionEntries = ConstU64<0>; + type KeyOwnerProof = sp_session::MembershipProof; + type EquivocationReportSystem = (); +} + +impl From> for RuntimeOrigin { + fn from(signer: Option) -> Self { + match signer { + None => RuntimeOrigin::none(), + Some(signer) => RuntimeOrigin::signed(signer.into()), + } + } +} + +impl From for RuntimeCall { + fn from(call: serai_abi::Call) -> Self { + match call { + serai_abi::Call::Coins(call) => { + use serai_abi::coins::Call; + use serai_coins_pallet::Call as Scall; + RuntimeCall::Coins(match call { + Call::transfer { to, coins } => Scall::transfer { to: to.into(), coins }, + Call::burn { coins } => Scall::burn { coins }, + Call::burn_with_instruction { instruction } => { + Scall::burn_with_instruction { instruction } + } + }) + } + serai_abi::Call::ValidatorSets(call) => { + use serai_abi::validator_sets::Call; + use serai_validator_sets_pallet::Call as Scall; + RuntimeCall::ValidatorSets(match call { + Call::set_keys { network, key_pair, signature_participants, signature } => { + Scall::set_keys { network, key_pair, signature_participants, signature } + } + Call::report_slashes { network, slashes, signature } => { + Scall::report_slashes { network, slashes, signature } + } + Call::set_embedded_elliptic_curve_keys { keys } => { + Scall::set_embedded_elliptic_curve_keys { keys } + } + Call::allocate { network, amount } => Scall::allocate { network, amount }, + Call::deallocate { network, amount } => Scall::deallocate { network, amount }, + Call::claim_deallocation { deallocation } => Scall::claim_deallocation { + network: deallocation.network, + session: deallocation.session, + }, + }) + } + serai_abi::Call::Signals(call) => { + use serai_abi::signals::Call; + use serai_signals_pallet::Call as Scall; + RuntimeCall::Signals(match call { + Call::register_retirement_signal { in_favor_of } => { + Scall::register_retirement_signal { in_favor_of } + } + Call::revoke_retirement_signal { was_in_favor_of } => { + Scall::revoke_retirement_signal { retirement_signal: was_in_favor_of } + } + Call::favor { signal, with_network } => Scall::favor { signal, with_network }, + Call::revoke_favor { signal, with_network } => { + Scall::revoke_favor { signal, with_network } + } + Call::stand_against { signal, with_network } => { + Scall::stand_against { signal, with_network } + } + }) + } + serai_abi::Call::Dex(call) => { + use serai_abi::dex::Call; + match call { + Call::transfer_liquidity { to, liquidity_tokens } => { + RuntimeCall::LiquidityTokens(serai_coins_pallet::Call::transfer { + to: to.into(), + coins: liquidity_tokens.into(), + }) + } + Call::add_liquidity { .. } | + Call::remove_liquidity { .. } | + Call::swap_exact { .. } | + Call::swap_for_exact { .. } => todo!("TODO"), + } + } + serai_abi::Call::GenesisLiquidity(call) => { + use serai_abi::genesis_liquidity::Call; + match call { + Call::oraclize_values { .. } | Call::remove_liquidity { .. } => todo!("TODO"), + } + } + serai_abi::Call::InInstructions(call) => { + use serai_abi::in_instructions::Call; + match call { + Call::execute_batch { .. } => todo!("TODO"), + } + } + } + } +} + +type Executive = frame_executive::Executive; + +const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); +pub const BABE_GENESIS_EPOCH_CONFIG: sp_consensus_babe::BabeEpochConfiguration = + sp_consensus_babe::BabeEpochConfiguration { + c: PRIMARY_PROBABILITY, + allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryPlainSlots, + }; + +sp_api::impl_runtime_apis! { + impl crate::GenesisApi for Runtime { + fn build(genesis: crate::GenesisConfig) { + let config = RuntimeGenesisConfig { + system: SystemConfig { _config: PhantomData }, + + coins: CoinsConfig { + accounts: genesis.coins.into_iter().map(|(key, balance)| (key.into(), balance)).collect(), + _instance: PhantomData, + }, + + liquidity_tokens: LiquidityTokensConfig { accounts: vec![], _instance: PhantomData }, + + validator_sets: ValidatorSetsConfig { + participants: + genesis.validators.into_iter().map(|(key, keys)| (key.into(), keys)).collect(), + }, + signals: SignalsConfig::default(), + + // We leave these `authorities` empty as `serai-validator-sets-pallet` initializes them + babe: BabeConfig { + authorities: vec![], + epoch_config: BABE_GENESIS_EPOCH_CONFIG, + _config: PhantomData, + }, + grandpa: GrandpaConfig { authorities: vec![], _config: PhantomData }, + }; + + Core::genesis(); + Core::start_transaction(); + ::build(&config); + Core::end_transaction([0; 32]); + + < + serai_core_pallet::EndOfBlock as frame_support::traits::PostTransactions + >::post_transactions(); + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + fn initialize_block(header: &Header) -> sp_runtime::ExtrinsicInclusionMode { + Executive::initialize_block(header) + } + fn execute_block(block: Block) { + Executive::execute_block(block); + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic( + extrinsic: ::Extrinsic, + ) -> sp_runtime::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: sp_runtime::transaction_validity::TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> sp_runtime::transaction_validity::TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_consensus_babe::BabeApi for Runtime { + fn configuration() -> sp_consensus_babe::BabeConfiguration { + use frame_support::traits::Get; + + let epoch_config = Babe::epoch_config().unwrap_or(BABE_GENESIS_EPOCH_CONFIG); + sp_consensus_babe::BabeConfiguration { + slot_duration: Babe::slot_duration(), + epoch_length: ::EpochDuration::get(), + c: epoch_config.c, + authorities: Babe::authorities().to_vec(), + randomness: Babe::randomness(), + allowed_slots: epoch_config.allowed_slots, + } + } + + fn current_epoch_start() -> sp_consensus_babe::Slot { + Babe::current_epoch_start() + } + + fn current_epoch() -> sp_consensus_babe::Epoch { + Babe::current_epoch() + } + + fn next_epoch() -> sp_consensus_babe::Epoch { + Babe::next_epoch() + } + + // TODO: Revisit + fn generate_key_ownership_proof( + _slot: sp_consensus_babe::Slot, + _authority_id: sp_consensus_babe::AuthorityId, + ) -> Option { + None + } + + // TODO: Revisit + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: sp_consensus_babe::EquivocationProof
, + _: sp_consensus_babe::OpaqueKeyOwnershipProof, + ) -> Option<()> { + None + } + } + + impl sp_consensus_grandpa::GrandpaApi for Runtime { + fn grandpa_authorities() -> sp_consensus_grandpa::AuthorityList { + Grandpa::grandpa_authorities() + } + + fn current_set_id() -> sp_consensus_grandpa::SetId { + Grandpa::current_set_id() + } + + // TODO: Revisit + fn generate_key_ownership_proof( + _set_id: sp_consensus_grandpa::SetId, + _authority_id: sp_consensus_grandpa::AuthorityId, + ) -> Option { + None + } + + // TODO: Revisit + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: sp_consensus_grandpa::EquivocationProof< + ::Hash, + u64, + >, + _: sp_consensus_grandpa::OpaqueKeyOwnershipProof, + ) -> Option<()> { + None + } + } + + impl sp_authority_discovery::AuthorityDiscoveryApi for Runtime { + fn authorities() -> Vec { + // Converts to `[u8; 32]` so it can be hashed + let mut all = alloc::collections::BTreeSet::<[u8; 32]>::new(); + for network in NetworkId::all() { + for participant in + >::validators(network) { + all.insert(sp_core::sr25519::Public::from(participant).into()); + } + } + all + .into_iter() + .map(|id| sp_authority_discovery::AuthorityId::from(sp_core::sr25519::Public::from(id))) + .collect() + } + } + + impl crate::SeraiApi for Runtime { + fn validators(network: NetworkId) -> Vec { + // Returning the latest-decided, not latest and active, means the active set + // may fail to peer find if there isn't sufficient overlap. If a large amount reboot, + // forcing some validators to successfully peer find in order for the threshold to become + // online again, this may cause a liveness failure. + // + // This is assumed not to matter in real life, yet an interesting note. + let Some(session) = ValidatorSets::latest_decided_session(network) else { + return vec![] + }; + ValidatorSets::selected_validators(ValidatorSet { network, session }) + .map(|validator| validator.0.into()) + .collect() + } + } +} + +#[derive(Clone, Default, PartialEq, Eq, Debug)] +struct Context; +impl serai_abi::TransactionContext for Context { + // TODO + const SIGNED_WEIGHT: Weight = Weight::zero(); + + type RuntimeCall = RuntimeCall; + + /// The implicit context to verify transactions with. + fn implicit_context() -> serai_abi::ImplicitContext { + serai_abi::ImplicitContext { + genesis: System::block_hash(0).into(), + protocol_id: [0; 32], // TODO via build script + } + } + + /// If a block is present in the blockchain. + fn block_is_present_in_blockchain(&self, hash: &serai_abi::primitives::BlockHash) -> bool { + serai_core_pallet::Pallet::::block_exists(hash) + } + /// The time embedded into the current block. + fn current_time(&self) -> Option { + todo!("TODO") + } + /// Get the next nonce for an account. + fn next_nonce(&self, signer: &SeraiAddress) -> u32 { + serai_core_pallet::Pallet::::next_nonce(signer) + } + /// If the signer can pay the SRI fee. + fn can_pay_fee( + &self, + signer: &SeraiAddress, + fee: serai_abi::primitives::balance::Amount, + ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { + use serai_abi::primitives::coin::Coin; + if serai_coins_pallet::Pallet::::balance(signer, Coin::Serai) >= fee { + Ok(()) + } else { + Err(sp_runtime::transaction_validity::TransactionValidityError::Invalid( + sp_runtime::transaction_validity::InvalidTransaction::Payment, + )) + } + } + + fn start_transaction(&self) { + Core::start_transaction() + } + fn consume_next_nonce(&self, signer: &SeraiAddress) { + serai_core_pallet::Pallet::::consume_next_nonce(signer) + } + /// Have the transaction pay its SRI fee. + fn pay_fee( + &self, + signer: &SeraiAddress, + fee: serai_abi::primitives::balance::Amount, + ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { + use serai_abi::primitives::{coin::*, balance::*}; + serai_coins_pallet::Pallet::::burn( + RuntimeOrigin::signed(Public::from(*signer)), + Balance { coin: Coin::Serai, amount: fee }, + ) + .map_err(|_| { + sp_runtime::transaction_validity::TransactionValidityError::Invalid( + sp_runtime::transaction_validity::InvalidTransaction::Payment, + ) + }) + } + fn end_transaction(&self, transaction_hash: [u8; 32]) { + Core::end_transaction(transaction_hash); + } +} + +/* TODO +use validator_sets::MembershipProof; + +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + + pub const SS58Prefix: u8 = 42; // TODO: Remove for Bech32m + + // 1 MB block size limit + pub BlockLength: system::limits::BlockLength = + system::limits::BlockLength::max_with_normal_ratio(BLOCK_SIZE, NORMAL_DISPATCH_RATIO); + pub BlockWeights: system::limits::BlockWeights = + system::limits::BlockWeights::with_sensible_defaults( + Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), + NORMAL_DISPATCH_RATIO, + ); +} + +impl timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = Babe; + type MinimumPeriod = ConstU64<{ (TARGET_BLOCK_TIME * 1000) / 2 }>; + type WeightInfo = (); +} + +impl coins::Config for Runtime { + type AllowMint = ValidatorSets; +} + +impl dex::Config for Runtime { + type LPFee = ConstU32<3>; // 0.3% + type MintMinLiquidity = ConstU64<10000>; + + type MaxSwapPathLength = ConstU32<3>; // coin1 -> SRI -> coin2 + + type MedianPriceWindowLength = ConstU16<{ MEDIAN_PRICE_WINDOW_LENGTH }>; + + type WeightInfo = dex::weights::SubstrateWeight; +} + +pub struct IdentityValidatorIdOf; +impl Convert> for IdentityValidatorIdOf { + fn convert(key: PublicKey) -> Option { + Some(key) + } +} + +impl signals::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // 1 week + #[allow(clippy::cast_possible_truncation)] + type RetirementValidityDuration = ConstU32<{ (7 * 24 * 60 * 60) / (TARGET_BLOCK_TIME as u32) }>; + // 2 weeks + #[allow(clippy::cast_possible_truncation)] + type RetirementLockInDuration = ConstU32<{ (2 * 7 * 24 * 60 * 60) / (TARGET_BLOCK_TIME as u32) }>; +} + +impl in_instructions::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +impl genesis_liquidity::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +impl emissions::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +impl economic_security::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +// for publishing equivocation evidences. +impl frame_system::offchain::SendTransactionTypes for Runtime +where + RuntimeCall: From, +{ + type Extrinsic = Transaction; + type OverarchingCall = RuntimeCall; +} + +// for validating equivocation evidences. +// The following runtime construction doesn't actually implement the pallet as doing so is +// unnecessary +// TODO: Replace the requirement on Config for a requirement on FindAuthor directly +impl pallet_authorship::Config for Runtime { + type FindAuthor = ValidatorSets; + type EventHandler = (); +} + +/// Longevity of an offence report. +pub type ReportLongevity = ::EpochDuration; + +#[cfg(feature = "runtime-benchmarks")] +#[macro_use] +extern crate frame_benchmarking; + +#[cfg(feature = "runtime-benchmarks")] +mod benches { + define_benchmarks!( + [frame_benchmarking, BaselineBench::] + + [system, SystemBench::] + + [pallet_timestamp, Timestamp] + + [balances, Balances] + + [babe, Babe] + [grandpa, Grandpa] + ); +} + +sp_api::impl_runtime_apis! { + impl validator_sets::ValidatorSetsApi for Runtime { + fn external_network_key(network: ExternalNetworkId) -> Option> { + ValidatorSets::external_network_key(network) + } + } + + impl dex::DexApi for Runtime { + fn quote_price_exact_tokens_for_tokens( + coin1: Coin, + coin2: Coin, + amount: SubstrateAmount, + include_fee: bool + ) -> Option { + Dex::quote_price_exact_tokens_for_tokens(coin1, coin2, amount, include_fee) + } + + fn quote_price_tokens_for_exact_tokens( + coin1: Coin, + coin2: Coin, + amount: SubstrateAmount, + include_fee: bool + ) -> Option { + Dex::quote_price_tokens_for_exact_tokens(coin1, coin2, amount, include_fee) + } + + fn get_reserves(coin1: Coin, coin2: Coin) -> Option<(SubstrateAmount, SubstrateAmount)> { + Dex::get_reserves(&coin1, &coin2).ok() + } + } +} +*/