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