Skip to content
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

Create invalid extrinsics fraud proof storage, and move block randomness into it #3314

Merged
merged 4 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions crates/pallet-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ mod pallet {
#[cfg(not(feature = "runtime-benchmarks"))]
use crate::staking_epoch::do_slash_operator;
use crate::staking_epoch::{do_finalize_domain_current_epoch, Error as StakingEpochError};
use crate::storage_proof::InvalidInherentExtrinsicData;
use crate::weights::WeightInfo;
#[cfg(not(feature = "runtime-benchmarks"))]
use crate::DomainHashingFor;
Expand Down Expand Up @@ -246,7 +247,7 @@ mod pallet {
use sp_std::collections::btree_set::BTreeSet;
use sp_std::fmt::Debug;
use sp_subspace_mmr::MmrProofVerifier;
use subspace_core_primitives::U256;
use subspace_core_primitives::{Randomness, U256};
use subspace_runtime_primitives::StorageFee;

#[pallet::config]
Expand Down Expand Up @@ -1850,6 +1851,10 @@ mod pallet {
}
}

/// Combined fraud proof data for the InvalidInherentExtrinsic fraud proof
#[pallet::storage]
pub type BlockInvalidInherentExtrinsicData<T> = StorageValue<_, InvalidInherentExtrinsicData>;

#[pallet::hooks]
// TODO: proper benchmark
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
Expand Down Expand Up @@ -1894,10 +1899,27 @@ mod pallet {
}
}

BlockInvalidInherentExtrinsicData::<T>::kill();

Weight::zero()
}

fn on_finalize(_: BlockNumberFor<T>) {
// 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()
{
let extrinsics_shuffling_seed = Randomness::from(
Into::<H256>::into(Self::extrinsics_shuffling_seed()).to_fixed_bytes(),
);

let invalid_inherent_extrinsic_data = InvalidInherentExtrinsicData {
extrinsics_shuffling_seed,
};

BlockInvalidInherentExtrinsicData::<T>::set(Some(invalid_inherent_extrinsic_data));
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
}

let _ = LastEpochStakingDistribution::<T>::clear(u32::MAX, None);
let _ = NewAddedHeadReceipt::<T>::clear(u32::MAX, None);
}
Expand Down Expand Up @@ -2717,8 +2739,8 @@ impl<T: Config> Pallet<T> {
}

pub fn extrinsics_shuffling_seed() -> T::Hash {
let seed = DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT;
let (randomness, _) = T::Randomness::random(seed);
let subject = DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT;
let (randomness, _) = T::Randomness::random(subject);
randomness
}

Expand Down Expand Up @@ -2889,9 +2911,9 @@ impl<T: Config> Pallet<T> {
parent_receipt.consensus_block_number
};

let is_domain_runtime_updraded = current_runtime_obj.updated_at >= at;
let is_domain_runtime_upgraded = current_runtime_obj.updated_at >= at;

let mut runtime_obj = match (is_domain_runtime_updraded, maybe_domain_runtime_code_at) {
let mut runtime_obj = match (is_domain_runtime_upgraded, maybe_domain_runtime_code_at) {
// The domain runtime is upgraded since `at`, the domain runtime code in `at` is not available
// so `domain_runtime_code_proof` must be provided
(true, None) => return Err(FraudProofError::DomainRuntimeCodeProofNotFound),
Expand Down
6 changes: 5 additions & 1 deletion crates/sp-domains-fraud-proof/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,11 @@ pub struct InvalidExtrinsicsRootProof {
/// Valid Bundle digests
pub valid_bundle_digests: Vec<ValidBundleDigest>,

/// The storage proof used during verification
/// The combined storage proofs used during verification
pub invalid_inherent_extrinsic_proofs: InvalidInherentExtrinsicDataProof,

/// The individual storage proofs used during verification
// TODO: combine these proofs into `InvalidInherentExtrinsicDataProof`
pub invalid_inherent_extrinsic_proof: InvalidInherentExtrinsicProof,

/// Optional sudo extrinsic call storage proof
Expand Down
2 changes: 1 addition & 1 deletion crates/sp-domains-fraud-proof/src/host_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ where
) -> Option<DomainInherentExtrinsic> {
let DomainInherentExtrinsicData {
// Used by caller
block_randomness: _,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field was introduced recently and is not exist in the consensus runtime in mainnet, so we should also remove it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically in #3312, where it wasn’t caught in review. Is there an automated test we could add to catch accidental changes like this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is hard to test compatibility with an existing network in unit/integration test, usually we will test runtime upgrade before release (by simulating the existing network in a local network).

extrinsics_shuffling_seed: _,
timestamp,
maybe_domain_runtime_upgrade,
consensus_transaction_byte_fee,
Expand Down
2 changes: 1 addition & 1 deletion crates/sp-domains-fraud-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ pub enum DomainChainAllowlistUpdateExtrinsic {

#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub struct DomainInherentExtrinsicData {
pub block_randomness: Randomness,
pub extrinsics_shuffling_seed: Randomness,
pub timestamp: Moment,
pub maybe_domain_runtime_upgrade: Option<Vec<u8>>,
pub consensus_transaction_byte_fee: Balance,
Expand Down
71 changes: 39 additions & 32 deletions crates/sp-domains-fraud-proof/src/storage_proof.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::DomainInherentExtrinsicData;
use codec::{Decode, Encode};
use frame_support::PalletError;
use scale_info::TypeInfo;
Expand All @@ -13,6 +12,8 @@ use sp_domains::{
};
use sp_runtime::generic::Digest;
use sp_runtime::traits::{Block as BlockT, HashingFor, Header as HeaderT, NumberFor};
use sp_runtime_interface::pass_by;
use sp_runtime_interface::pass_by::PassBy;
use sp_std::marker::PhantomData;
use sp_std::vec::Vec;
use sp_trie::StorageProof;
Expand All @@ -36,7 +37,7 @@ pub enum VerificationError {
InvalidBundleStorageProof,
RuntimeCodeNotFound,
UnexpectedDomainRuntimeUpgrade,
BlockRandomnessStorageProof(StorageProofVerificationError),
InvalidInherentExtrinsicStorageProof(StorageProofVerificationError),
TimestampStorageProof(StorageProofVerificationError),
SuccessfulBundlesStorageProof(StorageProofVerificationError),
TransactionByteFeeStorageProof(StorageProofVerificationError),
Expand All @@ -54,7 +55,7 @@ pub enum VerificationError {

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub enum FraudProofStorageKeyRequest<Number> {
BlockRandomness,
InvalidInherentExtrinsicData,
Timestamp,
Comment on lines 56 to +58
Copy link
Contributor Author

@teor2345 teor2345 Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hang on, is this a breaking runtime API change?

This enum is passed to a runtime API here:

sp_api::decl_runtime_apis! {
/// API necessary for fraud proof.
pub trait FraudProofApi<DomainHeader: HeaderT> {
/// Submit the fraud proof via an unsigned extrinsic.
fn submit_fraud_proof_unsigned(fraud_proof: FraudProof<NumberFor<Block>, Block::Hash, DomainHeader, H256>);
/// Reture the storage key used in fraud proof
fn fraud_proof_storage_key(req: FraudProofStorageKeyRequest<NumberFor<Block>>) -> Vec<u8>;
}
}

If it is, I think we'll need to bump the runtime version in this PR as well.

Edit: Or since it already got bumped in #3303, maybe that's ok.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fine as runtime API only fail when we actually call it and it has never been called yet as there is no domain initialized (while the host function change will crash the node upon runtime/client upgrade).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, in this case it would produce wrong results (because enum variants have changed indexes) or fail (because the expected data does not accompany the variant at that index). So that’s fine as long as most nodes are upgraded before then.

We might want to bump the runtime version for Taurus though.

SuccessfulBundles(DomainId),
TransactionByteFee,
Expand All @@ -69,7 +70,9 @@ pub enum FraudProofStorageKeyRequest<Number> {
impl<Number> FraudProofStorageKeyRequest<Number> {
fn into_error(self, err: StorageProofVerificationError) -> VerificationError {
match self {
Self::BlockRandomness => VerificationError::BlockRandomnessStorageProof(err),
Self::InvalidInherentExtrinsicData => {
VerificationError::InvalidInherentExtrinsicStorageProof(err)
}
Self::Timestamp => VerificationError::TimestampStorageProof(err),
Self::SuccessfulBundles(_) => VerificationError::SuccessfulBundlesStorageProof(err),
Self::TransactionByteFee => VerificationError::TransactionByteFeeStorageProof(err),
Expand Down Expand Up @@ -173,17 +176,6 @@ impl<Block: BlockT> BasicStorageProof<Block> for SuccessfulBundlesProof {
}
}

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct BlockRandomnessProof(StorageProof);

impl_storage_proof!(BlockRandomnessProof);
impl<Block: BlockT> BasicStorageProof<Block> for BlockRandomnessProof {
type StorageValue = Randomness;
fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
FraudProofStorageKeyRequest::BlockRandomness
}
}

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct DomainChainsAllowlistUpdateStorageProof(StorageProof);

Expand Down Expand Up @@ -414,10 +406,28 @@ impl MaybeDomainRuntimeUpgradedProof {
}

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct InvalidInherentExtrinsicProof {
/// Block randomness storage proof
pub block_randomness_proof: BlockRandomnessProof,
pub struct InvalidInherentExtrinsicData {
/// Extrinsics shuffling seed, derived from block randomness
pub extrinsics_shuffling_seed: Randomness,
}

impl PassBy for InvalidInherentExtrinsicData {
type PassBy = pass_by::Codec<Self>;
}

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct InvalidInherentExtrinsicDataProof(StorageProof);

impl_storage_proof!(InvalidInherentExtrinsicDataProof);
impl<Block: BlockT> BasicStorageProof<Block> for InvalidInherentExtrinsicDataProof {
type StorageValue = InvalidInherentExtrinsicData;
fn storage_key_request(_key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
FraudProofStorageKeyRequest::InvalidInherentExtrinsicData
}
}

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct InvalidInherentExtrinsicProof {
/// Block timestamp storage proof
pub timestamp_proof: TimestampStorageProof,

Expand All @@ -434,6 +444,15 @@ pub struct InvalidInherentExtrinsicProof {
pub domain_chain_allowlist_proof: DomainChainsAllowlistUpdateStorageProof,
}

/// The verified data from an `InvalidInherentExtrinsicProof`
#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct InvalidInherentExtrinsicVerified {
pub timestamp: Moment,
pub maybe_domain_runtime_upgrade: Option<Vec<u8>>,
pub consensus_transaction_byte_fee: Balance,
pub domain_chain_allowlist: DomainAllowlistUpdates,
}

impl InvalidInherentExtrinsicProof {
#[cfg(feature = "std")]
#[allow(clippy::let_and_return)]
Expand All @@ -448,8 +467,6 @@ impl InvalidInherentExtrinsicProof {
block_hash: Block::Hash,
maybe_runtime_id: Option<RuntimeId>,
) -> Result<Self, GenerationError> {
let block_randomness_proof =
BlockRandomnessProof::generate(proof_provider, block_hash, (), storage_key_provider)?;
let timestamp_proof =
TimestampStorageProof::generate(proof_provider, block_hash, (), storage_key_provider)?;
let maybe_domain_runtime_upgrade_proof = MaybeDomainRuntimeUpgradedProof::generate(
Expand Down Expand Up @@ -478,7 +495,6 @@ impl InvalidInherentExtrinsicProof {
)?;

Ok(Self {
block_randomness_proof,
timestamp_proof,
maybe_domain_runtime_upgrade_proof,
dynamic_cost_of_storage_proof,
Expand All @@ -492,13 +508,7 @@ impl InvalidInherentExtrinsicProof {
domain_id: DomainId,
runtime_id: RuntimeId,
state_root: &Block::Hash,
) -> Result<DomainInherentExtrinsicData, VerificationError> {
let block_randomness = <BlockRandomnessProof as BasicStorageProof<Block>>::verify::<SKP>(
self.block_randomness_proof.clone(),
(),
state_root,
)?;

) -> Result<InvalidInherentExtrinsicVerified, VerificationError> {
let timestamp = <TimestampStorageProof as BasicStorageProof<Block>>::verify::<SKP>(
self.timestamp_proof.clone(),
(),
Expand Down Expand Up @@ -535,14 +545,11 @@ impl InvalidInherentExtrinsicProof {
state_root,
)?;

Ok(DomainInherentExtrinsicData {
block_randomness,
Ok(InvalidInherentExtrinsicVerified {
timestamp,
maybe_domain_runtime_upgrade,
consensus_transaction_byte_fee,
domain_chain_allowlist,
// Populated by caller
maybe_sudo_runtime_call: None,
})
}
}
Expand Down
44 changes: 29 additions & 15 deletions crates/sp-domains-fraud-proof/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use crate::fraud_proof::{
};
use crate::storage_proof::{self, *};
use crate::{
fraud_proof_runtime_interface, DomainInherentExtrinsic, DomainStorageKeyRequest,
StatelessDomainRuntimeCall,
fraud_proof_runtime_interface, DomainInherentExtrinsic, DomainInherentExtrinsicData,
DomainStorageKeyRequest, StatelessDomainRuntimeCall,
};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
Expand All @@ -17,7 +17,7 @@ use domain_runtime_primitives::BlockNumber;
use hash_db::Hasher;
use sp_core::storage::StorageKey;
use sp_core::H256;
use sp_domains::extrinsics::{deduplicate_and_shuffle_extrinsics, extrinsics_shuffling_seed};
use sp_domains::extrinsics::deduplicate_and_shuffle_extrinsics;
use sp_domains::proof_provider_and_verifier::StorageProofVerifier;
use sp_domains::valued_trie::valued_ordered_trie_root;
use sp_domains::{
Expand All @@ -32,7 +32,7 @@ use sp_runtime::traits::{
use sp_runtime::{OpaqueExtrinsic, SaturatedConversion};
use sp_subspace_mmr::{ConsensusChainMmrLeafProof, MmrProofVerifier};
use sp_trie::{LayoutV1, StorageProof};
use subspace_core_primitives::{Randomness, U256};
use subspace_core_primitives::U256;
use trie_db::node::Value;

/// Verifies invalid domain extrinsic root fraud proof.
Expand Down Expand Up @@ -65,24 +65,40 @@ where
{
let InvalidExtrinsicsRootProof {
valid_bundle_digests,
invalid_inherent_extrinsic_proofs,
invalid_inherent_extrinsic_proof,
domain_sudo_call_proof,
} = fraud_proof;

let mut domain_inherent_extrinsic_data = invalid_inherent_extrinsic_proof
.verify::<CBlock, SKP>(domain_id, runtime_id, &state_root)?;
let invalid_inherent_extrinsic_data =
<InvalidInherentExtrinsicDataProof as BasicStorageProof<CBlock>>::verify::<SKP>(
invalid_inherent_extrinsic_proofs.clone(),
(),
&state_root,
)?;

let inherent_extrinsic_verified = invalid_inherent_extrinsic_proof.verify::<CBlock, SKP>(
domain_id,
runtime_id,
&state_root,
)?;

let domain_sudo_call = <DomainSudoCallStorageProof as BasicStorageProof<CBlock>>::verify::<SKP>(
domain_sudo_call_proof.clone(),
domain_id,
&state_root,
)?;
domain_inherent_extrinsic_data.maybe_sudo_runtime_call = domain_sudo_call.maybe_call;

let shuffling_seed = H256::from_slice(
extrinsics_shuffling_seed::<Hashing>(domain_inherent_extrinsic_data.block_randomness)
.as_ref(),
);
let shuffling_seed = invalid_inherent_extrinsic_data.extrinsics_shuffling_seed;

let domain_inherent_extrinsic_data = DomainInherentExtrinsicData {
extrinsics_shuffling_seed: invalid_inherent_extrinsic_data.extrinsics_shuffling_seed,
timestamp: inherent_extrinsic_verified.timestamp,
maybe_domain_runtime_upgrade: inherent_extrinsic_verified.maybe_domain_runtime_upgrade,
consensus_transaction_byte_fee: inherent_extrinsic_verified.consensus_transaction_byte_fee,
domain_chain_allowlist: inherent_extrinsic_verified.domain_chain_allowlist,
maybe_sudo_runtime_call: domain_sudo_call.maybe_call,
};

let DomainInherentExtrinsic {
domain_timestamp_extrinsic,
Expand Down Expand Up @@ -115,10 +131,8 @@ where
bundle_extrinsics_digests.extend(bundle_digest.bundle_digest.clone());
}

let mut ordered_extrinsics = deduplicate_and_shuffle_extrinsics(
bundle_extrinsics_digests,
Randomness::from(shuffling_seed.to_fixed_bytes()),
);
let mut ordered_extrinsics =
deduplicate_and_shuffle_extrinsics(bundle_extrinsics_digests, shuffling_seed);

// NOTE: the order of the inherent extrinsic MUST aligned with the
// pallets order defined in `construct_runtime` macro for domains.
Expand Down
4 changes: 2 additions & 2 deletions crates/subspace-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1038,8 +1038,8 @@ pub struct StorageKeyProvider;
impl FraudProofStorageKeyProvider<NumberFor<Block>> for StorageKeyProvider {
fn storage_key(req: FraudProofStorageKeyRequest<NumberFor<Block>>) -> Vec<u8> {
match req {
FraudProofStorageKeyRequest::BlockRandomness => {
pallet_subspace::BlockRandomness::<Runtime>::hashed_key().to_vec()
FraudProofStorageKeyRequest::InvalidInherentExtrinsicData => {
pallet_domains::BlockInvalidInherentExtrinsicData::<Runtime>::hashed_key().to_vec()
}
FraudProofStorageKeyRequest::Timestamp => {
pallet_timestamp::Now::<Runtime>::hashed_key().to_vec()
Expand Down
8 changes: 8 additions & 0 deletions domains/client/domain-operator/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,13 @@ where
let maybe_runtime_id =
self.is_domain_runtime_upgraded_at(domain_id, consensus_block_hash)?;

let invalid_inherent_extrinsic_proofs = InvalidInherentExtrinsicDataProof::generate(
self.consensus_client.as_ref(),
consensus_block_hash,
(),
&self.storage_key_provider,
)?;

let invalid_inherent_extrinsic_proof = InvalidInherentExtrinsicProof::generate(
&self.storage_key_provider,
self.consensus_client.as_ref(),
Expand All @@ -404,6 +411,7 @@ where
maybe_domain_runtime_code_proof,
proof: FraudProofVariant::InvalidExtrinsicsRoot(InvalidExtrinsicsRootProof {
valid_bundle_digests,
invalid_inherent_extrinsic_proofs,
invalid_inherent_extrinsic_proof,
domain_sudo_call_proof,
}),
Expand Down
Loading
Loading