subspace_test_runtime/
lib.rs

1// Copyright (C) 2021 Subspace Labs, Inc.
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17#![cfg_attr(not(feature = "std"), no_std)]
18#![feature(variant_count)]
19// `generic_const_exprs` is an incomplete feature
20#![allow(incomplete_features)]
21// TODO: This feature is not actually used in this crate, but is added as a workaround for
22//  https://github.com/rust-lang/rust/issues/133199
23#![feature(generic_const_exprs)]
24// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
25#![recursion_limit = "256"]
26// TODO: remove when upstream issue is fixed
27#![allow(
28    non_camel_case_types,
29    reason = "https://github.com/rust-lang/rust-analyzer/issues/16514"
30)]
31
32extern crate alloc;
33
34// Make the WASM binary available.
35#[cfg(feature = "std")]
36include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
37
38use alloc::borrow::Cow;
39use core::mem;
40use core::num::NonZeroU64;
41use domain_runtime_primitives::opaque::Header as DomainHeader;
42use domain_runtime_primitives::{
43    AccountIdConverter, BlockNumber as DomainNumber, EthereumAccountId, Hash as DomainHash,
44    MAX_OUTGOING_MESSAGES,
45};
46use frame_support::genesis_builder_helper::{build_state, get_preset};
47use frame_support::inherent::ProvideInherent;
48use frame_support::traits::fungible::Inspect;
49use frame_support::traits::tokens::WithdrawConsequence;
50use frame_support::traits::{
51    ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, Currency, Everything, ExistenceRequirement,
52    Get, Imbalance, Time, VariantCount, WithdrawReasons,
53};
54use frame_support::weights::constants::{ParityDbWeight, WEIGHT_REF_TIME_PER_SECOND};
55use frame_support::weights::{ConstantMultiplier, Weight};
56use frame_support::{construct_runtime, parameter_types, PalletId};
57use frame_system::limits::{BlockLength, BlockWeights};
58use frame_system::pallet_prelude::{OriginFor, RuntimeCallFor};
59use pallet_balances::NegativeImbalance;
60pub use pallet_rewards::RewardPoint;
61pub use pallet_subspace::{AllowAuthoringBy, EnableRewardsAt};
62use pallet_transporter::EndpointHandler;
63use parity_scale_codec::{Compact, CompactLen, Decode, Encode, MaxEncodedLen};
64use scale_info::TypeInfo;
65use sp_api::impl_runtime_apis;
66use sp_consensus_slots::{Slot, SlotDuration};
67use sp_consensus_subspace::{ChainConstants, PotParameters, SignedVote, SolutionRanges, Vote};
68use sp_core::crypto::KeyTypeId;
69use sp_core::{OpaqueMetadata, H256};
70use sp_domains::bundle_producer_election::BundleProducerElectionParams;
71use sp_domains::{
72    DomainAllowlistUpdates, DomainId, DomainInstanceData, ExecutionReceiptFor, OpaqueBundle,
73    OpaqueBundles, OperatorId, OperatorPublicKey, OperatorRewardSource,
74    PermissionedActionAllowedBy, DOMAIN_STORAGE_FEE_MULTIPLIER, INITIAL_DOMAIN_TX_RANGE,
75};
76use sp_domains_fraud_proof::fraud_proof::FraudProof;
77use sp_domains_fraud_proof::storage_proof::{
78    FraudProofStorageKeyProvider, FraudProofStorageKeyRequest,
79};
80use sp_messenger::endpoint::{Endpoint, EndpointHandler as EndpointHandlerT, EndpointId};
81use sp_messenger::messages::{
82    BlockMessagesQuery, BlockMessagesWithStorageKey, ChainId, ChannelId, ChannelStateWithNonce,
83    CrossDomainMessage, MessageId, MessageKey, MessagesWithStorageKey, Nonce as XdmNonce,
84};
85use sp_messenger::{ChannelNonce, XdmId};
86use sp_messenger_host_functions::{get_storage_key, StorageKeyRequest};
87use sp_mmr_primitives::EncodableOpaqueLeaf;
88use sp_runtime::traits::{
89    AccountIdConversion, AccountIdLookup, AsSystemOriginSigner, BlakeTwo256, ConstBool,
90    DispatchInfoOf, Keccak256, NumberFor, PostDispatchInfoOf, TransactionExtension, ValidateResult,
91    Zero,
92};
93use sp_runtime::transaction_validity::{
94    InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
95    ValidTransaction,
96};
97use sp_runtime::type_with_default::TypeWithDefault;
98use sp_runtime::{
99    generic, impl_tx_ext_default, AccountId32, ApplyExtrinsicResult, ExtrinsicInclusionMode,
100    Perbill,
101};
102use sp_std::collections::btree_map::BTreeMap;
103use sp_std::collections::btree_set::BTreeSet;
104use sp_std::marker::PhantomData;
105use sp_std::prelude::*;
106use sp_subspace_mmr::ConsensusChainMmrLeafProof;
107use sp_version::RuntimeVersion;
108use static_assertions::const_assert;
109use subspace_core_primitives::objects::{BlockObject, BlockObjectMapping};
110use subspace_core_primitives::pieces::Piece;
111use subspace_core_primitives::segments::{
112    HistorySize, SegmentCommitment, SegmentHeader, SegmentIndex,
113};
114use subspace_core_primitives::solutions::SolutionRange;
115use subspace_core_primitives::{hashes, PublicKey, Randomness, SlotNumber, U256};
116use subspace_runtime_primitives::utility::{
117    nested_call_iter, DefaultNonceProvider, MaybeMultisigCall, MaybeNestedCall, MaybeUtilityCall,
118};
119use subspace_runtime_primitives::{
120    AccountId, Balance, BlockHashFor, BlockNumber, ConsensusEventSegmentSize, ExtrinsicFor,
121    FindBlockRewardAddress, Hash, HeaderFor, HoldIdentifier, Moment, Nonce, Signature,
122    SlowAdjustingFeeUpdate, TargetBlockFullness, XdmAdjustedWeightToFee, XdmFeeMultipler,
123    MAX_BLOCK_LENGTH, MAX_CALL_RECURSION_DEPTH, MIN_REPLICATION_FACTOR, SHANNON, SSC,
124};
125use subspace_test_primitives::DOMAINS_BLOCK_PRUNING_DEPTH;
126
127sp_runtime::impl_opaque_keys! {
128    pub struct SessionKeys {
129    }
130}
131
132// Smaller value for testing purposes
133const MAX_PIECES_IN_SECTOR: u16 = 32;
134
135// To learn more about runtime versioning and what each of the following value means:
136//   https://substrate.dev/docs/en/knowledgebase/runtime/upgrades#runtime-versioning
137#[sp_version::runtime_version]
138pub const VERSION: RuntimeVersion = RuntimeVersion {
139    spec_name: Cow::Borrowed("subspace"),
140    impl_name: Cow::Borrowed("subspace"),
141    authoring_version: 1,
142    // The version of the runtime specification. A full node will not attempt to use its native
143    //   runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`,
144    //   `spec_version`, and `authoring_version` are the same between Wasm and native.
145    // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
146    //   the compatible custom types.
147    spec_version: 100,
148    impl_version: 1,
149    apis: RUNTIME_API_VERSIONS,
150    transaction_version: 1,
151    system_version: 2,
152};
153
154// TODO: Many of below constants should probably be updatable but currently they are not
155
156/// Expected block time in milliseconds.
157///
158/// Since Subspace is probabilistic this is the average expected block time that
159/// we are targeting. Blocks will be produced at a minimum duration defined
160/// by `SLOT_DURATION`, but some slots will not be allocated to any
161/// farmer and hence no block will be produced. We expect to have this
162/// block time on average following the defined slot duration and the value
163/// of `c` configured for Subspace (where `1 - c` represents the probability of
164/// a slot being empty).
165/// This value is only used indirectly to define the unit constants below
166/// that are expressed in blocks. The rest of the code should use
167/// `SLOT_DURATION` instead (like the Timestamp pallet for calculating the
168/// minimum period).
169///
170/// Based on:
171/// <https://research.web3.foundation/en/latest/polkadot/block-production/Babe.html#-6.-practical-results>
172pub const MILLISECS_PER_BLOCK: u64 = 2000;
173
174// NOTE: Currently it is not possible to change the slot duration after the chain has started.
175//       Attempting to do so will brick block production.
176pub const SLOT_DURATION: u64 = 2000;
177
178/// 1 in 6 slots (on average, not counting collisions) will have a block.
179/// Must match ratio between block and slot duration in constants above.
180const SLOT_PROBABILITY: (u64, u64) = (1, 1);
181/// Number of slots between slot arrival and when corresponding block can be produced.
182const BLOCK_AUTHORING_DELAY: SlotNumber = 2;
183
184/// Interval, in blocks, between blockchain entropy injection into proof of time chain.
185const POT_ENTROPY_INJECTION_INTERVAL: BlockNumber = 5;
186
187/// Interval, in entropy injection intervals, where to take entropy for injection from.
188const POT_ENTROPY_INJECTION_LOOKBACK_DEPTH: u8 = 2;
189
190/// Delay after block, in slots, when entropy injection takes effect.
191const POT_ENTROPY_INJECTION_DELAY: SlotNumber = 4;
192
193// Entropy injection interval must be bigger than injection delay or else we may end up in a
194// situation where we'll need to do more than one injection at the same slot
195const_assert!(POT_ENTROPY_INJECTION_INTERVAL as u64 > POT_ENTROPY_INJECTION_DELAY);
196// Entropy injection delay must be bigger than block authoring delay or else we may include
197// invalid future proofs in parent block, +1 ensures we do not have unnecessary reorgs that will
198// inevitably happen otherwise
199const_assert!(POT_ENTROPY_INJECTION_DELAY > BLOCK_AUTHORING_DELAY + 1);
200
201/// Era duration in blocks.
202const ERA_DURATION_IN_BLOCKS: BlockNumber = 2016;
203
204/// Any solution range is valid in the test environment.
205const INITIAL_SOLUTION_RANGE: SolutionRange = SolutionRange::MAX;
206
207/// A ratio of `Normal` dispatch class within block, for `BlockWeight` and `BlockLength`.
208const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
209
210/// The block weight for 2 seconds of compute
211const BLOCK_WEIGHT_FOR_2_SEC: Weight =
212    Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX);
213
214parameter_types! {
215    pub const Version: RuntimeVersion = VERSION;
216    pub const BlockHashCount: BlockNumber = 250;
217    /// We allow for 2 seconds of compute with a 6 second average block time.
218    pub SubspaceBlockWeights: BlockWeights = BlockWeights::with_sensible_defaults(BLOCK_WEIGHT_FOR_2_SEC, NORMAL_DISPATCH_RATIO);
219    /// We allow for 3.75 MiB for `Normal` extrinsic with 5 MiB maximum block length.
220    pub SubspaceBlockLength: BlockLength = BlockLength::max_with_normal_ratio(MAX_BLOCK_LENGTH, NORMAL_DISPATCH_RATIO);
221}
222
223pub type SS58Prefix = ConstU16<6094>;
224
225// Configure FRAME pallets to include in runtime.
226
227impl frame_system::Config for Runtime {
228    /// The basic call filter to use in dispatchable.
229    ///
230    /// `Everything` is used here as we use the signed extension
231    /// `DisablePallets` as the actual call filter.
232    type BaseCallFilter = Everything;
233    /// Block & extrinsics weights: base values and limits.
234    type BlockWeights = SubspaceBlockWeights;
235    /// The maximum length of a block (in bytes).
236    type BlockLength = SubspaceBlockLength;
237    /// The identifier used to distinguish between accounts.
238    type AccountId = AccountId;
239    /// The aggregated dispatch type that is available for extrinsics.
240    type RuntimeCall = RuntimeCall;
241    /// The aggregated `RuntimeTask` type.
242    type RuntimeTask = RuntimeTask;
243    /// The lookup mechanism to get account ID from whatever is passed in dispatchers.
244    type Lookup = AccountIdLookup<AccountId, ()>;
245    /// The type for storing how many extrinsics an account has signed.
246    type Nonce = TypeWithDefault<Nonce, DefaultNonceProvider<System, Nonce>>;
247    /// The type for hashing blocks and tries.
248    type Hash = Hash;
249    /// The hashing algorithm used.
250    type Hashing = BlakeTwo256;
251    /// The block type.
252    type Block = Block;
253    /// The ubiquitous event type.
254    type RuntimeEvent = RuntimeEvent;
255    /// The ubiquitous origin type.
256    type RuntimeOrigin = RuntimeOrigin;
257    /// Maximum number of block number to block hash mappings to keep (oldest pruned first).
258    type BlockHashCount = BlockHashCount;
259    /// The weight of database operations that the runtime can invoke.
260    type DbWeight = ParityDbWeight;
261    /// Version of the runtime.
262    type Version = Version;
263    /// Converts a module to the index of the module in `construct_runtime!`.
264    ///
265    /// This type is being generated by `construct_runtime!`.
266    type PalletInfo = PalletInfo;
267    /// What to do if a new account is created.
268    type OnNewAccount = ();
269    /// What to do if an account is fully reaped from the system.
270    type OnKilledAccount = ();
271    /// The data to be stored in an account.
272    type AccountData = pallet_balances::AccountData<Balance>;
273    /// Weight information for the extrinsics of this pallet.
274    type SystemWeightInfo = frame_system::weights::SubstrateWeight<Runtime>;
275    /// This is used as an identifier of the chain.
276    type SS58Prefix = SS58Prefix;
277    /// The set code logic, just the default since we're not a parachain.
278    type OnSetCode = ();
279    type SingleBlockMigrations = ();
280    type MultiBlockMigrator = ();
281    type PreInherents = ();
282    type PostInherents = ();
283    type PostTransactions = ();
284    type MaxConsumers = ConstU32<16>;
285    type ExtensionsWeightInfo = frame_system::ExtensionsWeight<Runtime>;
286    type EventSegmentSize = ConsensusEventSegmentSize;
287}
288
289parameter_types! {
290    pub const BlockAuthoringDelay: SlotNumber = BLOCK_AUTHORING_DELAY;
291    pub const PotEntropyInjectionInterval: BlockNumber = POT_ENTROPY_INJECTION_INTERVAL;
292    pub const PotEntropyInjectionLookbackDepth: u8 = POT_ENTROPY_INJECTION_LOOKBACK_DEPTH;
293    pub const PotEntropyInjectionDelay: SlotNumber = POT_ENTROPY_INJECTION_DELAY;
294    pub const EraDuration: BlockNumber = ERA_DURATION_IN_BLOCKS;
295    pub const SlotProbability: (u64, u64) = SLOT_PROBABILITY;
296    pub const ShouldAdjustSolutionRange: bool = false;
297    pub const ExpectedVotesPerBlock: u32 = 9;
298    pub const ConfirmationDepthK: u32 = 5;
299    pub const RecentSegments: HistorySize = HistorySize::new(NonZeroU64::new(5).unwrap());
300    pub const RecentHistoryFraction: (HistorySize, HistorySize) = (
301        HistorySize::new(NonZeroU64::new(1).unwrap()),
302        HistorySize::new(NonZeroU64::new(10).unwrap()),
303    );
304    pub const MinSectorLifetime: HistorySize = HistorySize::new(NonZeroU64::new(4).unwrap());
305    pub const BlockSlotCount: u32 = 6;
306    pub TransactionWeightFee: Balance = 100_000 * SHANNON;
307}
308
309impl pallet_subspace::Config for Runtime {
310    type RuntimeEvent = RuntimeEvent;
311    type SubspaceOrigin = pallet_subspace::EnsureSubspaceOrigin;
312    type BlockAuthoringDelay = BlockAuthoringDelay;
313    type PotEntropyInjectionInterval = PotEntropyInjectionInterval;
314    type PotEntropyInjectionLookbackDepth = PotEntropyInjectionLookbackDepth;
315    type PotEntropyInjectionDelay = PotEntropyInjectionDelay;
316    type EraDuration = EraDuration;
317    type InitialSolutionRange = ConstU64<INITIAL_SOLUTION_RANGE>;
318    type SlotProbability = SlotProbability;
319    type ConfirmationDepthK = ConfirmationDepthK;
320    type RecentSegments = RecentSegments;
321    type RecentHistoryFraction = RecentHistoryFraction;
322    type MinSectorLifetime = MinSectorLifetime;
323    type ExpectedVotesPerBlock = ExpectedVotesPerBlock;
324    type MaxPiecesInSector = ConstU16<{ MAX_PIECES_IN_SECTOR }>;
325    type ShouldAdjustSolutionRange = ShouldAdjustSolutionRange;
326    type EraChangeTrigger = pallet_subspace::NormalEraChange;
327    type WeightInfo = pallet_subspace::weights::SubstrateWeight<Runtime>;
328    type BlockSlotCount = BlockSlotCount;
329    type ExtensionWeightInfo = pallet_subspace::extensions::weights::SubstrateWeight<Runtime>;
330}
331
332impl pallet_timestamp::Config for Runtime {
333    /// A timestamp: milliseconds since the unix epoch.
334    type Moment = Moment;
335    type OnTimestampSet = ();
336    type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>;
337    type WeightInfo = pallet_timestamp::weights::SubstrateWeight<Runtime>;
338}
339
340#[derive(
341    PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug,
342)]
343pub struct HoldIdentifierWrapper(HoldIdentifier);
344
345impl pallet_domains::HoldIdentifier<Runtime> for HoldIdentifierWrapper {
346    fn staking_staked() -> Self {
347        Self(HoldIdentifier::DomainStaking)
348    }
349
350    fn domain_instantiation_id() -> Self {
351        Self(HoldIdentifier::DomainInstantiation)
352    }
353
354    fn storage_fund_withdrawal() -> Self {
355        Self(HoldIdentifier::DomainStorageFund)
356    }
357}
358
359impl pallet_messenger::HoldIdentifier<Runtime> for HoldIdentifierWrapper {
360    fn messenger_channel() -> Self {
361        Self(HoldIdentifier::MessengerChannel)
362    }
363}
364
365impl VariantCount for HoldIdentifierWrapper {
366    const VARIANT_COUNT: u32 = mem::variant_count::<HoldIdentifier>() as u32;
367}
368
369impl pallet_balances::Config for Runtime {
370    type RuntimeFreezeReason = RuntimeFreezeReason;
371    type MaxLocks = ConstU32<50>;
372    type MaxReserves = ();
373    type ReserveIdentifier = [u8; 8];
374    /// The type for recording an account's balance.
375    type Balance = Balance;
376    /// The ubiquitous event type.
377    type RuntimeEvent = RuntimeEvent;
378    type DustRemoval = ();
379    type ExistentialDeposit = ConstU128<{ 10_000_000_000_000 * SHANNON }>;
380    type AccountStore = System;
381    type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
382    type FreezeIdentifier = ();
383    type MaxFreezes = ();
384    type RuntimeHoldReason = HoldIdentifierWrapper;
385    type DoneSlashHandler = ();
386}
387
388pub struct CreditSupply;
389
390impl Get<Balance> for CreditSupply {
391    fn get() -> Balance {
392        Balances::total_issuance()
393    }
394}
395
396pub struct TotalSpacePledged;
397
398impl Get<u128> for TotalSpacePledged {
399    fn get() -> u128 {
400        // Operations reordered to avoid data loss, but essentially are:
401        // u64::MAX * SlotProbability / (solution_range / PIECE_SIZE)
402        u128::from(u64::MAX)
403            .saturating_mul(Piece::SIZE as u128)
404            .saturating_mul(u128::from(SlotProbability::get().0))
405            / u128::from(Subspace::solution_ranges().current)
406            / u128::from(SlotProbability::get().1)
407    }
408}
409
410pub struct BlockchainHistorySize;
411
412impl Get<u128> for BlockchainHistorySize {
413    fn get() -> u128 {
414        u128::from(Subspace::archived_history_size())
415    }
416}
417
418impl pallet_transaction_fees::Config for Runtime {
419    type RuntimeEvent = RuntimeEvent;
420    type MinReplicationFactor = ConstU16<MIN_REPLICATION_FACTOR>;
421    type CreditSupply = CreditSupply;
422    type TotalSpacePledged = TotalSpacePledged;
423    type BlockchainHistorySize = BlockchainHistorySize;
424    type Currency = Balances;
425    type FindBlockRewardAddress = Subspace;
426    type DynamicCostOfStorage = ConstBool<false>;
427    type WeightInfo = pallet_transaction_fees::weights::SubstrateWeight<Runtime>;
428}
429
430pub struct TransactionByteFee;
431
432impl Get<Balance> for TransactionByteFee {
433    fn get() -> Balance {
434        TransactionFees::transaction_byte_fee()
435    }
436}
437
438pub struct LiquidityInfo {
439    storage_fee: Balance,
440    imbalance: NegativeImbalance<Runtime>,
441}
442
443/// Implementation of [`pallet_transaction_payment::OnChargeTransaction`] that charges transaction
444/// fees and distributes storage/compute fees and tip separately.
445pub struct OnChargeTransaction;
446
447impl pallet_transaction_payment::OnChargeTransaction<Runtime> for OnChargeTransaction {
448    type LiquidityInfo = Option<LiquidityInfo>;
449    type Balance = Balance;
450
451    fn withdraw_fee(
452        who: &AccountId,
453        call: &RuntimeCall,
454        _info: &DispatchInfoOf<RuntimeCall>,
455        fee: Self::Balance,
456        tip: Self::Balance,
457    ) -> Result<Self::LiquidityInfo, TransactionValidityError> {
458        if fee.is_zero() {
459            return Ok(None);
460        }
461
462        let withdraw_reason = if tip.is_zero() {
463            WithdrawReasons::TRANSACTION_PAYMENT
464        } else {
465            WithdrawReasons::TRANSACTION_PAYMENT | WithdrawReasons::TIP
466        };
467
468        let withdraw_result =
469            Balances::withdraw(who, fee, withdraw_reason, ExistenceRequirement::KeepAlive);
470        let imbalance = withdraw_result.map_err(|_error| InvalidTransaction::Payment)?;
471
472        // Separate storage fee while we have access to the call data structure to calculate it.
473        let storage_fee = TransactionByteFee::get()
474            * Balance::try_from(call.encoded_size())
475                .expect("Size of the call never exceeds balance units; qed");
476
477        Ok(Some(LiquidityInfo {
478            storage_fee,
479            imbalance,
480        }))
481    }
482
483    fn correct_and_deposit_fee(
484        who: &AccountId,
485        _dispatch_info: &DispatchInfoOf<RuntimeCall>,
486        _post_info: &PostDispatchInfoOf<RuntimeCall>,
487        corrected_fee: Self::Balance,
488        tip: Self::Balance,
489        liquidity_info: Self::LiquidityInfo,
490    ) -> Result<(), TransactionValidityError> {
491        if let Some(LiquidityInfo {
492            storage_fee,
493            imbalance,
494        }) = liquidity_info
495        {
496            // Calculate how much refund we should return
497            let refund_amount = imbalance.peek().saturating_sub(corrected_fee);
498            // Refund to the account that paid the fees. If this fails, the account might have
499            // dropped below the existential balance. In that case we don't refund anything.
500            let refund_imbalance = Balances::deposit_into_existing(who, refund_amount)
501                .unwrap_or_else(|_| <Balances as Currency<AccountId>>::PositiveImbalance::zero());
502            // Merge the imbalance caused by paying the fees and refunding parts of it again.
503            let adjusted_paid = imbalance
504                .offset(refund_imbalance)
505                .same()
506                .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?;
507
508            // Split the tip from the total fee that ended up being paid.
509            let (tip, fee) = adjusted_paid.split(tip);
510            // Split paid storage and compute fees so that they can be distributed separately.
511            let (paid_storage_fee, paid_compute_fee) = fee.split(storage_fee);
512
513            TransactionFees::note_transaction_fees(
514                paid_storage_fee.peek(),
515                paid_compute_fee.peek(),
516                tip.peek(),
517            );
518        }
519        Ok(())
520    }
521
522    fn can_withdraw_fee(
523        who: &AccountId,
524        _call: &RuntimeCall,
525        _dispatch_info: &DispatchInfoOf<RuntimeCall>,
526        fee: Self::Balance,
527        _tip: Self::Balance,
528    ) -> Result<(), TransactionValidityError> {
529        if fee.is_zero() {
530            return Ok(());
531        }
532
533        match Balances::can_withdraw(who, fee) {
534            WithdrawConsequence::Success => Ok(()),
535            _ => Err(InvalidTransaction::Payment.into()),
536        }
537    }
538}
539
540impl pallet_transaction_payment::Config for Runtime {
541    type RuntimeEvent = RuntimeEvent;
542    type OnChargeTransaction = OnChargeTransaction;
543    type OperationalFeeMultiplier = ConstU8<5>;
544    type WeightToFee = ConstantMultiplier<Balance, TransactionWeightFee>;
545    type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
546    type FeeMultiplierUpdate = SlowAdjustingFeeUpdate<Runtime, TargetBlockFullness>;
547    type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight<Runtime>;
548}
549
550impl pallet_utility::Config for Runtime {
551    type RuntimeEvent = RuntimeEvent;
552    type RuntimeCall = RuntimeCall;
553    type PalletsOrigin = OriginCaller;
554    type WeightInfo = pallet_utility::weights::SubstrateWeight<Runtime>;
555}
556
557impl MaybeMultisigCall<Runtime> for RuntimeCall {
558    /// If this call is a `pallet_multisig::Call<Runtime>` call, returns the inner call.
559    fn maybe_multisig_call(&self) -> Option<&pallet_multisig::Call<Runtime>> {
560        match self {
561            RuntimeCall::Multisig(call) => Some(call),
562            _ => None,
563        }
564    }
565}
566
567impl MaybeUtilityCall<Runtime> for RuntimeCall {
568    /// If this call is a `pallet_utility::Call<Runtime>` call, returns the inner call.
569    fn maybe_utility_call(&self) -> Option<&pallet_utility::Call<Runtime>> {
570        match self {
571            RuntimeCall::Utility(call) => Some(call),
572            _ => None,
573        }
574    }
575}
576
577impl MaybeNestedCall<Runtime> for RuntimeCall {
578    /// If this call is a nested runtime call, returns the inner call(s).
579    ///
580    /// Ignored calls (such as `pallet_utility::Call::__Ignore`) should be yielded themsevles, but
581    /// their contents should not be yielded.
582    fn maybe_nested_call(&self) -> Option<Vec<&RuntimeCallFor<Runtime>>> {
583        // We currently ignore privileged calls, because privileged users can already change
584        // runtime code. This includes sudo, collective, and scheduler nested `RuntimeCall`s,
585        // and democracy nested `BoundedCall`s.
586
587        // It is ok to return early, because each call can only belong to one pallet.
588        let calls = self.maybe_nested_utility_calls();
589        if calls.is_some() {
590            return calls;
591        }
592
593        let calls = self.maybe_nested_multisig_calls();
594        if calls.is_some() {
595            return calls;
596        }
597
598        None
599    }
600}
601
602impl pallet_sudo::Config for Runtime {
603    type RuntimeEvent = RuntimeEvent;
604    type RuntimeCall = RuntimeCall;
605    type WeightInfo = pallet_sudo::weights::SubstrateWeight<Runtime>;
606}
607
608parameter_types! {
609    pub SelfChainId: ChainId = ChainId::Consensus;
610}
611
612pub struct MmrProofVerifier;
613
614impl sp_subspace_mmr::MmrProofVerifier<mmr::Hash, NumberFor<Block>, Hash> for MmrProofVerifier {
615    fn verify_proof_and_extract_leaf(
616        mmr_leaf_proof: ConsensusChainMmrLeafProof<NumberFor<Block>, Hash, mmr::Hash>,
617    ) -> Option<mmr::Leaf> {
618        let mmr_root = SubspaceMmr::mmr_root_hash(mmr_leaf_proof.consensus_block_number)?;
619        Self::verify_proof_stateless(mmr_root, mmr_leaf_proof)
620    }
621
622    fn verify_proof_stateless(
623        mmr_root: mmr::Hash,
624        mmr_leaf_proof: ConsensusChainMmrLeafProof<NumberFor<Block>, Hash, mmr::Hash>,
625    ) -> Option<mmr::Leaf> {
626        let ConsensusChainMmrLeafProof {
627            opaque_mmr_leaf,
628            proof,
629            ..
630        } = mmr_leaf_proof;
631
632        pallet_mmr::verify_leaves_proof::<mmr::Hashing, _>(
633            mmr_root,
634            vec![mmr::DataOrHash::Data(
635                EncodableOpaqueLeaf(opaque_mmr_leaf.0.clone()).into_opaque_leaf(),
636            )],
637            proof,
638        )
639        .ok()?;
640
641        let leaf: mmr::Leaf = opaque_mmr_leaf.into_opaque_leaf().try_decode()?;
642
643        Some(leaf)
644    }
645}
646
647pub struct StorageKeys;
648
649impl sp_messenger::StorageKeys for StorageKeys {
650    fn confirmed_domain_block_storage_key(domain_id: DomainId) -> Option<Vec<u8>> {
651        Some(Domains::confirmed_domain_block_storage_key(domain_id))
652    }
653
654    fn outbox_storage_key(chain_id: ChainId, message_key: MessageKey) -> Option<Vec<u8>> {
655        get_storage_key(StorageKeyRequest::OutboxStorageKey {
656            chain_id,
657            message_key,
658        })
659    }
660
661    fn inbox_responses_storage_key(chain_id: ChainId, message_key: MessageKey) -> Option<Vec<u8>> {
662        get_storage_key(StorageKeyRequest::InboxResponseStorageKey {
663            chain_id,
664            message_key,
665        })
666    }
667}
668
669pub struct DomainRegistration;
670impl sp_messenger::DomainRegistration for DomainRegistration {
671    fn is_domain_registered(domain_id: DomainId) -> bool {
672        Domains::is_domain_registered(domain_id)
673    }
674}
675
676parameter_types! {
677    pub const ChannelReserveFee: Balance = SSC;
678    pub const ChannelInitReservePortion: Perbill = Perbill::from_percent(20);
679    pub const MaxOutgoingMessages: u32 = MAX_OUTGOING_MESSAGES;
680}
681
682// ensure the max outgoing messages is not 0.
683const_assert!(MaxOutgoingMessages::get() >= 1);
684
685pub struct OnXDMRewards;
686
687impl sp_messenger::OnXDMRewards<Balance> for OnXDMRewards {
688    fn on_xdm_rewards(reward: Balance) {
689        if let Some(block_author) = Subspace::find_block_reward_address() {
690            let _ = Balances::deposit_creating(&block_author, reward);
691        }
692    }
693
694    fn on_chain_protocol_fees(chain_id: ChainId, fees: Balance) {
695        // on consensus chain, reward the domain operators
696        // balance is already on this consensus runtime
697        if let ChainId::Domain(domain_id) = chain_id {
698            Domains::reward_domain_operators(domain_id, OperatorRewardSource::XDMProtocolFees, fees)
699        }
700    }
701}
702
703impl pallet_messenger::Config for Runtime {
704    type RuntimeEvent = RuntimeEvent;
705    type SelfChainId = SelfChainId;
706
707    fn get_endpoint_handler(endpoint: &Endpoint) -> Option<Box<dyn EndpointHandlerT<MessageId>>> {
708        if endpoint == &Endpoint::Id(TransporterEndpointId::get()) {
709            Some(Box::new(EndpointHandler(PhantomData::<Runtime>)))
710        } else {
711            None
712        }
713    }
714
715    type Currency = Balances;
716    type WeightInfo = pallet_messenger::weights::SubstrateWeight<Runtime>;
717    type WeightToFee = ConstantMultiplier<Balance, TransactionWeightFee>;
718    type AdjustedWeightToFee = XdmAdjustedWeightToFee<Runtime>;
719    type FeeMultiplier = XdmFeeMultipler;
720    type OnXDMRewards = OnXDMRewards;
721    type MmrHash = mmr::Hash;
722    type MmrProofVerifier = MmrProofVerifier;
723    type StorageKeys = StorageKeys;
724    type DomainOwner = Domains;
725    type HoldIdentifier = HoldIdentifierWrapper;
726    type ChannelReserveFee = ChannelReserveFee;
727    type ChannelInitReservePortion = ChannelInitReservePortion;
728    type DomainRegistration = DomainRegistration;
729    type MaxOutgoingMessages = MaxOutgoingMessages;
730    type MessengerOrigin = pallet_messenger::EnsureMessengerOrigin;
731    type NoteChainTransfer = Transporter;
732    type ExtensionWeightInfo = pallet_messenger::extensions::weights::SubstrateWeight<Runtime>;
733}
734
735impl<C> frame_system::offchain::CreateTransactionBase<C> for Runtime
736where
737    RuntimeCall: From<C>,
738{
739    type Extrinsic = UncheckedExtrinsic;
740    type RuntimeCall = RuntimeCall;
741}
742
743impl<C> subspace_runtime_primitives::CreateUnsigned<C> for Runtime
744where
745    RuntimeCall: From<C>,
746{
747    fn create_unsigned(call: Self::RuntimeCall) -> Self::Extrinsic {
748        create_unsigned_general_extrinsic(call)
749    }
750}
751
752parameter_types! {
753    pub const TransporterEndpointId: EndpointId = 1;
754}
755
756impl pallet_transporter::Config for Runtime {
757    type RuntimeEvent = RuntimeEvent;
758    type SelfChainId = SelfChainId;
759    type SelfEndpointId = TransporterEndpointId;
760    type Currency = Balances;
761    type Sender = Messenger;
762    type AccountIdConverter = AccountIdConverter;
763    type WeightInfo = pallet_transporter::weights::SubstrateWeight<Runtime>;
764    type SkipBalanceTransferChecks = ();
765}
766
767parameter_types! {
768    pub const MaximumReceiptDrift: BlockNumber = 2;
769    pub const InitialDomainTxRange: u64 = INITIAL_DOMAIN_TX_RANGE;
770    pub const DomainTxRangeAdjustmentInterval: u64 = 100;
771    pub const MinOperatorStake: Balance = 100 * SSC;
772    pub const MinNominatorStake: Balance = SSC;
773    /// Use the consensus chain's `Normal` extrinsics block size limit as the domain block size limit
774    pub MaxDomainBlockSize: u32 = NORMAL_DISPATCH_RATIO * MAX_BLOCK_LENGTH;
775    /// Use the consensus chain's `Normal` extrinsics block weight limit as the domain block weight limit
776    pub MaxDomainBlockWeight: Weight = NORMAL_DISPATCH_RATIO * BLOCK_WEIGHT_FOR_2_SEC;
777    pub const DomainInstantiationDeposit: Balance = 100 * SSC;
778    pub const MaxDomainNameLength: u32 = 32;
779    pub const BlockTreePruningDepth: u32 = DOMAINS_BLOCK_PRUNING_DEPTH;
780    pub const StakeWithdrawalLockingPeriod: BlockNumber = 20;
781    pub const StakeEpochDuration: DomainNumber = 5;
782    pub TreasuryAccount: AccountId = PalletId(*b"treasury").into_account_truncating();
783    pub const MaxPendingStakingOperation: u32 = 512;
784    pub const DomainsPalletId: PalletId = PalletId(*b"domains_");
785    pub const MaxInitialDomainAccounts: u32 = 20;
786    pub const MinInitialDomainAccountBalance: Balance = SSC;
787    pub const BundleLongevity: u32 = 5;
788    pub const WithdrawalLimit: u32 = 32;
789}
790
791// `BlockSlotCount` must at least keep the slot for the current and the parent block, it also need to
792// keep enough block slot for bundle validation
793const_assert!(BlockSlotCount::get() >= 2 && BlockSlotCount::get() > BundleLongevity::get());
794
795// `BlockHashCount` must greater than `BlockSlotCount` because we need to use the block number found
796// with `BlockSlotCount` to get the block hash.
797const_assert!(BlockHashCount::get() > BlockSlotCount::get());
798
799// Minimum operator stake must be >= minimum nominator stake since operator is also a nominator.
800const_assert!(MinOperatorStake::get() >= MinNominatorStake::get());
801
802pub struct BlockSlot;
803
804impl pallet_domains::BlockSlot<Runtime> for BlockSlot {
805    fn future_slot(block_number: BlockNumber) -> Option<Slot> {
806        let block_slots = Subspace::block_slots();
807        block_slots
808            .get(&block_number)
809            .map(|slot| *slot + Slot::from(BlockAuthoringDelay::get()))
810    }
811
812    fn slot_produced_after(to_check: Slot) -> Option<BlockNumber> {
813        let block_slots = Subspace::block_slots();
814        for (block_number, slot) in block_slots.into_iter().rev() {
815            if to_check > slot {
816                return Some(block_number);
817            }
818        }
819        None
820    }
821}
822
823pub struct OnChainRewards;
824
825impl sp_domains::OnChainRewards<Balance> for OnChainRewards {
826    fn on_chain_rewards(chain_id: ChainId, reward: Balance) {
827        match chain_id {
828            ChainId::Consensus => {
829                if let Some(block_author) = Subspace::find_block_reward_address() {
830                    let _ = Balances::deposit_creating(&block_author, reward);
831                }
832            }
833            ChainId::Domain(domain_id) => Domains::reward_domain_operators(
834                domain_id,
835                OperatorRewardSource::XDMProtocolFees,
836                reward,
837            ),
838        }
839    }
840}
841
842impl pallet_domains::Config for Runtime {
843    type RuntimeEvent = RuntimeEvent;
844    type DomainOrigin = pallet_domains::EnsureDomainOrigin;
845    type DomainHash = DomainHash;
846    type Balance = Balance;
847    type DomainHeader = DomainHeader;
848    type ConfirmationDepthK = ConfirmationDepthK;
849    type Currency = Balances;
850    type Share = Balance;
851    type HoldIdentifier = HoldIdentifierWrapper;
852    type BlockTreePruningDepth = BlockTreePruningDepth;
853    type ConsensusSlotProbability = SlotProbability;
854    type MaxDomainBlockSize = MaxDomainBlockSize;
855    type MaxDomainBlockWeight = MaxDomainBlockWeight;
856    type MaxDomainNameLength = MaxDomainNameLength;
857    type DomainInstantiationDeposit = DomainInstantiationDeposit;
858    type WeightInfo = pallet_domains::weights::SubstrateWeight<Runtime>;
859    type InitialDomainTxRange = InitialDomainTxRange;
860    type DomainTxRangeAdjustmentInterval = DomainTxRangeAdjustmentInterval;
861    type MinOperatorStake = MinOperatorStake;
862    type MinNominatorStake = MinNominatorStake;
863    type StakeWithdrawalLockingPeriod = StakeWithdrawalLockingPeriod;
864    type StakeEpochDuration = StakeEpochDuration;
865    type TreasuryAccount = TreasuryAccount;
866    type MaxPendingStakingOperation = MaxPendingStakingOperation;
867    type Randomness = Subspace;
868    type PalletId = DomainsPalletId;
869    type StorageFee = TransactionFees;
870    type BlockTimestamp = pallet_timestamp::Pallet<Runtime>;
871    type BlockSlot = BlockSlot;
872    type DomainsTransfersTracker = Transporter;
873    type MaxInitialDomainAccounts = MaxInitialDomainAccounts;
874    type MinInitialDomainAccountBalance = MinInitialDomainAccountBalance;
875    type BundleLongevity = BundleLongevity;
876    type DomainBundleSubmitted = Messenger;
877    type OnDomainInstantiated = Messenger;
878    type MmrHash = mmr::Hash;
879    type MmrProofVerifier = MmrProofVerifier;
880    type FraudProofStorageKeyProvider = StorageKeyProvider;
881    type OnChainRewards = OnChainRewards;
882    type WithdrawalLimit = WithdrawalLimit;
883}
884
885parameter_types! {
886    pub const AvgBlockspaceUsageNumBlocks: BlockNumber = 100;
887    pub const ProposerTaxOnVotes: (u32, u32) = (1, 10);
888}
889
890impl pallet_rewards::Config for Runtime {
891    type RuntimeEvent = RuntimeEvent;
892    type Currency = Balances;
893    type AvgBlockspaceUsageNumBlocks = AvgBlockspaceUsageNumBlocks;
894    type TransactionByteFee = TransactionByteFee;
895    type MaxRewardPoints = ConstU32<20>;
896    type ProposerTaxOnVotes = ProposerTaxOnVotes;
897    type RewardsEnabled = Subspace;
898    type FindBlockRewardAddress = Subspace;
899    type FindVotingRewardAddresses = Subspace;
900    type WeightInfo = pallet_rewards::weights::SubstrateWeight<Runtime>;
901    type OnReward = ();
902}
903
904mod mmr {
905    use super::Runtime;
906    pub use pallet_mmr::primitives::*;
907
908    pub type Leaf = <<Runtime as pallet_mmr::Config>::LeafData as LeafDataProvider>::LeafData;
909    pub type Hashing = <Runtime as pallet_mmr::Config>::Hashing;
910    pub type Hash = <Hashing as sp_runtime::traits::Hash>::Output;
911}
912
913pub struct BlockHashProvider;
914
915impl pallet_mmr::BlockHashProvider<BlockNumber, Hash> for BlockHashProvider {
916    fn block_hash(block_number: BlockNumber) -> Hash {
917        sp_subspace_mmr::subspace_mmr_runtime_interface::consensus_block_hash(block_number)
918            .expect("Hash must exist for a given block number.")
919    }
920}
921
922impl pallet_mmr::Config for Runtime {
923    const INDEXING_PREFIX: &'static [u8] = mmr::INDEXING_PREFIX;
924    type Hashing = Keccak256;
925    type LeafData = SubspaceMmr;
926    type OnNewRoot = SubspaceMmr;
927    type BlockHashProvider = BlockHashProvider;
928    type WeightInfo = ();
929    #[cfg(feature = "runtime-benchmarks")]
930    type BenchmarkHelper = ();
931}
932
933parameter_types! {
934    pub const MmrRootHashCount: u32 = 15;
935}
936
937impl pallet_subspace_mmr::Config for Runtime {
938    type MmrRootHash = mmr::Hash;
939    type MmrRootHashCount = MmrRootHashCount;
940}
941
942impl pallet_runtime_configs::Config for Runtime {
943    type WeightInfo = pallet_runtime_configs::weights::SubstrateWeight<Runtime>;
944}
945
946parameter_types! {
947    pub const MaxSignatories: u32 = 100;
948}
949
950macro_rules! deposit {
951    ($name:ident, $item_fee:expr, $items:expr, $bytes:expr) => {
952        pub struct $name;
953
954        impl Get<Balance> for $name {
955            fn get() -> Balance {
956                $item_fee.saturating_mul($items.into()).saturating_add(
957                    TransactionFees::transaction_byte_fee().saturating_mul($bytes.into()),
958                )
959            }
960        }
961    };
962}
963
964// One storage item; key size is 32; value is size 4+4+16+32 bytes = 56 bytes.
965// Each multisig costs 20 SSC + bytes_of_storge * TransactionByteFee
966deposit!(DepositBaseFee, 20 * SSC, 1u32, 88u32);
967
968// Additional storage item size of 32 bytes.
969deposit!(DepositFactor, 0u128, 0u32, 32u32);
970
971impl pallet_multisig::Config for Runtime {
972    type RuntimeEvent = RuntimeEvent;
973    type RuntimeCall = RuntimeCall;
974    type Currency = Balances;
975    type DepositBase = DepositBaseFee;
976    type DepositFactor = DepositFactor;
977    type MaxSignatories = MaxSignatories;
978    type WeightInfo = pallet_multisig::weights::SubstrateWeight<Runtime>;
979}
980
981construct_runtime!(
982    pub struct Runtime {
983        System: frame_system = 0,
984        Timestamp: pallet_timestamp = 1,
985
986        Subspace: pallet_subspace = 2,
987        Rewards: pallet_rewards = 9,
988
989        Balances: pallet_balances = 4,
990        TransactionFees: pallet_transaction_fees = 12,
991        TransactionPayment: pallet_transaction_payment = 5,
992        Utility: pallet_utility = 8,
993
994        Domains: pallet_domains = 11,
995        RuntimeConfigs: pallet_runtime_configs = 14,
996
997        Mmr: pallet_mmr = 30,
998        SubspaceMmr: pallet_subspace_mmr = 31,
999
1000        // messenger stuff
1001        // Note: Indexes should match with indexes on other chains and domains
1002        Messenger: pallet_messenger exclude_parts { Inherent } = 60,
1003        Transporter: pallet_transporter = 61,
1004
1005        // Multisig
1006        Multisig: pallet_multisig = 90,
1007
1008        // Reserve some room for other pallets as we'll remove sudo pallet eventually.
1009        Sudo: pallet_sudo = 100,
1010    }
1011);
1012
1013/// The address format for describing accounts.
1014pub type Address = sp_runtime::MultiAddress<AccountId, ()>;
1015/// Block header type as expected by this runtime.
1016pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
1017/// Block type as expected by this runtime.
1018pub type Block = generic::Block<Header, UncheckedExtrinsic>;
1019/// The SignedExtension to the basic transaction logic.
1020pub type SignedExtra = (
1021    frame_system::CheckNonZeroSender<Runtime>,
1022    frame_system::CheckSpecVersion<Runtime>,
1023    frame_system::CheckTxVersion<Runtime>,
1024    frame_system::CheckGenesis<Runtime>,
1025    frame_system::CheckMortality<Runtime>,
1026    frame_system::CheckNonce<Runtime>,
1027    frame_system::CheckWeight<Runtime>,
1028    pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
1029    DisablePallets,
1030    pallet_subspace::extensions::SubspaceExtension<Runtime>,
1031    pallet_domains::extensions::DomainsExtension<Runtime>,
1032    pallet_messenger::extensions::MessengerExtension<Runtime>,
1033);
1034/// Unchecked extrinsic type as expected by this runtime.
1035pub type UncheckedExtrinsic =
1036    generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
1037/// Executive: handles dispatch to the various modules.
1038pub type Executive = frame_executive::Executive<
1039    Runtime,
1040    Block,
1041    frame_system::ChainContext<Runtime>,
1042    Runtime,
1043    AllPalletsWithSystem,
1044>;
1045/// The payload being signed in transactions.
1046pub type SignedPayload = generic::SignedPayload<RuntimeCall, SignedExtra>;
1047
1048impl pallet_subspace::extensions::MaybeSubspaceCall<Runtime> for RuntimeCall {
1049    fn maybe_subspace_call(&self) -> Option<&pallet_subspace::Call<Runtime>> {
1050        match self {
1051            RuntimeCall::Subspace(call) => Some(call),
1052            _ => None,
1053        }
1054    }
1055}
1056
1057impl pallet_domains::extensions::MaybeDomainsCall<Runtime> for RuntimeCall {
1058    fn maybe_domains_call(&self) -> Option<&pallet_domains::Call<Runtime>> {
1059        match self {
1060            RuntimeCall::Domains(call) => Some(call),
1061            _ => None,
1062        }
1063    }
1064}
1065
1066impl pallet_messenger::extensions::MaybeMessengerCall<Runtime> for RuntimeCall {
1067    fn maybe_messenger_call(&self) -> Option<&pallet_messenger::Call<Runtime>> {
1068        match self {
1069            RuntimeCall::Messenger(call) => Some(call),
1070            _ => None,
1071        }
1072    }
1073}
1074
1075fn extract_segment_headers(ext: &UncheckedExtrinsic) -> Option<Vec<SegmentHeader>> {
1076    match &ext.function {
1077        RuntimeCall::Subspace(pallet_subspace::Call::store_segment_headers { segment_headers }) => {
1078            Some(segment_headers.clone())
1079        }
1080        _ => None,
1081    }
1082}
1083
1084fn is_xdm_mmr_proof_valid(ext: &ExtrinsicFor<Block>) -> Option<bool> {
1085    match &ext.function {
1086        RuntimeCall::Messenger(pallet_messenger::Call::relay_message { msg })
1087        | RuntimeCall::Messenger(pallet_messenger::Call::relay_message_response { msg }) => {
1088            let ConsensusChainMmrLeafProof {
1089                consensus_block_number,
1090                opaque_mmr_leaf,
1091                proof,
1092                ..
1093            } = msg.proof.consensus_mmr_proof();
1094
1095            let mmr_root = SubspaceMmr::mmr_root_hash(consensus_block_number)?;
1096
1097            Some(
1098                pallet_mmr::verify_leaves_proof::<mmr::Hashing, _>(
1099                    mmr_root,
1100                    vec![mmr::DataOrHash::Data(
1101                        EncodableOpaqueLeaf(opaque_mmr_leaf.0.clone()).into_opaque_leaf(),
1102                    )],
1103                    proof,
1104                )
1105                .is_ok(),
1106            )
1107        }
1108        _ => None,
1109    }
1110}
1111
1112// This code must be kept in sync with `crates/subspace-runtime/src/object_mapping.rs`.
1113fn extract_utility_block_object_mapping(
1114    mut base_offset: u32,
1115    objects: &mut Vec<BlockObject>,
1116    call: &pallet_utility::Call<Runtime>,
1117    mut recursion_depth_left: u16,
1118) {
1119    if recursion_depth_left == 0 {
1120        return;
1121    }
1122
1123    recursion_depth_left -= 1;
1124
1125    // Add enum variant to the base offset.
1126    base_offset += 1;
1127
1128    match call {
1129        pallet_utility::Call::batch { calls }
1130        | pallet_utility::Call::batch_all { calls }
1131        | pallet_utility::Call::force_batch { calls } => {
1132            base_offset += Compact::compact_len(&(calls.len() as u32)) as u32;
1133
1134            for call in calls {
1135                extract_call_block_object_mapping(base_offset, objects, call, recursion_depth_left);
1136
1137                base_offset += call.encoded_size() as u32;
1138            }
1139        }
1140        pallet_utility::Call::as_derivative { index, call } => {
1141            base_offset += index.encoded_size() as u32;
1142
1143            extract_call_block_object_mapping(
1144                base_offset,
1145                objects,
1146                call.as_ref(),
1147                recursion_depth_left,
1148            );
1149        }
1150        pallet_utility::Call::dispatch_as { as_origin, call } => {
1151            base_offset += as_origin.encoded_size() as u32;
1152
1153            extract_call_block_object_mapping(
1154                base_offset,
1155                objects,
1156                call.as_ref(),
1157                recursion_depth_left,
1158            );
1159        }
1160        pallet_utility::Call::with_weight { call, .. } => {
1161            extract_call_block_object_mapping(
1162                base_offset,
1163                objects,
1164                call.as_ref(),
1165                recursion_depth_left,
1166            );
1167        }
1168        pallet_utility::Call::__Ignore(_, _) => {
1169            // Ignore.
1170        }
1171    }
1172}
1173
1174fn extract_call_block_object_mapping(
1175    mut base_offset: u32,
1176    objects: &mut Vec<BlockObject>,
1177    call: &RuntimeCall,
1178    recursion_depth_left: u16,
1179) {
1180    // Add RuntimeCall enum variant to the base offset.
1181    base_offset += 1;
1182
1183    match call {
1184        // Extract the actual object mappings.
1185        RuntimeCall::System(frame_system::Call::remark { remark }) => {
1186            objects.push(BlockObject {
1187                hash: hashes::blake3_hash(remark),
1188                // Add frame_system::Call enum variant to the base offset.
1189                offset: base_offset + 1,
1190            });
1191        }
1192        RuntimeCall::System(frame_system::Call::remark_with_event { remark }) => {
1193            objects.push(BlockObject {
1194                hash: hashes::blake3_hash(remark),
1195                // Add frame_system::Call enum variant to the base offset.
1196                offset: base_offset + 1,
1197            });
1198        }
1199
1200        // Recursively extract object mappings for the call.
1201        RuntimeCall::Utility(call) => {
1202            extract_utility_block_object_mapping(base_offset, objects, call, recursion_depth_left)
1203        }
1204        // Other calls don't contain object mappings.
1205        _ => {}
1206    }
1207}
1208
1209fn extract_block_object_mapping(block: Block) -> BlockObjectMapping {
1210    let mut block_object_mapping = BlockObjectMapping::default();
1211    let mut base_offset =
1212        block.header.encoded_size() + Compact::compact_len(&(block.extrinsics.len() as u32));
1213    for extrinsic in block.extrinsics {
1214        let preamble_size = extrinsic.preamble.encoded_size();
1215        // Extrinsic starts with vector length followed by preamble and
1216        // `function` encoding.
1217        let base_extrinsic_offset = base_offset
1218            + Compact::compact_len(&((preamble_size + extrinsic.function.encoded_size()) as u32))
1219            + preamble_size;
1220
1221        extract_call_block_object_mapping(
1222            base_extrinsic_offset as u32,
1223            block_object_mapping.objects_mut(),
1224            &extrinsic.function,
1225            MAX_CALL_RECURSION_DEPTH as u16,
1226        );
1227
1228        base_offset += extrinsic.encoded_size();
1229    }
1230
1231    block_object_mapping
1232}
1233
1234fn extract_successful_bundles(
1235    domain_id: DomainId,
1236    extrinsics: Vec<UncheckedExtrinsic>,
1237) -> OpaqueBundles<Block, DomainHeader, Balance> {
1238    let successful_bundles = Domains::successful_bundles(domain_id);
1239    extrinsics
1240        .into_iter()
1241        .filter_map(|uxt| match uxt.function {
1242            RuntimeCall::Domains(pallet_domains::Call::submit_bundle { opaque_bundle })
1243                if opaque_bundle.domain_id() == domain_id
1244                    && successful_bundles.contains(&opaque_bundle.hash()) =>
1245            {
1246                Some(opaque_bundle)
1247            }
1248            _ => None,
1249        })
1250        .collect()
1251}
1252
1253fn create_unsigned_general_extrinsic(call: RuntimeCall) -> UncheckedExtrinsic {
1254    let extra: SignedExtra = (
1255        frame_system::CheckNonZeroSender::<Runtime>::new(),
1256        frame_system::CheckSpecVersion::<Runtime>::new(),
1257        frame_system::CheckTxVersion::<Runtime>::new(),
1258        frame_system::CheckGenesis::<Runtime>::new(),
1259        frame_system::CheckMortality::<Runtime>::from(generic::Era::Immortal),
1260        // for unsigned extrinsic, nonce check will be skipped
1261        // so set a default value
1262        frame_system::CheckNonce::<Runtime>::from(0u32.into()),
1263        frame_system::CheckWeight::<Runtime>::new(),
1264        // for unsigned extrinsic, transaction fee check will be skipped
1265        // so set a default value
1266        pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(0u128),
1267        DisablePallets,
1268        pallet_subspace::extensions::SubspaceExtension::<Runtime>::new(),
1269        pallet_domains::extensions::DomainsExtension::<Runtime>::new(),
1270        pallet_messenger::extensions::MessengerExtension::<Runtime>::new(),
1271    );
1272
1273    UncheckedExtrinsic::new_transaction(call, extra)
1274}
1275
1276struct RewardAddress([u8; 32]);
1277
1278impl From<PublicKey> for RewardAddress {
1279    #[inline]
1280    fn from(public_key: PublicKey) -> Self {
1281        Self(*public_key)
1282    }
1283}
1284
1285impl From<RewardAddress> for AccountId32 {
1286    #[inline]
1287    fn from(reward_address: RewardAddress) -> Self {
1288        reward_address.0.into()
1289    }
1290}
1291
1292pub struct StorageKeyProvider;
1293impl FraudProofStorageKeyProvider<NumberFor<Block>> for StorageKeyProvider {
1294    fn storage_key(req: FraudProofStorageKeyRequest<NumberFor<Block>>) -> Vec<u8> {
1295        match req {
1296            FraudProofStorageKeyRequest::InvalidInherentExtrinsicData => {
1297                pallet_domains::BlockInherentExtrinsicData::<Runtime>::hashed_key().to_vec()
1298            }
1299            FraudProofStorageKeyRequest::SuccessfulBundles(domain_id) => {
1300                pallet_domains::SuccessfulBundles::<Runtime>::hashed_key_for(domain_id)
1301            }
1302            FraudProofStorageKeyRequest::DomainAllowlistUpdates(domain_id) => {
1303                Messenger::domain_allow_list_update_storage_key(domain_id)
1304            }
1305            FraudProofStorageKeyRequest::DomainRuntimeUpgrades => {
1306                pallet_domains::DomainRuntimeUpgrades::<Runtime>::hashed_key().to_vec()
1307            }
1308            FraudProofStorageKeyRequest::RuntimeRegistry(runtime_id) => {
1309                pallet_domains::RuntimeRegistry::<Runtime>::hashed_key_for(runtime_id)
1310            }
1311            FraudProofStorageKeyRequest::DomainSudoCall(domain_id) => {
1312                pallet_domains::DomainSudoCalls::<Runtime>::hashed_key_for(domain_id)
1313            }
1314            FraudProofStorageKeyRequest::EvmDomainContractCreationAllowedByCall(domain_id) => {
1315                pallet_domains::EvmDomainContractCreationAllowedByCalls::<Runtime>::hashed_key_for(
1316                    domain_id,
1317                )
1318            }
1319            FraudProofStorageKeyRequest::MmrRoot(block_number) => {
1320                pallet_subspace_mmr::MmrRootHashes::<Runtime>::hashed_key_for(block_number)
1321            }
1322        }
1323    }
1324}
1325
1326impl_runtime_apis! {
1327    impl sp_api::Core<Block> for Runtime {
1328        fn version() -> RuntimeVersion {
1329            VERSION
1330        }
1331
1332        fn execute_block(block: Block) {
1333            Executive::execute_block(block);
1334        }
1335
1336        fn initialize_block(header: &HeaderFor<Block>) -> ExtrinsicInclusionMode {
1337            Executive::initialize_block(header)
1338        }
1339    }
1340
1341    impl sp_api::Metadata<Block> for Runtime {
1342        fn metadata() -> OpaqueMetadata {
1343            OpaqueMetadata::new(Runtime::metadata().into())
1344        }
1345
1346        fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
1347            Runtime::metadata_at_version(version)
1348        }
1349
1350        fn metadata_versions() -> Vec<u32> {
1351            Runtime::metadata_versions()
1352        }
1353    }
1354
1355    impl sp_block_builder::BlockBuilder<Block> for Runtime {
1356        fn apply_extrinsic(extrinsic: ExtrinsicFor<Block>) -> ApplyExtrinsicResult {
1357            Executive::apply_extrinsic(extrinsic)
1358        }
1359
1360        fn finalize_block() -> HeaderFor<Block> {
1361            Executive::finalize_block()
1362        }
1363
1364        fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<ExtrinsicFor<Block>> {
1365            data.create_extrinsics()
1366        }
1367
1368        fn check_inherents(
1369            block: Block,
1370            data: sp_inherents::InherentData,
1371        ) -> sp_inherents::CheckInherentsResult {
1372            data.check_extrinsics(&block)
1373        }
1374    }
1375
1376    impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
1377        fn validate_transaction(
1378            source: TransactionSource,
1379            tx: ExtrinsicFor<Block>,
1380            block_hash: BlockHashFor<Block>,
1381        ) -> TransactionValidity {
1382            Executive::validate_transaction(source, tx, block_hash)
1383        }
1384    }
1385
1386    impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
1387        fn offchain_worker(header: &HeaderFor<Block>) {
1388            Executive::offchain_worker(header)
1389        }
1390    }
1391
1392    impl sp_objects::ObjectsApi<Block> for Runtime {
1393        fn extract_block_object_mapping(block: Block) -> BlockObjectMapping {
1394            extract_block_object_mapping(block)
1395        }
1396    }
1397
1398    impl sp_consensus_subspace::SubspaceApi<Block, PublicKey> for Runtime {
1399        fn pot_parameters() -> PotParameters {
1400            Subspace::pot_parameters()
1401        }
1402
1403        fn solution_ranges() -> SolutionRanges {
1404            Subspace::solution_ranges()
1405        }
1406
1407        fn submit_vote_extrinsic(
1408            signed_vote: SignedVote<NumberFor<Block>, BlockHashFor<Block>, PublicKey>,
1409        ) {
1410            let SignedVote { vote, signature } = signed_vote;
1411            let Vote::V0 {
1412                height,
1413                parent_hash,
1414                slot,
1415                solution,
1416                proof_of_time,
1417                future_proof_of_time,
1418            } = vote;
1419
1420            Subspace::submit_vote(SignedVote {
1421                vote: Vote::V0 {
1422                    height,
1423                    parent_hash,
1424                    slot,
1425                    solution: solution.into_reward_address_format::<RewardAddress, AccountId32>(),
1426                    proof_of_time,
1427                    future_proof_of_time,
1428                },
1429                signature,
1430            })
1431        }
1432
1433        fn history_size() -> HistorySize {
1434            <pallet_subspace::Pallet<Runtime>>::history_size()
1435        }
1436
1437        fn max_pieces_in_sector() -> u16 {
1438            MAX_PIECES_IN_SECTOR
1439        }
1440
1441        fn segment_commitment(segment_index: SegmentIndex) -> Option<SegmentCommitment> {
1442            Subspace::segment_commitment(segment_index)
1443        }
1444
1445        fn extract_segment_headers(ext: &ExtrinsicFor<Block>) -> Option<Vec<SegmentHeader >> {
1446            extract_segment_headers(ext)
1447        }
1448
1449        fn is_inherent(ext: &ExtrinsicFor<Block>) -> bool {
1450            match &ext.function {
1451                RuntimeCall::Subspace(call) => Subspace::is_inherent(call),
1452                RuntimeCall::Timestamp(call) => Timestamp::is_inherent(call),
1453                _ => false,
1454            }
1455        }
1456
1457        fn root_plot_public_key() -> Option<PublicKey> {
1458            Subspace::root_plot_public_key()
1459        }
1460
1461        fn should_adjust_solution_range() -> bool {
1462            Subspace::should_adjust_solution_range()
1463        }
1464
1465        fn chain_constants() -> ChainConstants {
1466            ChainConstants::V0 {
1467                confirmation_depth_k: ConfirmationDepthK::get(),
1468                block_authoring_delay: Slot::from(BlockAuthoringDelay::get()),
1469                era_duration: EraDuration::get(),
1470                slot_probability: SlotProbability::get(),
1471                slot_duration: SlotDuration::from_millis(SLOT_DURATION),
1472                recent_segments: RecentSegments::get(),
1473                recent_history_fraction: RecentHistoryFraction::get(),
1474                min_sector_lifetime: MinSectorLifetime::get(),
1475            }
1476        }
1477    }
1478
1479    impl sp_domains::DomainsApi<Block, DomainHeader> for Runtime {
1480        fn submit_bundle_unsigned(
1481            opaque_bundle: OpaqueBundle<NumberFor<Block>, BlockHashFor<Block>, DomainHeader, Balance>,
1482        ) {
1483            Domains::submit_bundle_unsigned(opaque_bundle)
1484        }
1485
1486        fn submit_receipt_unsigned(
1487            singleton_receipt: sp_domains::SealedSingletonReceipt<NumberFor<Block>, BlockHashFor<Block>, DomainHeader, Balance>,
1488        ) {
1489            Domains::submit_receipt_unsigned(singleton_receipt)
1490        }
1491
1492        fn extract_successful_bundles(
1493            domain_id: DomainId,
1494            extrinsics: Vec<ExtrinsicFor<Block>>,
1495        ) -> OpaqueBundles<Block, DomainHeader, Balance> {
1496            extract_successful_bundles(domain_id, extrinsics)
1497        }
1498
1499        fn extrinsics_shuffling_seed() -> Randomness {
1500            Randomness::from(Domains::extrinsics_shuffling_seed().to_fixed_bytes())
1501        }
1502
1503        fn domain_runtime_code(domain_id: DomainId) -> Option<Vec<u8>> {
1504            Domains::domain_runtime_code(domain_id)
1505        }
1506
1507        fn runtime_id(domain_id: DomainId) -> Option<sp_domains::RuntimeId> {
1508            Domains::runtime_id(domain_id)
1509        }
1510
1511        fn runtime_upgrades() -> Vec<sp_domains::RuntimeId> {
1512            Domains::runtime_upgrades()
1513        }
1514
1515        fn domain_instance_data(domain_id: DomainId) -> Option<(DomainInstanceData, NumberFor<Block>)> {
1516            Domains::domain_instance_data(domain_id)
1517        }
1518
1519        fn domain_timestamp() -> Moment {
1520            Domains::timestamp()
1521        }
1522
1523        fn timestamp() -> Moment {
1524            Timestamp::now()
1525        }
1526
1527        fn consensus_transaction_byte_fee() -> Balance {
1528            Domains::consensus_transaction_byte_fee()
1529        }
1530
1531        fn consensus_chain_byte_fee() -> Balance {
1532            DOMAIN_STORAGE_FEE_MULTIPLIER * TransactionFees::transaction_byte_fee()
1533        }
1534
1535        fn domain_tx_range(_: DomainId) -> U256 {
1536            U256::MAX
1537        }
1538
1539        fn genesis_state_root(domain_id: DomainId) -> Option<H256> {
1540            Domains::genesis_state_root(domain_id)
1541        }
1542
1543        fn head_receipt_number(domain_id: DomainId) -> DomainNumber {
1544            Domains::head_receipt_number(domain_id)
1545        }
1546
1547        fn oldest_unconfirmed_receipt_number(domain_id: DomainId) -> Option<DomainNumber> {
1548            Domains::oldest_unconfirmed_receipt_number(domain_id)
1549        }
1550
1551        fn domain_bundle_limit(domain_id: DomainId) -> Option<sp_domains::DomainBundleLimit> {
1552            Domains::domain_bundle_limit(domain_id).ok().flatten()
1553        }
1554
1555        fn non_empty_er_exists(domain_id: DomainId) -> bool {
1556            Domains::non_empty_er_exists(domain_id)
1557        }
1558
1559        fn domain_best_number(domain_id: DomainId) -> Option<DomainNumber> {
1560            Domains::domain_best_number(domain_id).ok()
1561        }
1562
1563        fn execution_receipt(receipt_hash: DomainHash) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>> {
1564            Domains::execution_receipt(receipt_hash)
1565        }
1566
1567        fn domain_operators(domain_id: DomainId) -> Option<(BTreeMap<OperatorId, Balance>, Vec<OperatorId>)> {
1568            Domains::domain_staking_summary(domain_id).map(|summary| {
1569                let next_operators = summary.next_operators.into_iter().collect();
1570                (summary.current_operators, next_operators)
1571            })
1572        }
1573
1574        fn receipt_hash(domain_id: DomainId, domain_number: DomainNumber) -> Option<DomainHash> {
1575            Domains::receipt_hash(domain_id, domain_number)
1576        }
1577
1578        fn latest_confirmed_domain_block(domain_id: DomainId) -> Option<(DomainNumber, DomainHash)>{
1579            Domains::latest_confirmed_domain_block(domain_id)
1580        }
1581
1582        fn is_bad_er_pending_to_prune(domain_id: DomainId, receipt_hash: DomainHash) -> bool {
1583            Domains::execution_receipt(receipt_hash).map(
1584                |er| Domains::is_bad_er_pending_to_prune(domain_id, er.domain_block_number)
1585            )
1586            .unwrap_or(false)
1587        }
1588
1589        fn storage_fund_account_balance(operator_id: OperatorId) -> Balance {
1590            Domains::storage_fund_account_balance(operator_id)
1591        }
1592
1593        fn is_domain_runtime_upgraded_since(domain_id: DomainId, at: NumberFor<Block>) -> Option<bool> {
1594            Domains::is_domain_runtime_upgraded_since(domain_id, at)
1595        }
1596
1597        fn domain_sudo_call(domain_id: DomainId) -> Option<Vec<u8>> {
1598            Domains::domain_sudo_call(domain_id)
1599        }
1600
1601        fn evm_domain_contract_creation_allowed_by_call(domain_id: DomainId) -> Option<PermissionedActionAllowedBy<EthereumAccountId>> {
1602            Domains::evm_domain_contract_creation_allowed_by_call(domain_id)
1603        }
1604
1605        fn last_confirmed_domain_block_receipt(domain_id: DomainId) -> Option<ExecutionReceiptFor<DomainHeader, Block, Balance>>{
1606            Domains::latest_confirmed_domain_execution_receipt(domain_id)
1607        }
1608    }
1609
1610    impl sp_domains::BundleProducerElectionApi<Block, Balance> for Runtime {
1611        fn bundle_producer_election_params(domain_id: DomainId) -> Option<BundleProducerElectionParams<Balance>> {
1612            Domains::bundle_producer_election_params(domain_id)
1613        }
1614
1615        fn operator(operator_id: OperatorId) -> Option<(OperatorPublicKey, Balance)> {
1616            Domains::operator(operator_id)
1617        }
1618    }
1619
1620    impl sp_session::SessionKeys<Block> for Runtime {
1621        fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
1622            SessionKeys::generate(seed)
1623        }
1624
1625        fn decode_session_keys(
1626            encoded: Vec<u8>,
1627        ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {
1628            SessionKeys::decode_into_raw_public_keys(&encoded)
1629        }
1630    }
1631
1632    impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce> for Runtime {
1633        fn account_nonce(account: AccountId) -> Nonce {
1634            *System::account_nonce(account)
1635        }
1636    }
1637
1638    impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {
1639        fn query_info(
1640            uxt: ExtrinsicFor<Block>,
1641            len: u32,
1642        ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
1643            TransactionPayment::query_info(uxt, len)
1644        }
1645        fn query_fee_details(
1646            uxt: ExtrinsicFor<Block>,
1647            len: u32,
1648        ) -> pallet_transaction_payment::FeeDetails<Balance> {
1649            TransactionPayment::query_fee_details(uxt, len)
1650        }
1651        fn query_weight_to_fee(weight: Weight) -> Balance {
1652            TransactionPayment::weight_to_fee(weight)
1653        }
1654        fn query_length_to_fee(length: u32) -> Balance {
1655            TransactionPayment::length_to_fee(length)
1656        }
1657    }
1658
1659    impl sp_messenger::MessengerApi<Block, BlockNumber, BlockHashFor<Block>> for Runtime {
1660        fn is_xdm_mmr_proof_valid(
1661            extrinsic: &ExtrinsicFor<Block>
1662        ) -> Option<bool> {
1663            is_xdm_mmr_proof_valid(extrinsic)
1664        }
1665
1666        fn extract_xdm_mmr_proof(ext: &ExtrinsicFor<Block>) -> Option<ConsensusChainMmrLeafProof<BlockNumber, BlockHashFor<Block>, sp_core::H256>> {
1667            match &ext.function {
1668                RuntimeCall::Messenger(pallet_messenger::Call::relay_message { msg })
1669                | RuntimeCall::Messenger(pallet_messenger::Call::relay_message_response { msg }) => {
1670                    Some(msg.proof.consensus_mmr_proof())
1671                }
1672                _ => None,
1673            }
1674        }
1675
1676        fn batch_extract_xdm_mmr_proof(extrinsics: &Vec<ExtrinsicFor<Block>>) -> BTreeMap<u32, ConsensusChainMmrLeafProof<BlockNumber, BlockHashFor<Block>, sp_core::H256>> {
1677            let mut mmr_proofs = BTreeMap::new();
1678            for (index, ext) in extrinsics.iter().enumerate() {
1679                match &ext.function {
1680                    RuntimeCall::Messenger(pallet_messenger::Call::relay_message { msg })
1681                    | RuntimeCall::Messenger(pallet_messenger::Call::relay_message_response { msg }) => {
1682                        mmr_proofs.insert(index as u32, msg.proof.consensus_mmr_proof());
1683                    }
1684                    _ => {},
1685                }
1686            }
1687            mmr_proofs
1688        }
1689
1690        fn confirmed_domain_block_storage_key(domain_id: DomainId) -> Vec<u8> {
1691            Domains::confirmed_domain_block_storage_key(domain_id)
1692        }
1693
1694        fn outbox_storage_key(message_key: MessageKey) -> Vec<u8> {
1695            Messenger::outbox_storage_key(message_key)
1696        }
1697
1698        fn inbox_response_storage_key(message_key: MessageKey) -> Vec<u8> {
1699            Messenger::inbox_response_storage_key(message_key)
1700        }
1701
1702        fn domain_chains_allowlist_update(domain_id: DomainId) -> Option<DomainAllowlistUpdates>{
1703            Messenger::domain_chains_allowlist_update(domain_id)
1704        }
1705
1706        fn xdm_id(ext: &ExtrinsicFor<Block>) -> Option<XdmId> {
1707            match &ext.function {
1708                RuntimeCall::Messenger(pallet_messenger::Call::relay_message { msg })=> {
1709                    Some(XdmId::RelayMessage((msg.src_chain_id, msg.channel_id, msg.nonce)))
1710                }
1711                RuntimeCall::Messenger(pallet_messenger::Call::relay_message_response { msg }) => {
1712                    Some(XdmId::RelayResponseMessage((msg.src_chain_id, msg.channel_id, msg.nonce)))
1713                }
1714                _ => None,
1715            }
1716        }
1717
1718        fn channel_nonce(chain_id: ChainId, channel_id: ChannelId) -> Option<ChannelNonce> {
1719            Messenger::channel_nonce(chain_id, channel_id)
1720        }
1721    }
1722
1723    impl sp_messenger::RelayerApi<Block, BlockNumber, BlockNumber, BlockHashFor<Block>> for Runtime {
1724        fn block_messages() -> BlockMessagesWithStorageKey {
1725            BlockMessagesWithStorageKey::default()
1726        }
1727
1728        fn outbox_message_unsigned(msg: CrossDomainMessage<NumberFor<Block>, BlockHashFor<Block>, BlockHashFor<Block>>) -> Option<ExtrinsicFor<Block>> {
1729            Messenger::outbox_message_unsigned(msg)
1730        }
1731
1732        fn inbox_response_message_unsigned(msg: CrossDomainMessage<NumberFor<Block>, BlockHashFor<Block>, BlockHashFor<Block>>) -> Option<ExtrinsicFor<Block>> {
1733            Messenger::inbox_response_message_unsigned(msg)
1734        }
1735
1736        fn should_relay_outbox_message(_: ChainId, _: MessageId) -> bool {
1737            false
1738        }
1739
1740        fn should_relay_inbox_message_response(_: ChainId, _: MessageId) -> bool {
1741            false
1742        }
1743
1744        fn updated_channels() -> BTreeSet<(ChainId, ChannelId)> {
1745            Messenger::updated_channels()
1746        }
1747
1748        fn channel_storage_key(chain_id: ChainId, channel_id: ChannelId) -> Vec<u8> {
1749            Messenger::channel_storage_key(chain_id, channel_id)
1750        }
1751
1752        fn open_channels() -> BTreeSet<(ChainId, ChannelId)> {
1753            Messenger::open_channels()
1754        }
1755
1756        fn block_messages_with_query(query: BlockMessagesQuery) -> MessagesWithStorageKey {
1757            Messenger::get_block_messages(query)
1758        }
1759
1760        fn channels_and_state() -> Vec<(ChainId, ChannelId, ChannelStateWithNonce)> {
1761            Messenger::channels_and_states()
1762        }
1763
1764        fn first_outbox_message_nonce_to_relay(dst_chain_id: ChainId, channel_id: ChannelId, from_nonce: XdmNonce) -> Option<XdmNonce> {
1765            Messenger::first_outbox_message_nonce_to_relay(dst_chain_id, channel_id, from_nonce)
1766        }
1767
1768        fn first_inbox_message_response_nonce_to_relay(dst_chain_id: ChainId, channel_id: ChannelId, from_nonce: XdmNonce) -> Option<XdmNonce> {
1769            Messenger::first_inbox_message_response_nonce_to_relay(dst_chain_id, channel_id, from_nonce)
1770        }
1771    }
1772
1773    impl sp_domains_fraud_proof::FraudProofApi<Block, DomainHeader> for Runtime {
1774        fn submit_fraud_proof_unsigned(fraud_proof: FraudProof<NumberFor<Block>, BlockHashFor<Block>, DomainHeader, H256>) {
1775            Domains::submit_fraud_proof_unsigned(fraud_proof)
1776        }
1777
1778        fn fraud_proof_storage_key(req: FraudProofStorageKeyRequest<NumberFor<Block>>) -> Vec<u8> {
1779            <StorageKeyProvider as FraudProofStorageKeyProvider<NumberFor<Block>>>::storage_key(req)
1780        }
1781    }
1782
1783    impl mmr::MmrApi<Block, mmr::Hash, BlockNumber> for Runtime {
1784        fn mmr_root() -> Result<mmr::Hash, mmr::Error> {
1785            Ok(Mmr::mmr_root())
1786        }
1787
1788        fn mmr_leaf_count() -> Result<mmr::LeafIndex, mmr::Error> {
1789            Ok(Mmr::mmr_leaves())
1790        }
1791
1792        fn generate_proof(
1793            block_numbers: Vec<BlockNumber>,
1794            best_known_block_number: Option<BlockNumber>,
1795        ) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::LeafProof<mmr::Hash>), mmr::Error> {
1796            Mmr::generate_proof(block_numbers, best_known_block_number).map(
1797                |(leaves, proof)| {
1798                    (
1799                        leaves
1800                            .into_iter()
1801                            .map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf))
1802                            .collect(),
1803                        proof,
1804                    )
1805                },
1806            )
1807        }
1808
1809        fn verify_proof(leaves: Vec<mmr::EncodableOpaqueLeaf>, proof: mmr::LeafProof<mmr::Hash>)
1810            -> Result<(), mmr::Error>
1811        {
1812            let leaves = leaves.into_iter().map(|leaf|
1813                leaf.into_opaque_leaf()
1814                .try_decode()
1815                .ok_or(mmr::Error::Verify)).collect::<Result<Vec<mmr::Leaf>, mmr::Error>>()?;
1816            Mmr::verify_leaves(leaves, proof)
1817        }
1818
1819        fn verify_proof_stateless(
1820            root: mmr::Hash,
1821            leaves: Vec<mmr::EncodableOpaqueLeaf>,
1822            proof: mmr::LeafProof<mmr::Hash>
1823        ) -> Result<(), mmr::Error> {
1824            let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect();
1825            pallet_mmr::verify_leaves_proof::<mmr::Hashing, _>(root, nodes, proof)
1826        }
1827    }
1828
1829    impl subspace_test_primitives::OnchainStateApi<Block, AccountId, Balance> for Runtime {
1830        fn free_balance(account_id: AccountId) -> Balance {
1831            Balances::free_balance(account_id)
1832        }
1833
1834        fn get_open_channel_for_chain(dst_chain_id: ChainId) -> Option<ChannelId> {
1835            Messenger::get_open_channel_for_chain(dst_chain_id)
1836        }
1837
1838        fn verify_proof_and_extract_leaf(mmr_leaf_proof: ConsensusChainMmrLeafProof<NumberFor<Block>, BlockHashFor<Block>, H256>) -> Option<mmr::Leaf> {
1839            <MmrProofVerifier as sp_subspace_mmr::MmrProofVerifier<_, _, _,>>::verify_proof_and_extract_leaf(mmr_leaf_proof)
1840        }
1841
1842        fn domain_balance(domain_id: DomainId) -> Balance {
1843            Transporter::domain_balances(domain_id)
1844        }
1845    }
1846
1847    impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
1848        fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
1849            build_state::<RuntimeGenesisConfig>(config)
1850        }
1851
1852        fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
1853            get_preset::<RuntimeGenesisConfig>(id, |_| None)
1854        }
1855
1856        fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
1857            vec![]
1858        }
1859    }
1860}
1861
1862/// Disable balance transfers, if configured in the runtime.
1863#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Default, TypeInfo)]
1864pub struct DisablePallets;
1865
1866impl DisablePallets {
1867    fn do_validate_unsigned(call: &RuntimeCall) -> TransactionValidity {
1868        if matches!(call, RuntimeCall::Domains(_)) && !RuntimeConfigs::enable_domains() {
1869            InvalidTransaction::Call.into()
1870        } else {
1871            Ok(ValidTransaction::default())
1872        }
1873    }
1874
1875    fn do_validate_signed(call: &RuntimeCall) -> TransactionValidity {
1876        // Disable normal balance transfers.
1877        if !RuntimeConfigs::enable_balance_transfers() && contains_balance_transfer(call) {
1878            Err(InvalidTransaction::Call.into())
1879        } else {
1880            Ok(ValidTransaction::default())
1881        }
1882    }
1883}
1884
1885impl TransactionExtension<RuntimeCall> for DisablePallets {
1886    const IDENTIFIER: &'static str = "DisablePallets";
1887    type Implicit = ();
1888    type Val = ();
1889    type Pre = ();
1890
1891    // TODO: calculate weight for extension
1892    fn weight(&self, _call: &RuntimeCall) -> Weight {
1893        // there is always one storage read
1894        <Runtime as frame_system::Config>::DbWeight::get().reads(1)
1895    }
1896
1897    fn validate(
1898        &self,
1899        origin: OriginFor<Runtime>,
1900        call: &RuntimeCallFor<Runtime>,
1901        _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
1902        _len: usize,
1903        _self_implicit: Self::Implicit,
1904        _inherited_implication: &impl Encode,
1905        _source: TransactionSource,
1906    ) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> {
1907        let validity = if origin.as_system_origin_signer().is_some() {
1908            Self::do_validate_signed(call)?
1909        } else {
1910            ValidTransaction::default()
1911        };
1912
1913        Ok((validity, (), origin))
1914    }
1915
1916    impl_tx_ext_default!(RuntimeCallFor<Runtime>; prepare);
1917
1918    fn bare_validate(
1919        call: &RuntimeCallFor<Runtime>,
1920        _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
1921        _len: usize,
1922    ) -> TransactionValidity {
1923        Self::do_validate_unsigned(call)
1924    }
1925
1926    fn bare_validate_and_prepare(
1927        call: &RuntimeCallFor<Runtime>,
1928        _info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
1929        _len: usize,
1930    ) -> Result<(), TransactionValidityError> {
1931        Self::do_validate_unsigned(call)?;
1932        Ok(())
1933    }
1934}
1935
1936fn contains_balance_transfer(call: &RuntimeCall) -> bool {
1937    for call in nested_call_iter::<Runtime>(call) {
1938        // Any other calls might contain nested calls, so we can only return early if we find a
1939        // balance transfer call.
1940        if let RuntimeCall::Balances(
1941            pallet_balances::Call::transfer_allow_death { .. }
1942            | pallet_balances::Call::transfer_keep_alive { .. }
1943            | pallet_balances::Call::transfer_all { .. },
1944        ) = call
1945        {
1946            return true;
1947        }
1948    }
1949
1950    false
1951}
1952
1953#[cfg(test)]
1954mod tests {
1955    use crate::Runtime;
1956    use pallet_domains::bundle_storage_fund::AccountType;
1957    use sp_domains::OperatorId;
1958    use sp_runtime::traits::AccountIdConversion;
1959
1960    #[test]
1961    fn test_bundle_storage_fund_account_uniqueness() {
1962        let _: <Runtime as frame_system::Config>::AccountId = <Runtime as pallet_domains::Config>::PalletId::get()
1963            .try_into_sub_account((AccountType::StorageFund, OperatorId::MAX))
1964            .expect(
1965                "The `AccountId` type must be large enough to fit the seed of the bundle storage fund account",
1966            );
1967    }
1968}