diff --git a/.github/workflows/check-migrations.yml b/.github/workflows/check-migrations.yml index b3cbb702ea..83d14c2120 100644 --- a/.github/workflows/check-migrations.yml +++ b/.github/workflows/check-migrations.yml @@ -77,7 +77,7 @@ jobs: CHECKS="pre-and-post" else echo "Enabling weight checks since we are not on a relay" - + echo "Enabling try-state checks on the non-relay" CHECKS="all" fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c39a997c0..609983d7c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] -### Fixed +### Fixed +- Fix AH staking inflation calculation to use correct total issuance (https://github.com/polkadot-fellows/runtimes/pull/998). - Set invulnerable deposit for Polkadot AssetHub staking election ([#993](https://github.com/polkadot-fellows/runtimes/pull/993)) ## [2.0.1] 04.11.2025 diff --git a/Cargo.lock b/Cargo.lock index ac877a7a41..b5a5d11f3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,6 +1149,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "system-parachains-common", "system-parachains-constants", "xcm-runtime-apis", ] @@ -12314,10 +12315,7 @@ name = "relay-common" version = "1.0.0" dependencies = [ "pallet-staking-reward-fn", - "parity-scale-codec", "polkadot-primitives", - "scale-info", - "sp-api", "sp-runtime", ] @@ -16314,6 +16312,10 @@ dependencies = [ "cumulus-primitives-core", "frame-support", "log", + "parity-scale-codec", + "polkadot-primitives", + "scale-info", + "sp-api", "sp-runtime", "sp-state-machine", ] diff --git a/relay/common/Cargo.toml b/relay/common/Cargo.toml index abdbb864ac..c91ca2b85d 100644 --- a/relay/common/Cargo.toml +++ b/relay/common/Cargo.toml @@ -8,23 +8,15 @@ repository.workspace = true version.workspace = true [dependencies] -codec = { features = ["derive", "max-encoded-len"], workspace = true } -scale-info = { features = ["derive"], workspace = true } - -sp-api = { workspace = true } sp-runtime = { workspace = true } -polkadot-primitives = { workspace = true } pallet-staking-reward-fn = { workspace = true } +polkadot-primitives = { workspace = true } [features] default = ["std"] std = [ - "codec/std", - "scale-info/std", - "pallet-staking-reward-fn/std", "polkadot-primitives/std", - "sp-api/std", "sp-runtime/std", ] runtime-benchmarks = [ diff --git a/relay/common/src/lib.rs b/relay/common/src/lib.rs index e38d994081..a846796396 100644 --- a/relay/common/src/lib.rs +++ b/relay/common/src/lib.rs @@ -20,35 +20,6 @@ use polkadot_primitives::Balance; use sp_runtime::{Perquintill, Saturating}; -/// Extra runtime APIs for kusama runtime. -pub mod apis { - /// Information about the current inflation rate of the system. - /// - /// Both fields should be treated as best-effort, given that the inflation rate might not be - /// fully predict-able. - #[derive(scale_info::TypeInfo, codec::Encode, codec::Decode)] - #[cfg_attr(feature = "std", derive(Debug))] - pub struct InflationInfo { - /// The rate of inflation estimated per annum. - pub inflation: sp_runtime::Perquintill, - /// Next amount that we anticipate to mint. - /// - /// First item is the amount that goes to stakers, second is the leftover that is usually - /// forwarded to the treasury. - pub next_mint: (polkadot_primitives::Balance, polkadot_primitives::Balance), - } - - sp_api::decl_runtime_apis! { - pub trait Inflation { - /// Return the current estimates of the inflation amount. - /// - /// This is marked as experimental in light of RFC#89. Nonetheless, its usage is highly - /// recommended over trying to read-storage, or re-create the onchain logic. - fn experimental_inflation_prediction_info() -> InflationInfo; - } - } -} - // ---- TODO: Below is copy pasted from sdk, remove once we pull the version containing // https://github.com/paritytech/polkadot-sdk/pull/4938 diff --git a/relay/kusama/src/lib.rs b/relay/kusama/src/lib.rs index a9462d094d..48ecbe3126 100644 --- a/relay/kusama/src/lib.rs +++ b/relay/kusama/src/lib.rs @@ -97,7 +97,6 @@ use polkadot_runtime_common::{ paras_registrar, prod_or_fast, slots, BalanceToU256, BlockHashCount, BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, U256ToBalance, }; -use relay_common::apis::*; use runtime_parachains::{ assigner_coretime as parachains_assigner_coretime, configuration::{ @@ -124,7 +123,7 @@ use sp_runtime::{ generic, impl_opaque_keys, traits::{ AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Convert, ConvertInto, - Get, IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Saturating, Verify, + Get, IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, OpaqueValue, Perbill, Percent, Permill, @@ -2437,43 +2436,7 @@ mod benches { #[cfg(feature = "runtime-benchmarks")] use benches::*; -impl Runtime { - fn impl_experimental_inflation_info() -> InflationInfo { - use pallet_staking::{ActiveEra, EraPayout, ErasTotalStake}; - let (staked, _start) = ActiveEra::::get() - .map(|ae| (ErasTotalStake::::get(ae.index), ae.start.unwrap_or(0))) - .unwrap_or((0, 0)); - - let ideal_staking_rate = dynamic_params::inflation::IdealStake::get(); - let inflation = if dynamic_params::inflation::UseAuctionSlots::get() { - let auctioned_slots = parachains_paras::Parachains::::get() - .into_iter() - // All active para-ids that do not belong to a system chain is the number of - // parachains that we should take into account for inflation. - .filter(|i| *i >= LOWEST_PUBLIC_ID) - .count() as u64; - ideal_staking_rate - .saturating_sub(Perquintill::from_rational(auctioned_slots.min(60), 200u64)) - } else { - ideal_staking_rate - }; - - // We assume un-delayed 6h eras. - let era_duration = 6 * (HOURS as Moment) * MILLISECS_PER_BLOCK; - let next_mint = - ::EraPayout::era_payout(staked, 0, era_duration); - - InflationInfo { inflation, next_mint } - } -} - sp_api::impl_runtime_apis! { - impl relay_common::apis::Inflation for Runtime { - fn experimental_inflation_prediction_info() -> InflationInfo { - Runtime::impl_experimental_inflation_info() - } - } - impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION diff --git a/relay/polkadot/src/lib.rs b/relay/polkadot/src/lib.rs index c5fbf9cb8f..db0684aa33 100644 --- a/relay/polkadot/src/lib.rs +++ b/relay/polkadot/src/lib.rs @@ -94,7 +94,6 @@ use sp_runtime::traits::Convert; use pallet_staking_async_ah_client as ah_client; use pallet_staking_async_rc_client as rc_client; -use relay_common::apis::InflationInfo; use runtime_parachains::{ assigner_coretime as parachains_assigner_coretime, configuration as parachains_configuration, configuration::ActiveConfigHrmpChannelSizeAndCapacityRatio, @@ -2261,39 +2260,7 @@ mod benches { #[cfg(feature = "runtime-benchmarks")] use benches::*; -impl Runtime { - fn impl_experimental_inflation_info() -> InflationInfo { - use pallet_staking::{ActiveEra, EraPayout, ErasTotalStake}; - let (staked, _start) = ActiveEra::::get() - .map(|ae| (ErasTotalStake::::get(ae.index), ae.start.unwrap_or(0))) - .unwrap_or((0, 0)); - let stake_able_issuance = Balances::total_issuance(); - - // We assume un-delayed 24h eras. - let era_duration = 24 * (HOURS as Moment) * MILLISECS_PER_BLOCK; - let next_mint = ::EraPayout::era_payout( - staked, - stake_able_issuance, - era_duration, - ); - // reverse-engineer the current inflation by looking at the total minted against the total - // issuance. - let inflation = Perquintill::from_rational( - (next_mint.0 + next_mint.1) * 36525 / 100, - stake_able_issuance, - ); - - InflationInfo { inflation, next_mint } - } -} - sp_api::impl_runtime_apis! { - impl relay_common::apis::Inflation for Runtime { - fn experimental_inflation_prediction_info() -> InflationInfo { - Runtime::impl_experimental_inflation_info() - } - } - impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { VERSION @@ -3618,7 +3585,6 @@ mod remote_tests { log::info!(target: LOG_TARGET, "era-duration = {average_era_duration_millis:?}"); log::info!(target: LOG_TARGET, "maxStakingRewards = {:?}", pallet_staking::MaxStakedRewards::::get()); log::info!(target: LOG_TARGET, "💰 Inflation ==> staking = {:?} / leftover = {:?}", token.amount(staking), token.amount(leftover)); - log::info!(target: LOG_TARGET, "inflation_rate runtime API: {:?}", Runtime::impl_experimental_inflation_info()); }); } } diff --git a/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs b/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs index 297abacb23..b97cde1525 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs @@ -1440,15 +1440,17 @@ pub mod dynamic_params { pub mod staking_election { /// 10m worth of local 6s blocks for signed phase. #[codec(index = 0)] - pub static SignedPhase: BlockNumber = 10 * system_parachains_constants::MINUTES; + pub static SignedPhase: BlockNumber = + 10 * system_parachains_constants::async_backing::MINUTES; /// Allow up to 16 signed solutions to be submitted. #[codec(index = 1)] pub static MaxSignedSubmissions: u32 = 16; - /// 10m for unsigned phase... + /// 5m for unsigned phase... #[codec(index = 2)] - pub static UnsignedPhase: BlockNumber = 10 * system_parachains_constants::MINUTES; + pub static UnsignedPhase: BlockNumber = + 5 * system_parachains_constants::async_backing::MINUTES; /// .. in which we try and mine a 4-page solution. #[codec(index = 3)] @@ -2650,6 +2652,12 @@ pallet_revive::impl_runtime_apis_plus_revive!( } } + impl system_parachains_common::apis::Inflation for Runtime { + fn experimental_issuance_prediction_info() -> system_parachains_common::apis::InflationInfo { + crate::staking::EraPayout::impl_experimental_inflation_info() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/system-parachains/asset-hubs/asset-hub-kusama/src/staking/mod.rs b/system-parachains/asset-hubs/asset-hub-kusama/src/staking/mod.rs index 8e779968b0..06cd6b0528 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/src/staking/mod.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/src/staking/mod.rs @@ -30,6 +30,7 @@ use pallet_staking_async_rc_client as rc_client; use scale_info::TypeInfo; use sp_runtime::{transaction_validity::TransactionPriority, Perquintill}; use sp_staking::SessionIndex; +use system_parachains_common::apis::InflationInfo; use xcm::v5::prelude::*; // alias for the ones backed by parameters-pallet. @@ -332,6 +333,30 @@ impl pallet_staking_async::EraPayout for EraPayout { } } +impl EraPayout { + pub(crate) fn impl_experimental_inflation_info() -> InflationInfo { + use pallet_staking_async::{ActiveEra, ActiveEraInfo, ErasTotalStake}; + let staked = ActiveEra::::get() + .map(|ActiveEraInfo { index, .. }| ErasTotalStake::::get(index)) + .unwrap_or(0); + let ti = pallet_balances::Pallet::::total_issuance(); + + // We assume un-delayed 6h eras. + let era_duration = 6 * 60 * 60 * 1000; + let next_mint = >::era_payout( + staked, + ti, + era_duration, + ); + let total = next_mint.0 + next_mint.1; + const NUM_ERAS_PER_DAY: u128 = 4; + let annual_issuance = total * 36525 * NUM_ERAS_PER_DAY / 100; + let issuance = Perquintill::from_rational(annual_issuance, ti); + + InflationInfo { issuance, next_mint } + } +} + parameter_types! { pub const SessionsPerEra: SessionIndex = 6; /// Note: This is measured in RC block time. Our calculation of when to plan a new era might get @@ -557,6 +582,23 @@ mod tests { ); assert_eq!(staking, 844_606070970705); assert_eq!(treasury, 320_110565207524); + + pallet_balances::TotalIssuance::::put(17016510054564053390u128); + pallet_staking_async::ActiveEra::::put(pallet_staking_async::ActiveEraInfo { + index: 777, + start: None, + }); + pallet_staking_async::ErasTotalStake::::insert(777, 8085567183241128549u128); + let expected_issuance_parts = 99999999999999249; + assert_eq!( + super::EraPayout::impl_experimental_inflation_info(), + InflationInfo { + issuance: Perquintill::from_parts(99999999999999249), + next_mint: (staking, treasury), + } + ); + // around 9% now + assert_eq!(expected_issuance_parts * 100 / 10u64.pow(18), 9) }); } @@ -566,6 +608,7 @@ mod tests { // session `n` and the results to be ready before the end of that session. Atm RC and KAH // have the same block time, 6s. sp_io::TestExternalities::new_empty().execute_with(|| { + sp_tracing::try_init_simple(); let duration = <::ElectionProvider as ElectionProvider>::duration(); let session = RelaySessionDuration::get(); log::info!(target: "runtime::asset-hub-kusama", "election duration is {duration:?}, relay session {session:?}",); diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml index b5202badc4..159c2bfa4d 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml @@ -27,6 +27,7 @@ collectives-polkadot-runtime-constants = { workspace = true } kusama-runtime-constants = { workspace = true } polkadot-runtime-constants = { workspace = true } system-parachains-constants = { workspace = true } +system-parachains-common = { workspace = true } # Substrate frame-benchmarking = { optional = true, workspace = true } @@ -217,6 +218,7 @@ runtime-benchmarks = [ "snowbridge-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", + "system-parachains-common/runtime-benchmarks", "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -281,6 +283,7 @@ try-runtime = [ "snowbridge-pallet-system-frontend/try-runtime", "snowbridge-runtime-common/try-runtime", "sp-runtime/try-runtime", + "system-parachains-common/try-runtime", ] std = [ "pallet-ah-migrator/std", @@ -388,6 +391,7 @@ std = [ "sp-version/std", "sp-weights/std", "substrate-wasm-builder", + "system-parachains-common/std", "system-parachains-constants/std", "xcm-builder/std", "xcm-executor/std", diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs index 110c1b3ecb..d430cd6d09 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs @@ -100,7 +100,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Perbill, Permill, }; -use system_parachains_constants::MINUTES; +use system_parachains_constants::async_backing::MINUTES; use xcm::latest::prelude::*; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, @@ -1230,7 +1230,7 @@ pub mod dynamic_params { pub static MaxSignedSubmissions: u32 = 16; /// Unsigned phase duration for election-provider-multi-block. #[codec(index = 2)] - pub static UnsignedPhase: BlockNumber = 30 * MINUTES; + pub static UnsignedPhase: BlockNumber = 15 * MINUTES; /// Miner pages for unsigned phase. #[codec(index = 3)] pub static MinerPages: u32 = 4; @@ -2441,6 +2441,12 @@ impl_runtime_apis! { } } + impl system_parachains_common::apis::Inflation for Runtime { + fn experimental_issuance_prediction_info() -> system_parachains_common::apis::InflationInfo { + crate::staking::EraPayout::impl_experimental_inflation_info() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/src/staking/mod.rs b/system-parachains/asset-hubs/asset-hub-polkadot/src/staking/mod.rs index b46bfcdeb4..0ccba7ef6e 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/src/staking/mod.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/src/staking/mod.rs @@ -29,10 +29,11 @@ use pallet_staking_async::UseValidatorsMap; use pallet_staking_async_rc_client as rc_client; use sp_arithmetic::FixedU128; use sp_runtime::{ - traits::Convert, transaction_validity::TransactionPriority, FixedPointNumber, + traits::Convert, transaction_validity::TransactionPriority, FixedPointNumber, Perquintill, SaturatedConversion, }; use sp_staking::SessionIndex; +use system_parachains_common::apis::InflationInfo; use xcm::v5::prelude::*; // stuff aliased to `parameters` pallet. @@ -301,7 +302,7 @@ impl pallet_staking_async::EraPayout for EraPayout { FixedU128::from_rational(era_duration_millis.into(), MILLISECONDS_PER_YEAR.into()); // TI at the time of execution of [Referendum 1139](https://polkadot.subsquare.io/referenda/1139), block hash: `0x39422610299a75ef69860417f4d0e1d94e77699f45005645ffc5e8e619950f9f`. - let fixed_total_issuance: i128 = 5_216_342_402_773_185_773; + let fixed_total_issuance: i128 = 15_011_657_390_566_252_333; let fixed_inflation_rate = FixedU128::from_rational(8, 100); let yearly_emission = fixed_inflation_rate.saturating_mul_int(fixed_total_issuance); @@ -314,6 +315,23 @@ impl pallet_staking_async::EraPayout for EraPayout { } } +impl EraPayout { + pub(crate) fn impl_experimental_inflation_info() -> InflationInfo { + // We assume un-delayed 24h eras. + let era_duration = 24 * 60 * 60 * 1000; + let next_mint = + >::era_payout(0, 0, era_duration); + + // What is our effective issuance rate now? + let total = next_mint.0 + next_mint.1; + let annual_issuance = total * 36525 / 100; + let ti = pallet_balances::TotalIssuance::::get(); + let issuance = Perquintill::from_rational(annual_issuance, ti); + + InflationInfo { issuance, next_mint } + } +} + parameter_types! { pub const SessionsPerEra: SessionIndex = prod_or_fast!(6, 1); pub const RelaySessionDuration: BlockNumber = prod_or_fast!(4 * RC_HOURS, RC_MINUTES); @@ -511,6 +529,40 @@ mod tests { use sp_weights::constants::{WEIGHT_PROOF_SIZE_PER_KB, WEIGHT_REF_TIME_PER_MILLIS}; // TODO: in the future, make these tests use remote-ext and increase their longevity. + #[test] + fn inflation_sanity_check() { + use pallet_staking_async::EraPayout as _; + // values taken from the last Polkadot staking payout while it was in RC. + // https://polkadot.subscan.io/block/28481296 + // Payout: 279k DOT to validators / 49k DOT to treasury + // active era: 1980 + // Note: Amount don't exactly match due to timestamp being an estimate. Same ballpark is + // good. + sp_io::TestExternalities::new_empty().execute_with(|| { + let average_era_duration_millis = 24 * 60 * 60 * 1000; // 24h + let (staking, treasury) = super::EraPayout::era_payout( + 0, // not used + 0, // not used + average_era_duration_millis, + ); + assert_eq!(staking, 279477_8104198508); + assert_eq!(treasury, 49319_6136035030); + + // a recent TI of Polkadot + pallet_balances::TotalIssuance::::put(16_336_817_797_558_128_793); + let expected_issuance_parts = 73510802784664934; + assert_eq!( + super::EraPayout::impl_experimental_inflation_info(), + InflationInfo { + issuance: Perquintill::from_parts(expected_issuance_parts), + next_mint: (2794778104198508, 493196136035030) + } + ); + // around 7% for now. + assert_eq!(expected_issuance_parts * 100 / 10u64.pow(18), 7); + }); + } + fn analyze_weight( op_name: &str, op_weight: Weight, diff --git a/system-parachains/common/Cargo.toml b/system-parachains/common/Cargo.toml index 97ed4888f2..14ce4069e9 100644 --- a/system-parachains/common/Cargo.toml +++ b/system-parachains/common/Cargo.toml @@ -9,21 +9,30 @@ version.workspace = true [dependencies] log = { workspace = true } +codec = { features = ["derive", "max-encoded-len"], workspace = true } +scale-info = { features = ["derive"], workspace = true } sp-runtime = { workspace = true } sp-state-machine = { workspace = true } +sp-api = { workspace = true } frame-support = { workspace = true } cumulus-pallet-parachain-system = { workspace = true } cumulus-primitives-core = { workspace = true } +polkadot-primitives = { workspace = true } [features] default = ["std"] std = [ + "codec/std", + "scale-info/std", + "cumulus-pallet-parachain-system/std", "cumulus-primitives-core/std", "frame-support/std", "log/std", + "polkadot-primitives/std", + "sp-api/std", "sp-runtime/std", "sp-state-machine/std", ] @@ -32,6 +41,7 @@ runtime-benchmarks = [ "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-primitives-core/runtime-benchmarks", "frame-support/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] diff --git a/system-parachains/common/src/lib.rs b/system-parachains/common/src/lib.rs index 16f375840e..4ba771b718 100644 --- a/system-parachains/common/src/lib.rs +++ b/system-parachains/common/src/lib.rs @@ -18,3 +18,32 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod randomness; + +/// Extra runtime APIs. +pub mod apis { + /// Information about the current issuance rate of the system. + /// + /// Both fields should be treated as best-effort, given that the issuance rate might not be + /// fully predict-able. + #[derive(scale_info::TypeInfo, codec::Encode, codec::Decode, Eq, PartialEq)] + #[cfg_attr(feature = "std", derive(Debug))] + pub struct InflationInfo { + /// The rate of issuance estimated per annum, represented as a `Perquintill`. + pub issuance: sp_runtime::Perquintill, + /// Next amount that we anticipate to mint. + /// + /// First item is the amount that goes to stakers, second is the leftover that is usually + /// forwarded to the treasury. + pub next_mint: (polkadot_primitives::Balance, polkadot_primitives::Balance), + } + + sp_api::decl_runtime_apis! { + pub trait Inflation { + /// Return the current estimates of the issuance amount. + /// + /// This is marked as experimental in light of RFC#89. Nonetheless, its usage is highly + /// recommended over trying to read-storage, or re-create the onchain logic. + fn experimental_issuance_prediction_info() -> InflationInfo; + } + } +}