-
Notifications
You must be signed in to change notification settings - Fork 246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Storage Proofs: BlockDigest -> DomainRuntimeUpgrades & merge AllowList #3329
base: main
Are you sure you want to change the base?
Changes from all commits
9cf99e0
af18b6c
089bd23
27f68f3
0c39996
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,9 +64,10 @@ use sp_consensus_subspace::WrappedPotOutput; | |
use sp_core::H256; | ||
use sp_domains::bundle_producer_election::BundleProducerElectionParams; | ||
use sp_domains::{ | ||
DomainBundleLimit, DomainId, DomainInstanceData, ExecutionReceipt, OpaqueBundle, OperatorId, | ||
OperatorPublicKey, OperatorRewardSource, OperatorSignature, ProofOfElection, RuntimeId, | ||
SealedSingletonReceipt, DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT, EMPTY_EXTRINSIC_ROOT, | ||
DomainAllowlistUpdates, DomainBundleLimit, DomainId, DomainInstanceData, ExecutionReceipt, | ||
OpaqueBundle, OperatorId, OperatorPublicKey, OperatorRewardSource, OperatorSignature, | ||
ProofOfElection, RuntimeId, SealedSingletonReceipt, DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT, | ||
EMPTY_EXTRINSIC_ROOT, | ||
}; | ||
use sp_domains_fraud_proof::fraud_proof::{ | ||
DomainRuntimeCodeAt, FraudProof, FraudProofVariant, InvalidBlockFeesProof, | ||
|
@@ -208,9 +209,10 @@ mod pallet { | |
#[cfg(not(feature = "runtime-benchmarks"))] | ||
use crate::MAX_NOMINATORS_TO_SLASH; | ||
use crate::{ | ||
BalanceOf, BlockSlot, BlockTreeNodeFor, DomainBlockNumberFor, ElectionVerificationParams, | ||
ExecutionReceiptOf, FraudProofFor, HoldIdentifier, NominatorId, OpaqueBundleOf, | ||
ReceiptHashFor, SingletonReceiptOf, StateRootOf, MAX_BUNDLE_PER_BLOCK, STORAGE_VERSION, | ||
BalanceOf, BlockSlot, BlockTreeNodeFor, DomainAllowlistUpdatesProvider, | ||
DomainBlockNumberFor, ElectionVerificationParams, ExecutionReceiptOf, FraudProofFor, | ||
HoldIdentifier, NominatorId, OpaqueBundleOf, ReceiptHashFor, SingletonReceiptOf, | ||
StateRootOf, MAX_BUNDLE_PER_BLOCK, STORAGE_VERSION, | ||
}; | ||
#[cfg(not(feature = "std"))] | ||
use alloc::string::String; | ||
|
@@ -238,8 +240,8 @@ mod pallet { | |
use sp_domains_fraud_proof::storage_proof::{self, FraudProofStorageKeyProvider}; | ||
use sp_domains_fraud_proof::{InvalidTransactionCode, StatelessDomainRuntimeCall}; | ||
use sp_runtime::traits::{ | ||
AtLeast32BitUnsigned, BlockNumberProvider, CheckEqual, CheckedAdd, Header as HeaderT, | ||
MaybeDisplay, One, SimpleBitOps, Zero, | ||
AtLeast32BitUnsigned, BlockNumberProvider, CheckEqual, CheckedAdd, CheckedSub, | ||
Header as HeaderT, MaybeDisplay, One, SimpleBitOps, Zero, | ||
}; | ||
use sp_runtime::Saturating; | ||
use sp_std::boxed::Box; | ||
|
@@ -415,6 +417,9 @@ mod pallet { | |
/// A hook to call after a domain is instantiated | ||
type OnDomainInstantiated: OnDomainInstantiated; | ||
|
||
/// Domain allow list update source | ||
type DomainAllowlistUpdates: DomainAllowlistUpdatesProvider; | ||
|
||
/// Hash type of MMR | ||
type MmrHash: Parameter + Member + Default + Clone; | ||
|
||
|
@@ -699,18 +704,18 @@ mod pallet { | |
#[pallet::storage] | ||
pub(super) type AccumulatedTreasuryFunds<T> = StorageValue<_, BalanceOf<T>, ValueQuery>; | ||
|
||
/// Storage used to keep track of which consensus block the domain runtime upgrade happen. | ||
/// Storage used to keep track of which consensus block each domain runtime upgrade happens in. | ||
#[pallet::storage] | ||
pub(super) type DomainRuntimeUpgradeRecords<T: Config> = StorageMap< | ||
pub type DomainRuntimeUpgradeRecords<T: Config> = StorageMap< | ||
_, | ||
Identity, | ||
RuntimeId, | ||
BTreeMap<BlockNumberFor<T>, DomainRuntimeUpgradeEntry<T::Hash>>, | ||
ValueQuery, | ||
>; | ||
|
||
/// Temporary storage keep track of domain runtime upgrade happen in the current block, cleared | ||
/// in the next block initialization. | ||
/// Temporary storage to keep track of domain runtime upgrades which happened in the parent | ||
/// block. Cleared in the current block's initialization. | ||
#[pallet::storage] | ||
pub type DomainRuntimeUpgrades<T> = StorageValue<_, Vec<RuntimeId>, ValueQuery>; | ||
|
||
|
@@ -1856,44 +1861,51 @@ mod pallet { | |
|
||
/// Combined fraud proof data for the InvalidInherentExtrinsic fraud proof | ||
#[pallet::storage] | ||
pub type BlockInvalidInherentExtrinsicData<T> = StorageValue<_, InvalidInherentExtrinsicData>; | ||
pub type BlockInvalidInherentExtrinsicData<T> = | ||
StorageMap<_, Identity, DomainId, InvalidInherentExtrinsicData, ValueQuery>; | ||
|
||
#[pallet::hooks] | ||
// TODO: proper benchmark | ||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { | ||
fn on_initialize(block_number: BlockNumberFor<T>) -> Weight { | ||
let parent_number = block_number - One::one(); | ||
let parent_hash = frame_system::Pallet::<T>::block_hash(parent_number); | ||
|
||
// Record any previous domain runtime upgrade in `DomainRuntimeUpgradeRecords` and then do the | ||
// domain runtime upgrade scheduled in the current block | ||
for runtime_id in DomainRuntimeUpgrades::<T>::take() { | ||
let reference_count = RuntimeRegistry::<T>::get(runtime_id) | ||
.expect("Runtime object must be present since domain is insantiated; qed") | ||
.instance_count; | ||
if !reference_count.is_zero() { | ||
DomainRuntimeUpgradeRecords::<T>::mutate(runtime_id, |upgrade_record| { | ||
upgrade_record.insert( | ||
parent_number, | ||
DomainRuntimeUpgradeEntry { | ||
at_hash: parent_hash, | ||
reference_count, | ||
}, | ||
) | ||
// At genesis, avoid block number underflow, and use the default zero genesis parent hash. | ||
let parent_number = block_number.checked_sub(&One::one()); | ||
let parent_hash = parent_number | ||
.map(|parent_number| frame_system::Pallet::<T>::block_hash(parent_number)) | ||
.unwrap_or_default(); | ||
|
||
// By definition, the genesis block doesn't have any previous domain runtime upgrades. | ||
if let Some(parent_number) = parent_number { | ||
// Record any previous domain runtime upgrade in `DomainRuntimeUpgradeRecords` and then do the | ||
// domain runtime upgrade scheduled in the current block. | ||
for runtime_id in DomainRuntimeUpgrades::<T>::take() { | ||
let reference_count = RuntimeRegistry::<T>::get(runtime_id) | ||
.expect("Runtime object must be present since domain is insantiated; qed") | ||
.instance_count; | ||
if !reference_count.is_zero() { | ||
DomainRuntimeUpgradeRecords::<T>::mutate(runtime_id, |upgrade_record| { | ||
upgrade_record.insert( | ||
parent_number, | ||
DomainRuntimeUpgradeEntry { | ||
at_hash: parent_hash, | ||
reference_count, | ||
}, | ||
) | ||
}); | ||
} | ||
} | ||
do_upgrade_runtimes::<T>(block_number); | ||
|
||
// Store the hash of the parent consensus block for domains that have bundles submitted | ||
// in that consensus block | ||
for (domain_id, _) in SuccessfulBundles::<T>::drain() { | ||
ConsensusBlockHash::<T>::insert(domain_id, parent_number, parent_hash); | ||
T::DomainBundleSubmitted::domain_bundle_submitted(domain_id); | ||
DomainSudoCalls::<T>::mutate(domain_id, |sudo_call| { | ||
sudo_call.clear(); | ||
}); | ||
} | ||
} | ||
do_upgrade_runtimes::<T>(block_number); | ||
|
||
// Store the hash of the parent consensus block for domain that have bundles submitted | ||
// in that consensus block | ||
for (domain_id, _) in SuccessfulBundles::<T>::drain() { | ||
ConsensusBlockHash::<T>::insert(domain_id, parent_number, parent_hash); | ||
T::DomainBundleSubmitted::domain_bundle_submitted(domain_id); | ||
DomainSudoCalls::<T>::mutate(domain_id, |sudo_call| { | ||
sudo_call.clear(); | ||
}); | ||
} | ||
|
||
for (operator_id, slot_set) in OperatorBundleSlot::<T>::drain() { | ||
// NOTE: `OperatorBundleSlot` use `BTreeSet` so `last` will return the maximum value in the set | ||
|
@@ -1902,15 +1914,27 @@ mod pallet { | |
} | ||
} | ||
|
||
BlockInvalidInherentExtrinsicData::<T>::kill(); | ||
let _ = BlockInvalidInherentExtrinsicData::<T>::clear(u32::MAX, None); | ||
|
||
Weight::zero() | ||
} | ||
|
||
fn on_finalize(_: BlockNumberFor<T>) { | ||
fn on_finalize(block_number: BlockNumberFor<T>) { | ||
let parent_number = block_number.checked_sub(&One::one()); | ||
let domain_runtime_upgrade_ids = if let Some(parent_number) = parent_number { | ||
DomainRuntimeUpgradeRecords::<T>::iter() | ||
.flat_map(|(runtime_id, upgrade_map)| { | ||
upgrade_map.get(&parent_number).map(|_entry| runtime_id) | ||
}) | ||
.collect::<Vec<RuntimeId>>() | ||
} else { | ||
// By definition, the genesis block doesn't have any parent block or any runtime upgrades | ||
Vec::new() | ||
}; | ||
|
||
// If this consensus block will derive any domain block, gather the necessary storage for potential fraud proof usage | ||
if SuccessfulBundles::<T>::iter_keys().count() > 0 | ||
|| DomainRuntimeUpgrades::<T>::exists() | ||
|| !domain_runtime_upgrade_ids.is_empty() | ||
{ | ||
let extrinsics_shuffling_seed = Randomness::from( | ||
Into::<H256>::into(Self::extrinsics_shuffling_seed()).to_fixed_bytes(), | ||
|
@@ -1931,13 +1955,54 @@ mod pallet { | |
let consensus_transaction_byte_fee = | ||
sp_domains::DOMAIN_STORAGE_FEE_MULTIPLIER * transaction_byte_fee; | ||
|
||
let invalid_inherent_extrinsic_data = InvalidInherentExtrinsicData { | ||
extrinsics_shuffling_seed, | ||
timestamp, | ||
consensus_transaction_byte_fee, | ||
}; | ||
// Record a storage proof for each domain with a successful bundle | ||
for domain_id in SuccessfulBundles::<T>::iter_keys() { | ||
let domain_chain_allowlist = | ||
T::DomainAllowlistUpdates::domain_allowlist_updates(domain_id) | ||
.unwrap_or_default(); | ||
Comment on lines
+1960
to
+1962
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it because of the allowlist update we need to store the While most of the data are the same for all the domain I think it makes more sense to store them once and shared by all the domains, for the allowlist update we can store |
||
|
||
let invalid_inherent_extrinsic_data = InvalidInherentExtrinsicData { | ||
extrinsics_shuffling_seed, | ||
timestamp, | ||
consensus_transaction_byte_fee, | ||
domain_chain_allowlist, | ||
}; | ||
|
||
BlockInvalidInherentExtrinsicData::<T>::insert( | ||
domain_id, | ||
invalid_inherent_extrinsic_data, | ||
); | ||
} | ||
|
||
BlockInvalidInherentExtrinsicData::<T>::set(Some(invalid_inherent_extrinsic_data)); | ||
// Record a storage proof for each domain with a runtime upgrade | ||
for runtime_id in domain_runtime_upgrade_ids { | ||
let Some(domain_id) = Self::domain_id(runtime_id) else { | ||
// If the genesis block isn't present for the runtime_id, there haven't | ||
// been any upgrades for it | ||
continue; | ||
}; | ||
|
||
if BlockInvalidInherentExtrinsicData::<T>::contains_key(domain_id) { | ||
// Skip domains that already have a record due to a successful bundle | ||
continue; | ||
} | ||
|
||
let domain_chain_allowlist = | ||
T::DomainAllowlistUpdates::domain_allowlist_updates(domain_id) | ||
.unwrap_or_default(); | ||
|
||
let invalid_inherent_extrinsic_data = InvalidInherentExtrinsicData { | ||
extrinsics_shuffling_seed, | ||
timestamp, | ||
consensus_transaction_byte_fee, | ||
domain_chain_allowlist, | ||
}; | ||
|
||
BlockInvalidInherentExtrinsicData::<T>::insert( | ||
domain_id, | ||
invalid_inherent_extrinsic_data, | ||
); | ||
} | ||
} | ||
|
||
let _ = LastEpochStakingDistribution::<T>::clear(u32::MAX, None); | ||
|
@@ -2101,6 +2166,15 @@ impl<T: Config> Pallet<T> { | |
.map(|domain_object| domain_object.domain_config.runtime_id) | ||
} | ||
|
||
pub fn domain_id(runtime_id: RuntimeId) -> Option<DomainId> { | ||
RuntimeRegistry::<T>::get(runtime_id).and_then(|runtime_object| { | ||
runtime_object | ||
.raw_genesis | ||
.domain_id() | ||
.expect("DomainId must be valid since domain is insantiated; qed") | ||
}) | ||
} | ||
|
||
pub fn domain_instance_data( | ||
domain_id: DomainId, | ||
) -> Option<(DomainInstanceData, BlockNumberFor<T>)> { | ||
|
@@ -2910,7 +2984,7 @@ impl<T: Config> Pallet<T> { | |
} | ||
|
||
// Get the domain runtime code that used to derive `receipt`, if the runtime code still present in | ||
// the state then get it from the state otherwise from the `maybe_domain_runtime_code_at` prood. | ||
// the state then get it from the state otherwise from the `maybe_domain_runtime_code_at` proof. | ||
pub fn get_domain_runtime_code_for_receipt( | ||
domain_id: DomainId, | ||
receipt: &ExecutionReceiptOf<T>, | ||
|
@@ -3120,3 +3194,8 @@ pub fn calculate_tx_range( | |
}; | ||
new_tx_range.clamp(lower_bound, upper_bound) | ||
} | ||
|
||
/// An abstraction that gets domain allow list updates from pallet-messenger. | ||
pub trait DomainAllowlistUpdatesProvider { | ||
fn domain_allowlist_updates(domain_id: DomainId) -> Option<DomainAllowlistUpdates>; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC there should be no
on_initialize/on_finalize
for the genesis block, the genesis block is built from the chain spec withgenesis_build
, also see https://github.com/paritytech/polkadot-sdk/blob/ca7817922148c1e6f6856138998f7135f42f3f4f/substrate/frame/system/src/lib.rs#L1812.cc @vedhavyas @nazar-pc in case I'm wrong.