Skip to main content

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