serai/substrate/runtime/src/lib.rs
Luke Parker c182b804bc
Move in instructions from inherent transactions to unsigned transactions
The original intent was to use inherent transactions to prevent needing to vote
on-chain, which would spam the chain with worthless votes. Inherent
transactions, and our Tendermint library, would use the BFT's processs voting
to also vote on all included transactions. This perfectly collapses integrity
voting creating *no additional on-chain costs*.

Unfortunately, this led to issues such as #6, along with questions of validator
scalability when all validators are expencted to participate in consensus (in
order to vote on if the included instructions are valid). This has been
summarized in #241.

With this change, we can remove Tendermint from Substrate. This greatly
decreases our complexity. While I'm unhappy with the amount of time spent on
it, just to reach this conclusion, thankfully tendermint-machine itself is
still usable for #163. This also has reached a tipping point recently as the
polkadot-v0.9.40 branch of substrate changed how syncing works, requiring
further changes to sc-tendermint. These have no value if we're just going to
get rid of it later, due to fundamental design issues, yet I would like to
keep Substrate updated.

This should be followed by moving back to GRANDPA, enabling closing most open
Tendermint issues.

Please note the current in-instructions-pallet does not actually verify the
included signature yet. It's marked TODO, despite this bing critical.
2023-03-26 02:58:04 -04:00

449 lines
12 KiB
Rust

#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#![recursion_limit = "256"]
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
// Re-export all components
pub use serai_primitives as primitives;
pub use frame_system as system;
pub use frame_support as support;
pub use pallet_balances as balances;
pub use pallet_transaction_payment as transaction_payment;
pub use pallet_assets as assets;
pub use tokens_pallet as tokens;
pub use in_instructions_pallet as in_instructions;
pub use validator_sets_pallet as validator_sets;
pub use pallet_session as session;
pub use pallet_tendermint as tendermint;
// Actually used by the runtime
use sp_core::OpaqueMetadata;
use sp_std::prelude::*;
use sp_version::RuntimeVersion;
#[cfg(feature = "std")]
use sp_version::NativeVersion;
use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys, KeyTypeId,
traits::{Convert, OpaqueKeys, BlakeTwo256, Block as BlockT},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, Perbill,
};
use primitives::{PublicKey, SeraiAddress, AccountLookup, Signature, SubstrateAmount, Coin};
use support::{
traits::{ConstU8, ConstU32, ConstU64, Contains},
weights::{
constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND},
IdentityFee, Weight,
},
parameter_types, construct_runtime,
};
use transaction_payment::CurrencyAdapter;
use session::PeriodicSessions;
/// An index to a block.
pub type BlockNumber = u64;
/// Index of a transaction in the chain, for a given account.
pub type Index = u32;
/// A hash of some data used by the chain.
pub type Hash = sp_core::H256;
pub mod opaque {
use super::*;
use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
pub type BlockId = generic::BlockId<Block>;
impl_opaque_keys! {
pub struct SessionKeys {
pub tendermint: Tendermint,
}
}
}
use opaque::SessionKeys;
#[sp_version::runtime_version]
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("serai"),
impl_name: create_runtime_str!("core"),
authoring_version: 1,
// TODO: 1? Do we prefer some level of compatibility or our own path?
spec_version: 100,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
state_version: 1,
};
// 1 MB
pub const BLOCK_SIZE: u32 = 1024 * 1024;
// 6 seconds
pub const TARGET_BLOCK_TIME: u64 = 6;
/// Measured in blocks.
pub const MINUTES: BlockNumber = 60 / TARGET_BLOCK_TIME;
pub const HOURS: BlockNumber = MINUTES * 60;
pub const DAYS: BlockNumber = HOURS * 24;
#[cfg(feature = "std")]
pub fn native_version() -> NativeVersion {
NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
}
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
parameter_types! {
pub const BlockHashCount: BlockNumber = 2400;
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_ref_time(2u64 * WEIGHT_REF_TIME_PER_SECOND).set_proof_size(u64::MAX),
NORMAL_DISPATCH_RATIO,
);
}
pub struct CallFilter;
impl Contains<RuntimeCall> for CallFilter {
fn contains(call: &RuntimeCall) -> bool {
if let RuntimeCall::Balances(call) = call {
return matches!(call, balances::Call::transfer { .. } | balances::Call::transfer_all { .. });
}
if let RuntimeCall::Assets(call) = call {
return matches!(
call,
assets::Call::approve_transfer { .. } |
assets::Call::cancel_approval { .. } |
assets::Call::transfer { .. } |
assets::Call::transfer_approved { .. }
);
}
if let RuntimeCall::Tokens(call) = call {
return matches!(call, tokens::Call::burn { .. });
}
if let RuntimeCall::InInstructions(call) = call {
return matches!(call, in_instructions::Call::execute_batch { .. });
}
if let RuntimeCall::ValidatorSets(call) = call {
return matches!(call, validator_sets::Call::vote { .. });
}
false
}
}
impl system::Config for Runtime {
type BaseCallFilter = CallFilter;
type BlockWeights = BlockWeights;
type BlockLength = BlockLength;
type AccountId = PublicKey;
type RuntimeCall = RuntimeCall;
type Lookup = AccountLookup;
type Index = Index;
type BlockNumber = BlockNumber;
type Hash = Hash;
type Hashing = BlakeTwo256;
type Header = Header;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type DbWeight = RocksDbWeight;
type Version = Version;
type PalletInfo = PalletInfo;
type OnNewAccount = ();
type OnKilledAccount = ();
type OnSetCode = ();
type AccountData = balances::AccountData<SubstrateAmount>;
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix; // TODO: Remove for Bech32m
type MaxConsumers = support::traits::ConstU32<16>;
}
impl balances::Config for Runtime {
type MaxLocks = ConstU32<50>;
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type Balance = SubstrateAmount;
type RuntimeEvent = RuntimeEvent;
type DustRemoval = ();
type ExistentialDeposit = ConstU64<500>;
type AccountStore = System;
type WeightInfo = balances::weights::SubstrateWeight<Runtime>;
}
impl transaction_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
type OperationalFeeMultiplier = ConstU8<5>;
type WeightToFee = IdentityFee<SubstrateAmount>;
type LengthToFee = IdentityFee<SubstrateAmount>;
type FeeMultiplierUpdate = ();
}
impl assets::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Balance = SubstrateAmount;
type Currency = Balances;
type AssetId = Coin;
type AssetIdParameter = Coin;
type StringLimit = ConstU32<32>;
// Don't allow anyone to create assets
type CreateOrigin = support::traits::AsEnsureOriginWithArg<system::EnsureNever<PublicKey>>;
type ForceOrigin = system::EnsureRoot<PublicKey>;
// Don't charge fees nor kill accounts
type RemoveItemsLimit = ConstU32<0>;
type AssetDeposit = ConstU64<0>;
type AssetAccountDeposit = ConstU64<0>;
type MetadataDepositBase = ConstU64<0>;
type MetadataDepositPerByte = ConstU64<0>;
type ApprovalDeposit = ConstU64<0>;
// Unused hooks
type CallbackHandle = ();
type Freezer = ();
type Extra = ();
type WeightInfo = assets::weights::SubstrateWeight<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
impl tokens::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl in_instructions::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
const SESSION_LENGTH: BlockNumber = 5 * DAYS;
type Sessions = PeriodicSessions<ConstU64<{ SESSION_LENGTH }>, ConstU64<{ SESSION_LENGTH }>>;
pub struct IdentityValidatorIdOf;
impl Convert<PublicKey, Option<PublicKey>> for IdentityValidatorIdOf {
fn convert(key: PublicKey) -> Option<PublicKey> {
Some(key)
}
}
impl validator_sets::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
impl session::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type ValidatorId = PublicKey;
type ValidatorIdOf = IdentityValidatorIdOf;
type ShouldEndSession = Sessions;
type NextSessionRotation = Sessions;
type SessionManager = ();
type SessionHandler = <SessionKeys as OpaqueKeys>::KeyTypeIdProviders;
type Keys = SessionKeys;
type WeightInfo = session::weights::SubstrateWeight<Runtime>;
}
impl tendermint::Config for Runtime {}
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
pub type SignedExtra = (
system::CheckNonZeroSender<Runtime>,
system::CheckSpecVersion<Runtime>,
system::CheckTxVersion<Runtime>,
system::CheckGenesis<Runtime>,
system::CheckEra<Runtime>,
system::CheckNonce<Runtime>,
system::CheckWeight<Runtime>,
transaction_payment::ChargeTransactionPayment<Runtime>,
);
pub type UncheckedExtrinsic =
generic::UncheckedExtrinsic<SeraiAddress, RuntimeCall, Signature, SignedExtra>;
pub type SignedPayload = generic::SignedPayload<RuntimeCall, SignedExtra>;
pub type Executive = frame_executive::Executive<
Runtime,
Block,
system::ChainContext<Runtime>,
Runtime,
AllPalletsWithSystem,
>;
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: system,
Balances: balances,
TransactionPayment: transaction_payment,
Assets: assets,
Tokens: tokens,
InInstructions: in_instructions,
ValidatorSets: validator_sets,
Session: session,
Tendermint: tendermint,
}
);
#[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]
);
}
sp_api::impl_runtime_apis! {
impl sp_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
VERSION
}
fn execute_block(block: Block) {
Executive::execute_block(block);
}
fn initialize_block(header: &<Block as BlockT>::Header) {
Executive::initialize_block(header)
}
}
impl sp_api::Metadata<Block> for Runtime {
fn metadata() -> OpaqueMetadata {
OpaqueMetadata::new(Runtime::metadata().into())
}
}
impl sp_block_builder::BlockBuilder<Block> for Runtime {
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
Executive::apply_extrinsic(extrinsic)
}
fn finalize_block() -> <Block as BlockT>::Header {
Executive::finalize_block()
}
fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::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<Block> for Runtime {
fn validate_transaction(
source: TransactionSource,
tx: <Block as BlockT>::Extrinsic,
block_hash: <Block as BlockT>::Hash,
) -> TransactionValidity {
Executive::validate_transaction(source, tx, block_hash)
}
}
impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
fn offchain_worker(header: &<Block as BlockT>::Header) {
Executive::offchain_worker(header)
}
}
impl sp_session::SessionKeys<Block> for Runtime {
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
opaque::SessionKeys::generate(seed)
}
fn decode_session_keys(
encoded: Vec<u8>,
) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {
opaque::SessionKeys::decode_into_raw_public_keys(&encoded)
}
}
impl sp_tendermint::TendermintApi<Block> for Runtime {
fn current_session() -> u32 {
Tendermint::session()
}
fn validators() -> Vec<PublicKey> {
Session::validators().drain(..).map(Into::into).collect()
}
}
impl frame_system_rpc_runtime_api::AccountNonceApi<Block, PublicKey, Index> for Runtime {
fn account_nonce(account: PublicKey) -> Index {
System::account_nonce(account)
}
}
impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<
Block,
SubstrateAmount
> for Runtime {
fn query_info(
uxt: <Block as BlockT>::Extrinsic,
len: u32,
) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<SubstrateAmount> {
TransactionPayment::query_info(uxt, len)
}
fn query_fee_details(
uxt: <Block as BlockT>::Extrinsic,
len: u32,
) -> transaction_payment::FeeDetails<SubstrateAmount> {
TransactionPayment::query_fee_details(uxt, len)
}
fn query_weight_to_fee(weight: Weight) -> SubstrateAmount {
TransactionPayment::weight_to_fee(weight)
}
fn query_length_to_fee(length: u32) -> SubstrateAmount {
TransactionPayment::length_to_fee(length)
}
}
}