Round out the runtime

Ensures the block's size limit is respected.

Defines a policy for weights. While I'm unsure I want to commit to this
forever, I do want to acknowledge it's valid and well-defined.

Cleans up the `serai-runtime` crate a bit with further modules in the `wasm`
folder.
This commit is contained in:
Luke Parker
2025-12-02 21:04:47 -05:00
parent 98044f93b1
commit ff95c58341
10 changed files with 347 additions and 325 deletions

View File

@@ -63,6 +63,11 @@ pub struct HeaderV1 {
pub consensus_commitment: [u8; 32],
}
impl HeaderV1 {
/// The size of a serialized V1 header.
pub const SIZE: usize = 8 + 32 + 8 + 32 + 32 + 32;
}
/// A header for a block.
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Header {
@@ -71,6 +76,9 @@ pub enum Header {
}
impl Header {
/// The size of a serialized header.
pub const SIZE: usize = 1 + HeaderV1::SIZE;
/// Get the hash of the header.
pub fn number(&self) -> u64 {
match self {
@@ -109,8 +117,8 @@ impl Header {
/// A block.
///
/// This does not guarantee consistency. The header's `transactions_root` may not match the
/// contained transactions.
/// This does not guarantee consistency nor validity. The header's `transactions_root` may not
/// match the contained transactions, among other ill effects.
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct Block {
/// The block's header.
@@ -119,6 +127,13 @@ pub struct Block {
pub transactions: Vec<Transaction>,
}
impl Block {
/// The size limit for a block.
///
/// This is not enforced upon deserialization. Be careful accordingly.
pub const SIZE_LIMIT: usize = 1024 * 1024;
}
#[cfg(feature = "substrate")]
mod substrate {
use core::fmt::Debug;
@@ -133,7 +148,7 @@ mod substrate {
use super::*;
// Add `serde` implementations which treat self as a `Vec<u8>`
// Add `serde` implementations which treat `self` as a `Vec<u8>`
impl sp_core::serde::Serialize for Transaction {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View File

@@ -279,12 +279,13 @@ mod substrate {
/// The implicit context to verify transactions with.
fn implicit_context() -> ImplicitContext;
/// The size of the current block.
fn current_block_size(&self) -> usize;
/// If a block is present in the blockchain.
fn block_is_present_in_blockchain(&self, hash: &BlockHash) -> bool;
/// The time embedded into the current block.
///
/// Returns `None` if the time has yet to be set.
fn current_time(&self) -> Option<u64>;
fn current_time(&self) -> u64;
/// Get the next nonce for an account.
fn next_nonce(&self, signer: &SeraiAddress) -> u32;
/// If the signer can pay the SRI fee.
@@ -295,7 +296,7 @@ mod substrate {
) -> Result<(), TransactionValidityError>;
/// Begin execution of a transaction.
fn start_transaction(&self);
fn start_transaction(&self, len: usize);
/// Consume the next nonce for an account.
///
/// This MUST NOT be called if the next nonce is `u32::MAX`. The caller MAY panic in that case.
@@ -390,9 +391,14 @@ mod substrate {
impl<Context: TransactionContext> TransactionWithContext<Context> {
fn validate_except_fee<V: ValidateUnsigned<Call = Context::RuntimeCall>>(
&self,
len: usize,
source: TransactionSource,
mempool_priority_if_signed: u64,
) -> TransactionValidity {
if self.1.current_block_size().saturating_add(len) > crate::Block::SIZE_LIMIT {
Err(TransactionValidityError::Invalid(InvalidTransaction::ExhaustsResources))?;
}
match &self.0 {
Transaction::Unsigned { call } => {
let ValidTransaction { priority: _, requires, provides, longevity: _, propagate: _ } =
@@ -417,13 +423,8 @@ mod substrate {
Err(TransactionValidityError::Unknown(UnknownTransaction::CannotLookup))?;
}
if let Some(include_by) = *include_by {
if let Some(current_time) = self.1.current_time() {
if current_time >= u64::from(include_by) {
// Since this transaction has a time bound which has passed, error
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?;
}
} else {
// Since this transaction has a time bound, yet we don't know the time, error
if self.1.current_time() >= u64::from(include_by) {
// Since this transaction has a time bound which has passed, error
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?;
}
}
@@ -471,7 +472,7 @@ mod substrate {
&self,
source: TransactionSource,
info: &DispatchInfo,
_len: usize,
len: usize,
) -> TransactionValidity {
let mempool_priority_if_signed = match &self.0 {
Transaction::Unsigned { .. } => {
@@ -493,19 +494,19 @@ mod substrate {
}
}
};
self.validate_except_fee::<V>(source, mempool_priority_if_signed)
self.validate_except_fee::<V>(len, source, mempool_priority_if_signed)
}
fn apply<V: ValidateUnsigned<Call = Context::RuntimeCall>>(
self,
_info: &DispatchInfo,
_len: usize,
len: usize,
) -> sp_runtime::ApplyExtrinsicResultWithInfo<PostDispatchInfo> {
// We use 0 for the mempool priority, as this is no longer in the mempool so it's irrelevant
self.validate_except_fee::<V>(TransactionSource::InBlock, 0)?;
self.validate_except_fee::<V>(len, TransactionSource::InBlock, 0)?;
// Start the transaction
self.1.start_transaction();
self.1.start_transaction(len);
let transaction_hash = self.0.hash();

View File

@@ -21,6 +21,8 @@ impl frame_system::Config for Test {
type AccountId = sp_core::sr25519::Public;
type Lookup = frame_support::sp_runtime::traits::IdentityLookup<Self::AccountId>;
type Block = frame_system::mocking::MockBlock<Test>;
type BlockLength = serai_core_pallet::Limits;
type BlockWeights = serai_core_pallet::Limits;
}
#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)]

View File

@@ -10,7 +10,7 @@ pub type CoinsEvent = serai_abi::coins::Event;
#[test]
fn mint() {
new_test_ext().execute_with(|| {
Core::start_transaction();
Core::start_transaction(0);
// minting u64::MAX should work
let coin = Coin::Serai;
@@ -51,7 +51,7 @@ fn mint() {
#[test]
fn burn_with_instruction() {
new_test_ext().execute_with(|| {
Core::start_transaction();
Core::start_transaction(0);
// mint some coin
let coin = Coin::External(ExternalCoin::Bitcoin);
@@ -106,7 +106,7 @@ fn burn_with_instruction() {
#[test]
fn transfer() {
new_test_ext().execute_with(|| {
Core::start_transaction();
Core::start_transaction(0);
// mint some coin
let coin = Coin::External(ExternalCoin::Bitcoin);

View File

@@ -8,6 +8,9 @@ extern crate alloc;
use frame_support::traits::{PreInherents, PostTransactions};
mod limits;
pub use limits::Limits;
mod iumt;
pub use iumt::*;
@@ -83,7 +86,8 @@ pub mod pallet {
#[pallet::config]
pub trait Config:
frame_system::Config<Hash: Into<[u8; 32]>> + pallet_timestamp::Config<Moment = u64>
frame_system::Config<Hash: Into<[u8; 32]>, BlockLength = Limits, BlockWeights = Limits>
+ pallet_timestamp::Config<Moment = u64>
{
}
@@ -120,7 +124,7 @@ pub mod pallet {
BlockTransactionsCommitmentMerkle::<T>::new_expecting_none();
BlockEventsCommitmentMerkle::<T>::new_expecting_none();
Self::start_transaction();
Self::start_transaction(0);
<_>::build(config);
Self::end_transaction([0; 32]);
@@ -130,7 +134,15 @@ pub mod pallet {
/// The code to run when beginning execution of a transaction.
///
/// The caller MUST ensure two transactions aren't simultaneously started.
pub fn start_transaction() {
pub fn start_transaction(len: usize) {
{
let existing_len = frame_system::AllExtrinsicsLen::<T>::get().unwrap_or(0);
let new_len = existing_len.saturating_add(u32::try_from(len).unwrap_or(u32::MAX));
// We panic here as this should've been caught earlier during validation
assert!(new_len <= u32::try_from(serai_abi::Block::SIZE_LIMIT).unwrap());
frame_system::AllExtrinsicsLen::<T>::set(Some(new_len));
}
TransactionEventsMerkle::<T>::new_expecting_none();
Self::deposit_event(Event::BeginTransaction);
}
@@ -192,7 +204,21 @@ impl<T: Config> PreInherents for StartOfBlock<T> {
BlockTransactionsCommitmentMerkle::<T>::new_expecting_none();
BlockEventsCommitmentMerkle::<T>::new_expecting_none();
Pallet::<T>::start_transaction();
/*
We assign the implicit transaction with the block the length of the block itself: its
header's length and the length of the length-prefix for the list of transactions.
The length-prefix will be a little-endian `u32`, as `Block` will be borsh-serialized
(https://borsh.io).
The length of each actual transaction is expected to be accurate as the SCALE implementation
defers to the `borsh` serialization.
*/
assert!(
frame_system::AllExtrinsicsLen::<T>::get().is_none(),
"AllExtrinsicsLen wasn't killed at the end of the last block"
);
Pallet::<T>::start_transaction(serai_abi::Header::SIZE + 4);
// Handle the `SeraiPreExecutionDigest`
/*
@@ -220,7 +246,7 @@ impl<T: Config> PreInherents for StartOfBlock<T> {
pub struct EndOfBlock<T: Config>(PhantomData<T>);
impl<T: Config> PostTransactions for EndOfBlock<T> {
fn post_transactions() {
Pallet::<T>::start_transaction();
Pallet::<T>::start_transaction(0);
// Other modules' `PostTransactions`
@@ -229,6 +255,8 @@ impl<T: Config> PostTransactions for EndOfBlock<T> {
end_of_block_transaction_hash[.. 16].copy_from_slice(&[0xff; 16]);
Pallet::<T>::end_transaction(end_of_block_transaction_hash);
frame_system::AllExtrinsicsLen::<T>::kill();
use serai_abi::SeraiExecutionDigest;
frame_system::Pallet::<T>::deposit_log(
frame_support::sp_runtime::generic::DigestItem::Consensus(

View File

@@ -0,0 +1,34 @@
use sp_core::Get;
use frame_support::weights::Weight;
use frame_system::limits::{BlockLength, BlockWeights};
/// The limits for the Serai protocol.
pub struct Limits;
impl Get<BlockLength> for Limits {
fn get() -> BlockLength {
/*
We do not reserve an allocation for mandatory/operational transactions, assuming they'll be
prioritized in the mempool. This does technically give block producers an inventive to
misbehave by on-purposely favoring paying non-operational transactions over operational
transactions, but ensures the entire block is available to the transactions actually present
in the mempool.
*/
BlockLength::max(u32::try_from(serai_abi::Block::SIZE_LIMIT).unwrap())
}
}
impl Get<BlockWeights> for Limits {
fn get() -> BlockWeights {
/*
While Serai does limit the size of a block, every transaction is expected to operate in
complexity constant to the current state size, regardless of what the state is. Accordingly,
the most efficient set of transactions (basic transfers?) is expected to be within an order
of magnitude of the most expensive transactions (multi-pool swaps?).
Instead of engaging with the complexity within the consensus protocol of metering both
bandwidth and computation, we do not define limits for weights. We do, however, still use the
weight system in order to determine fee rates and ensure prioritization to
computationally-cheaper transactions. That solely serves as mempool policy however.
*/
BlockWeights::simple_max(Weight::MAX)
}
}

View File

@@ -25,6 +25,8 @@ impl frame_system::Config for Test {
type AccountId = sp_core::sr25519::Public;
type Lookup = frame_support::sp_runtime::traits::IdentityLookup<Self::AccountId>;
type Block = frame_system::mocking::MockBlock<Test>;
type BlockLength = serai_core_pallet::Limits;
type BlockWeights = serai_core_pallet::Limits;
}
#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)]

View File

@@ -0,0 +1,127 @@
use super::*;
impl From<Option<SeraiAddress>> for RuntimeOrigin {
fn from(signer: Option<SeraiAddress>) -> Self {
match signer {
None => RuntimeOrigin::none(),
Some(signer) => RuntimeOrigin::signed(signer.into()),
}
}
}
impl From<serai_abi::Call> 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;
RuntimeCall::Dex(match call {
Call::add_liquidity {
external_coin,
sri_intended,
external_coin_intended,
sri_minimum,
external_coin_minimum,
} => serai_dex_pallet::Call::add_liquidity {
external_coin,
sri_intended,
external_coin_intended,
sri_minimum,
external_coin_minimum,
},
Call::transfer_liquidity { to, liquidity_tokens } => {
serai_dex_pallet::Call::transfer_liquidity { to, liquidity_tokens }
}
Call::remove_liquidity { liquidity_tokens, sri_minimum, external_coin_minimum } => {
serai_dex_pallet::Call::remove_liquidity {
liquidity_tokens,
sri_minimum,
external_coin_minimum,
}
}
Call::swap { coins_to_swap, minimum_to_receive } => {
serai_dex_pallet::Call::swap { coins_to_swap, minimum_to_receive }
}
Call::swap_for { coins_to_receive, maximum_to_swap } => {
serai_dex_pallet::Call::swap_for { coins_to_receive, maximum_to_swap }
}
})
}
serai_abi::Call::GenesisLiquidity(call) => {
use serai_abi::genesis_liquidity::Call;
RuntimeCall::GenesisLiquidity(match call {
Call::oraclize_values { values, signature } => {
serai_genesis_liquidity_pallet::Call::oraclize_values { values, signature }
}
Call::transfer_genesis_liquidity { to, genesis_liquidity } => {
serai_genesis_liquidity_pallet::Call::transfer_genesis_liquidity {
to,
genesis_liquidity,
}
}
Call::remove_genesis_liquidity { genesis_liquidity } => {
serai_genesis_liquidity_pallet::Call::remove_genesis_liquidity { genesis_liquidity }
}
})
}
serai_abi::Call::InInstructions(call) => {
use serai_abi::in_instructions::Call;
RuntimeCall::InInstructions(match call {
Call::execute_batch { batch } => {
serai_in_instructions_pallet::Call::execute_batch { batch }
}
})
}
}
}
}

View File

@@ -1,7 +1,7 @@
use core::marker::PhantomData;
use alloc::{borrow::Cow, vec, vec::Vec};
use sp_core::{ConstU32, ConstU64, sr25519::Public};
use sp_core::{Get, ConstU32, ConstU64, sr25519::Public};
use sp_runtime::{
Perbill, Weight,
traits::{Header as _, Block as _},
@@ -21,51 +21,10 @@ use serai_abi::{
use serai_coins_pallet::{CoinsInstance, LiquidityTokensInstance};
/// 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<Public, sp_runtime::traits::LookupError> {
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),
);
}
/// Maps `serai_abi` types into the types expected within the Substrate runtime
mod map;
/// The configuration for `frame_system`.
mod system;
#[frame_support::runtime]
mod runtime {
@@ -113,45 +72,6 @@ mod runtime {
pub type Grandpa = pallet_grandpa::Pallet<Runtime>;
}
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 Nonce = u32;
type Hash = <Self::Block as sp_runtime::traits::Block>::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<Runtime>;
type PostInherents = ();
type PostTransactions = serai_core_pallet::EndOfBlock<Runtime>;
}
impl serai_core_pallet::Config for Runtime {}
impl serai_coins_pallet::Config<CoinsInstance> for Runtime {
@@ -237,137 +157,6 @@ impl pallet_grandpa::Config for Runtime {
type EquivocationReportSystem = ();
}
impl From<Option<SeraiAddress>> for RuntimeOrigin {
fn from(signer: Option<SeraiAddress>) -> Self {
match signer {
None => RuntimeOrigin::none(),
Some(signer) => RuntimeOrigin::signed(signer.into()),
}
}
}
impl From<serai_abi::Call> 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::add_liquidity {
external_coin,
sri_intended,
external_coin_intended,
sri_minimum,
external_coin_minimum,
} => RuntimeCall::Dex(serai_dex_pallet::Call::add_liquidity {
external_coin,
sri_intended,
external_coin_intended,
sri_minimum,
external_coin_minimum,
}),
Call::transfer_liquidity { to, liquidity_tokens } => {
RuntimeCall::Dex(serai_dex_pallet::Call::transfer_liquidity { to, liquidity_tokens })
}
Call::remove_liquidity { liquidity_tokens, sri_minimum, external_coin_minimum } => {
RuntimeCall::Dex(serai_dex_pallet::Call::remove_liquidity {
liquidity_tokens,
sri_minimum,
external_coin_minimum,
})
}
Call::swap { coins_to_swap, minimum_to_receive } => {
RuntimeCall::Dex(serai_dex_pallet::Call::swap { coins_to_swap, minimum_to_receive })
}
Call::swap_for { coins_to_receive, maximum_to_swap } => {
RuntimeCall::Dex(serai_dex_pallet::Call::swap_for { coins_to_receive, maximum_to_swap })
}
}
}
serai_abi::Call::GenesisLiquidity(call) => {
use serai_abi::genesis_liquidity::Call;
match call {
Call::oraclize_values { values, signature } => {
RuntimeCall::GenesisLiquidity(serai_genesis_liquidity_pallet::Call::oraclize_values {
values,
signature,
})
}
Call::transfer_genesis_liquidity { to, genesis_liquidity } => {
RuntimeCall::GenesisLiquidity(
serai_genesis_liquidity_pallet::Call::transfer_genesis_liquidity {
to,
genesis_liquidity,
},
)
}
Call::remove_genesis_liquidity { genesis_liquidity } => RuntimeCall::GenesisLiquidity(
serai_genesis_liquidity_pallet::Call::remove_genesis_liquidity { genesis_liquidity },
),
}
}
serai_abi::Call::InInstructions(call) => {
use serai_abi::in_instructions::Call;
match call {
Call::execute_batch { batch } => {
RuntimeCall::InInstructions(serai_in_instructions_pallet::Call::execute_batch { batch })
}
}
}
}
}
}
type Executive = frame_executive::Executive<Runtime, Block, Context, Runtime, AllPalletsWithSystem>;
const PRIMARY_PROBABILITY: (u64, u64) = (1, 4);
@@ -411,7 +200,7 @@ sp_api::impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
VERSION
<Runtime as frame_system::Config>::Version::get()
}
fn initialize_block(header: &Header) -> sp_runtime::ExtrinsicInclusionMode {
Executive::initialize_block(header)
@@ -667,13 +456,19 @@ impl serai_abi::TransactionContext for Context {
}
}
/// The size of the current block.
fn current_block_size(&self) -> usize {
let current_block_size = frame_system::AllExtrinsicsLen::<Runtime>::get().unwrap_or(0);
usize::try_from(current_block_size).unwrap_or(usize::MAX)
}
/// 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::<Runtime>::block_exists(hash)
}
/// The time embedded into the current block.
fn current_time(&self) -> Option<u64> {
todo!("TODO")
fn current_time(&self) -> u64 {
pallet_timestamp::Pallet::<Runtime>::get()
}
/// Get the next nonce for an account.
fn next_nonce(&self, signer: &SeraiAddress) -> u32 {
@@ -695,8 +490,8 @@ impl serai_abi::TransactionContext for Context {
}
}
fn start_transaction(&self) {
Core::start_transaction()
fn start_transaction(&self, len: usize) {
Core::start_transaction(len)
}
fn consume_next_nonce(&self, signer: &SeraiAddress) {
serai_core_pallet::Pallet::<Runtime>::consume_next_nonce(signer)
@@ -726,26 +521,7 @@ impl serai_abi::TransactionContext for Context {
/* 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 = ();
}
@@ -782,14 +558,6 @@ impl signals::Config for Runtime {
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;
}
@@ -818,54 +586,4 @@ impl pallet_authorship::Config for Runtime {
/// Longevity of an offence report.
pub type ReportLongevity = <Runtime as pallet_babe::Config>::EpochDuration;
#[cfg(feature = "runtime-benchmarks")]
#[macro_use]
extern crate frame_benchmarking;
#[cfg(feature = "runtime-benchmarks")]
mod benches {
define_benchmarks!(
[frame_benchmarking, BaselineBench::<Runtime>]
[system, SystemBench::<Runtime>]
[balances, Balances]
[babe, Babe]
[grandpa, Grandpa]
);
}
sp_api::impl_runtime_apis! {
impl validator_sets::ValidatorSetsApi<Block> for Runtime {
fn external_network_key(network: ExternalNetworkId) -> Option<Vec<u8>> {
ValidatorSets::external_network_key(network)
}
}
impl dex::DexApi<Block> for Runtime {
fn quote_price_exact_tokens_for_tokens(
coin1: Coin,
coin2: Coin,
amount: SubstrateAmount,
include_fee: bool
) -> Option<SubstrateAmount> {
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<SubstrateAmount> {
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()
}
}
}
*/

View File

@@ -0,0 +1,95 @@
use super::*;
/// 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<Public, sp_runtime::traits::LookupError> {
Ok(source.into())
}
fn unlookup(source: Public) -> SeraiAddress {
source.into()
}
}
/// The runtime version.
pub struct Version;
// TODO: Are we reasonably able to prune `RuntimeVersion` from Substrate?
impl Get<RuntimeVersion> for Version {
fn get() -> RuntimeVersion {
#[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,
};
VERSION
}
}
impl frame_system::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
type PalletInfo = PalletInfo;
type Hashing = sp_runtime::traits::BlakeTwo256;
type Hash = <Self::Block as sp_runtime::traits::Block>::Hash;
type Block = Block;
type AccountId = sp_core::sr25519::Public;
type Lookup = Lookup;
type Nonce = u32;
type PreInherents = serai_core_pallet::StartOfBlock<Runtime>;
type PostInherents = ();
type PostTransactions = serai_core_pallet::EndOfBlock<Runtime>;
/*
We do not globally filter the types of calls which may be performed. Instead, our ABI only
exposes the calls we want exposed, and each call individually errors if it's called when it
shouldn't be.
*/
type BaseCallFilter = frame_support::traits::Everything;
/*
We do not have `frame_system` track historical block hashes by their block number. Instead,
`serai_core_pallet` populates a hash set (map of `[u8; 32] -> ()`) of all historical block's
hashes within itself.
The usage of `1` here is solely as `frame_system` requires it be at least `1`.
*/
type BlockHashCount = ConstU64<1>;
type Version = Version;
type BlockLength = serai_core_pallet::Limits;
type BlockWeights = serai_core_pallet::Limits;
// We assume `serai-node` will be run using the RocksDB backend
type DbWeight = frame_support::weights::constants::RocksDbWeight;
/*
Serai does not expose `frame_system::Call` nor does it use transaction extensions. We
accordingly have no consequence to using the default weights for these accordingly.
*/
type SystemWeightInfo = ();
type ExtensionsWeightInfo = ();
// We also don't use `frame_system`'s account system at all, leaving us to bottom these out.
type AccountData = ();
type MaxConsumers = ConstU32<{ u32::MAX }>;
type OnNewAccount = ();
type OnKilledAccount = ();
// Serai does perform any 'on-chain upgrades' to ensure upgrades are opted into by the entity
// running this node and accordingly consented to
type OnSetCode = ();
// We do not have any migrations declared
type SingleBlockMigrations = ();
type MultiBlockMigrator = ();
}