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