domain_runtime_primitives/lib.rs
1// Copyright (C) 2021 Subspace Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Common primitives for subspace domain runtime.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19
20#[cfg(not(feature = "std"))]
21extern crate alloc;
22
23#[cfg(not(feature = "std"))]
24use alloc::string::String;
25#[cfg(not(feature = "std"))]
26use alloc::vec::Vec;
27pub use fp_account::{AccountId20, EthereumSignature as EVMSignature};
28use frame_support::dispatch::DispatchClass;
29use frame_support::weights::constants::{BlockExecutionWeight, ExtrinsicBaseWeight};
30use frame_system::limits::{BlockLength, BlockWeights};
31use parity_scale_codec::{Decode, Encode};
32use scale_info::TypeInfo;
33use serde::{Deserialize, Serialize};
34use sp_core::parameter_types;
35use sp_runtime::generic::{ExtensionVersion, Preamble, UncheckedExtrinsic};
36use sp_runtime::traits::transaction_extension::TransactionExtension;
37use sp_runtime::traits::{Convert, Dispatchable, IdentifyAccount, Verify};
38use sp_runtime::transaction_validity::TransactionValidityError;
39use sp_runtime::{MultiAddress, MultiSignature, Perbill, Perquintill};
40use sp_weights::Weight;
41use sp_weights::constants::WEIGHT_REF_TIME_PER_SECOND;
42pub use subspace_runtime_primitives::HoldIdentifier;
43use subspace_runtime_primitives::{MAX_BLOCK_LENGTH, SHANNON};
44
45/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
46pub type Signature = MultiSignature;
47
48/// Some way of identifying an account on the chain. We intentionally make it equivalent
49/// to the public key of our transaction signing scheme.
50//
51// Note: sometimes this type alias causes complex trait ambiguity / conflicting implementation errors.
52// As a workaround, `use sp_runtime::AccountId32 as AccountId` instead.
53pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
54
55/// Balance of an account.
56pub type Balance = u128;
57
58/// Index of a transaction in the chain.
59pub type Nonce = u32;
60
61/// A hash of some data used by the chain.
62pub type Hash = sp_core::H256;
63
64/// An index to a block.
65pub type BlockNumber = u32;
66
67/// The address format for describing accounts.
68pub type Address = MultiAddress<AccountId, ()>;
69
70/// Slot duration that is same as consensus chain runtime.
71pub const SLOT_DURATION: u64 = 1000;
72
73/// The EVM chain Id type
74pub type EVMChainId = u64;
75
76/// Default EVM chain ID used for testing and Genesis domains for Devnet and Local net.
77pub const DEFAULT_EVM_CHAIN_ID: EVMChainId = 870;
78
79/// Alias to 512-bit hash when used in the context of a transaction signature on the EVM chain.
80pub type EthereumSignature = EVMSignature;
81
82/// Some way of identifying an account on the EVM chain. We intentionally make it equivalent
83/// to the public key of the EVM transaction signing scheme.
84//
85// Note: sometimes this type alias causes complex trait ambiguity / conflicting implementation errors.
86// As a workaround, `use fp_account::AccountId20 as EthereumAccountId` instead.
87pub type EthereumAccountId = <<EthereumSignature as Verify>::Signer as IdentifyAccount>::AccountId;
88
89/// Dispatch ratio for domains
90pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(65);
91
92// Max outgoing messages for XDM
93pub const MAX_OUTGOING_MESSAGES: u32 = 10_000;
94
95/// The maximum domain block weight with 3.25 MiB as proof size
96/// Consensus allows 3.75 MiB but Fraud proof can carry extra size along with proof size
97/// So we set the proof size to 3.25 MiB
98pub fn maximum_domain_block_weight() -> Weight {
99 let consensus_maximum_normal_block_length =
100 *maximum_block_length().max.get(DispatchClass::Normal) as u64;
101 let weight =
102 NORMAL_DISPATCH_RATIO * Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), 0);
103 weight.set_proof_size(consensus_maximum_normal_block_length)
104}
105
106// When updating these error codes, check for clashes between:
107// <https://github.com/autonomys/subspace/blob/main/crates/sp-domains-fraud-proof/src/lib.rs#L49-L64>
108// <https://github.com/autonomys/subspace/blob/main/domains/pallets/messenger/src/lib.rs#L49-L53>
109
110/// Custom error when an EVM nonce overflow occurs.
111pub const ERR_EVM_NONCE_OVERFLOW: u8 = 100;
112/// Custom error when balance overflow occurs.
113pub const ERR_BALANCE_OVERFLOW: u8 = 200;
114/// Custom error when a user tries to create a contract, but their account is not on the allow
115/// list.
116pub const ERR_CONTRACT_CREATION_NOT_ALLOWED: u8 = 210;
117
118/// Maximum block length for all dispatches.
119/// This is set to 3.75 MiB since consensus chain supports on 3.75 MiB for normal
120pub fn maximum_block_length() -> BlockLength {
121 BlockLength::max_with_normal_ratio(MAX_BLOCK_LENGTH, NORMAL_DISPATCH_RATIO)
122}
123
124/// Default version of the Extension used to construct the inherited implication for legacy transactions.
125// https://github.com/paritytech/polkadot-sdk/blob/master/substrate/primitives/runtime/src/generic/checked_extrinsic.rs#L37
126pub const DEFAULT_EXTENSION_VERSION: ExtensionVersion = 0;
127
128/// Computed as ED = Account data size * Price per byte, where
129/// Price per byte = Min Number of validators * Storage duration (years) * Storage cost per year
130/// Account data size (80 bytes)
131/// Min Number of redundant validators (10) - For a stable and redundant blockchain we need at least a certain number of full nodes/collators.
132/// Storage duration (1 year) - It is theoretically unlimited, accounts will stay around while the chain is alive.
133/// Storage cost per year of (12 * 1e-9 * 0.1 ) - SSD storage on cloud hosting costs about 0.1 USD per Gb per month
134pub const EXISTENTIAL_DEPOSIT: Balance = 1_000_000_000_000 * SHANNON;
135
136parameter_types! {
137 /// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less
138 /// than this will decrease the weight and more will increase.
139 pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25);
140}
141
142/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is
143/// used to limit the maximal weight of a single extrinsic.
144const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5);
145
146pub fn block_weights() -> BlockWeights {
147 // NOTE: this value is used in many places as the `max_block` weight (e.g. `used_weight / max_block`
148 // is used in tx fee adjustment), but it does not serve as a limit of the total weight of a domain block,
149 // which is `max_bundle_weight * number_of_bundle` and bundle production is probabilistic.
150 // See [`domain-check-weight::CheckWeight`] for more detail about the domain block weight check.
151 //
152 // TODO: instead of using a constant weight value for all the domain runtimes, perhaps derive and use the
153 // target domain block weight bases on `max_bundle_weight` and `bundle_slot_probability`.
154 let maximum_block_weight = maximum_domain_block_weight();
155
156 // If the bundle slot probability is the same as the consensus slot probability then
157 // there is one bundle per block, such a bundle is allowed to consume the whole domain
158 // block weight
159 let max_extrinsic_weight = maximum_block_weight
160 .checked_sub(&ExtrinsicBaseWeight::get())
161 .expect("Unable to produce blocks unless maximum block weight is greater than base extrinsic weight");
162
163 BlockWeights::builder()
164 .base_block(BlockExecutionWeight::get())
165 .for_class(DispatchClass::all(), |weights| {
166 weights.base_extrinsic = ExtrinsicBaseWeight::get();
167 // maximum weight of each transaction would be the maximum weight of
168 // single bundle
169 weights.max_extrinsic = Some(max_extrinsic_weight);
170 // explicitly set max_total weight for normal dispatches to maximum
171 weights.max_total = Some(maximum_block_weight);
172 })
173 .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)
174 .build_or_panic()
175}
176
177/// Extracts the signer from an unchecked extrinsic.
178///
179/// Used by executor to extract the optional signer when shuffling the extrinsics.
180pub trait Signer<AccountId, Lookup> {
181 /// Returns the AccountId of signer.
182 fn signer(&self, lookup: &Lookup) -> Option<AccountId>;
183}
184
185impl<Address, AccountId, Call, Signature, Extra, Lookup> Signer<AccountId, Lookup>
186 for UncheckedExtrinsic<Address, Call, Signature, Extra>
187where
188 Address: Clone,
189 Call: Dispatchable,
190 Extra: TransactionExtension<Call>,
191 Lookup: sp_runtime::traits::Lookup<Source = Address, Target = AccountId>,
192{
193 fn signer(&self, lookup: &Lookup) -> Option<AccountId> {
194 match &self.preamble {
195 Preamble::Bare(_) => None,
196 Preamble::Signed(address, _, _) => lookup.lookup(address.clone()).ok(),
197 Preamble::General(_, _) => None,
198 }
199 }
200}
201
202/// MultiAccountId used by all the domains to describe their account type.
203#[derive(
204 Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Serialize, Deserialize, Ord, PartialOrd,
205)]
206pub enum MultiAccountId {
207 /// 32 byte account Id.
208 AccountId32([u8; 32]),
209 /// 20 byte account Id. Ex: Ethereum
210 AccountId20([u8; 20]),
211 /// Some raw bytes
212 Raw(Vec<u8>),
213}
214
215/// Extensible conversion trait. Generic over both source and destination types.
216pub trait TryConvertBack<A, B>: Convert<A, B> {
217 /// Make conversion back.
218 fn try_convert_back(b: B) -> Option<A>;
219}
220
221/// An AccountId32 to MultiAccount converter.
222pub struct AccountIdConverter;
223
224impl Convert<AccountId, MultiAccountId> for AccountIdConverter {
225 fn convert(account_id: AccountId) -> MultiAccountId {
226 MultiAccountId::AccountId32(account_id.into())
227 }
228}
229
230impl TryConvertBack<AccountId, MultiAccountId> for AccountIdConverter {
231 fn try_convert_back(multi_account_id: MultiAccountId) -> Option<AccountId> {
232 match multi_account_id {
233 MultiAccountId::AccountId32(acc) => Some(AccountId::new(acc)),
234 _ => None,
235 }
236 }
237}
238
239/// An AccountId20 to MultiAccount converter.
240pub struct AccountId20Converter;
241
242impl Convert<AccountId20, MultiAccountId> for AccountId20Converter {
243 fn convert(account_id: AccountId20) -> MultiAccountId {
244 MultiAccountId::AccountId20(account_id.into())
245 }
246}
247
248impl TryConvertBack<AccountId20, MultiAccountId> for AccountId20Converter {
249 fn try_convert_back(multi_account_id: MultiAccountId) -> Option<AccountId20> {
250 match multi_account_id {
251 MultiAccountId::AccountId20(acc) => Some(AccountId20::from(acc)),
252 _ => None,
253 }
254 }
255}
256
257#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
258pub struct CheckExtrinsicsValidityError {
259 pub extrinsic_index: u32,
260 pub transaction_validity_error: TransactionValidityError,
261}
262
263#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
264pub struct DecodeExtrinsicError(pub String);
265
266/// Fully qualified method name of check_extrinsics_and_do_pre_dispatch runtime api.
267/// Used to call state machine.
268/// Change it when the runtime api's name is changed in the interface.
269pub const CHECK_EXTRINSICS_AND_DO_PRE_DISPATCH_METHOD_NAME: &str =
270 "DomainCoreApi_check_extrinsics_and_do_pre_dispatch";
271
272/// Opaque types.
273///
274/// These are used by the CLI to instantiate machinery that don't need to know the specifics of the
275/// runtime. They can then be made to be agnostic over specific formats of data like extrinsics,
276/// allowing for them to continue syncing the network through upgrades to even the core data
277/// structures.
278pub mod opaque {
279 use crate::BlockNumber;
280 #[cfg(not(feature = "std"))]
281 use alloc::vec::Vec;
282 pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
283 use sp_runtime::generic;
284 use sp_runtime::traits::BlakeTwo256;
285
286 /// Opaque block header type.
287 pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
288 /// Opaque block type.
289 pub type Block = generic::Block<Header, UncheckedExtrinsic>;
290 /// Opaque block identifier type.
291 pub type BlockId = generic::BlockId<Block>;
292 /// Opaque account identifier type.
293 pub type AccountId = Vec<u8>;
294}
295
296#[cfg(test)]
297mod test {
298 use super::block_weights;
299
300 #[test]
301 fn test_block_weights() {
302 // validate and build block weights
303 let _block_weights = block_weights();
304 }
305}