Skip to content

Commit

Permalink
Add fraud proof for contract allow list extrinsic, break host fns
Browse files Browse the repository at this point in the history
  • Loading branch information
teor2345 committed Feb 12, 2025
1 parent 1c8c3c5 commit 97cebce
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 11 deletions.
4 changes: 4 additions & 0 deletions crates/sp-domains-fraud-proof/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,10 @@ pub struct InvalidExtrinsicsRootProof {

/// Optional sudo extrinsic call storage proof
pub domain_sudo_call_proof: DomainSudoCallStorageProof,

/// Optional EVM domain "set contract creation allowed by" extrinsic call storage proof
pub evm_domain_contract_creation_allowed_by_call_proof:
EvmDomainContractCreationAllowedByCallStorageProof,
}

#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
Expand Down
13 changes: 13 additions & 0 deletions crates/sp-domains-fraud-proof/src/host_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ where
consensus_transaction_byte_fee,
domain_chain_allowlist,
maybe_sudo_runtime_call,
maybe_evm_domain_contract_creation_allowed_by_call,
} = domain_inherent_extrinsic_data;

let domain_stateless_runtime = StatelessRuntime::<Block, DomainBlock, _>::new(
Expand Down Expand Up @@ -315,12 +316,24 @@ where
),
};

let maybe_evm_domain_contract_creation_allowed_by_call_extrinsic =
match maybe_evm_domain_contract_creation_allowed_by_call {
None => None,
Some(call) => Some(
domain_stateless_runtime
.construct_evm_contract_creation_allowed_by_extrinsic(call)
.ok()
.map(|call| call.encode())?,
),
};

Some(DomainInherentExtrinsic {
domain_timestamp_extrinsic,
maybe_domain_chain_allowlist_extrinsic,
consensus_chain_byte_fee_extrinsic,
maybe_domain_set_code_extrinsic,
maybe_domain_sudo_call_extrinsic,
maybe_evm_domain_contract_creation_allowed_by_call_extrinsic,
})
}

Expand Down
2 changes: 2 additions & 0 deletions crates/sp-domains-fraud-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ pub struct DomainInherentExtrinsicData {
pub consensus_transaction_byte_fee: Balance,
pub domain_chain_allowlist: DomainAllowlistUpdates,
pub maybe_sudo_runtime_call: Option<Vec<u8>>,
pub maybe_evm_domain_contract_creation_allowed_by_call: Option<Vec<u8>>,
}

impl PassBy for DomainInherentExtrinsicData {
Expand All @@ -115,6 +116,7 @@ pub struct DomainInherentExtrinsic {
consensus_chain_byte_fee_extrinsic: Vec<u8>,
maybe_domain_set_code_extrinsic: Option<Vec<u8>>,
maybe_domain_sudo_call_extrinsic: Option<Vec<u8>>,
maybe_evm_domain_contract_creation_allowed_by_call_extrinsic: Option<Vec<u8>>,
}

#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
Expand Down
22 changes: 21 additions & 1 deletion crates/sp-domains-fraud-proof/src/storage_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use sp_domains::proof_provider_and_verifier::{
StorageProofVerifier, VerificationError as StorageProofVerificationError,
};
use sp_domains::{
DomainAllowlistUpdates, DomainId, DomainSudoCall, OpaqueBundle, RuntimeId, RuntimeObject,
DomainAllowlistUpdates, DomainId, DomainSudoCall, EvmDomainContractCreationAllowedByCall,
OpaqueBundle, RuntimeId, RuntimeObject,
};
use sp_runtime::traits::{Block as BlockT, HashingFor, Header as HeaderT, NumberFor};
use sp_runtime_interface::pass_by;
Expand Down Expand Up @@ -45,6 +46,7 @@ pub enum VerificationError {
TransfersStorageProof(StorageProofVerificationError),
ExtrinsicStorageProof(StorageProofVerificationError),
DomainSudoCallStorageProof(StorageProofVerificationError),
EvmDomainContractCreationAllowedByCallStorageProof(StorageProofVerificationError),
MmrRootStorageProof(StorageProofVerificationError),
}

Expand All @@ -56,6 +58,7 @@ pub enum FraudProofStorageKeyRequest<Number> {
DomainRuntimeUpgrades,
RuntimeRegistry(RuntimeId),
DomainSudoCall(DomainId),
EvmDomainContractCreationAllowedByCall(DomainId),
MmrRoot(Number),
}

Expand All @@ -76,6 +79,9 @@ impl<Number> FraudProofStorageKeyRequest<Number> {
FraudProofStorageKeyRequest::DomainSudoCall(_) => {
VerificationError::DomainSudoCallStorageProof(err)
}
FraudProofStorageKeyRequest::EvmDomainContractCreationAllowedByCall(_) => {
VerificationError::EvmDomainContractCreationAllowedByCallStorageProof(err)
}
Self::MmrRoot(_) => VerificationError::MmrRootStorageProof(err),
}
}
Expand Down Expand Up @@ -191,6 +197,20 @@ impl<Block: BlockT> BasicStorageProof<Block> for DomainSudoCallStorageProof {
}
}

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

impl_storage_proof!(EvmDomainContractCreationAllowedByCallStorageProof);
impl<Block: BlockT> BasicStorageProof<Block>
for EvmDomainContractCreationAllowedByCallStorageProof
{
type StorageValue = EvmDomainContractCreationAllowedByCall;
type Key = DomainId;
fn storage_key_request(key: Self::Key) -> FraudProofStorageKeyRequest<NumberFor<Block>> {
FraudProofStorageKeyRequest::EvmDomainContractCreationAllowedByCall(key)
}
}

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

Expand Down
36 changes: 30 additions & 6 deletions crates/sp-domains-fraud-proof/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ where
maybe_domain_runtime_upgraded_proof,
domain_chain_allowlist_proof,
domain_sudo_call_proof,
evm_domain_contract_creation_allowed_by_call_proof,
} = fraud_proof;

let invalid_inherent_extrinsic_data =
Expand All @@ -93,6 +94,15 @@ where
&state_root,
)?;

let evm_domain_contract_creation_allowed_by_call =
<EvmDomainContractCreationAllowedByCallStorageProof as BasicStorageProof<CBlock>>::verify::<
SKP,
>(
evm_domain_contract_creation_allowed_by_call_proof.clone(),
domain_id,
&state_root,
)?;

let shuffling_seed = invalid_inherent_extrinsic_data.extrinsics_shuffling_seed;

let domain_inherent_extrinsic_data = DomainInherentExtrinsicData {
Expand All @@ -102,6 +112,8 @@ where
.consensus_transaction_byte_fee,
domain_chain_allowlist,
maybe_sudo_runtime_call: domain_sudo_call.maybe_call,
maybe_evm_domain_contract_creation_allowed_by_call:
evm_domain_contract_creation_allowed_by_call.maybe_call,
};

let DomainInherentExtrinsic {
Expand All @@ -110,6 +122,7 @@ where
consensus_chain_byte_fee_extrinsic,
maybe_domain_set_code_extrinsic,
maybe_domain_sudo_call_extrinsic,
maybe_evm_domain_contract_creation_allowed_by_call_extrinsic,
} = fraud_proof_runtime_interface::construct_domain_inherent_extrinsic(
domain_runtime_code,
domain_inherent_extrinsic_data,
Expand Down Expand Up @@ -138,18 +151,29 @@ where
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.
// currently this is the following order
// NOTE: the order of the inherent extrinsics MUST be the same as the pallet order defined in
// the `construct_runtime` macro for domains.
// Currently this is the following order:
// - timestamp extrinsic
// - executive set_code extrinsic
// - messenger update_domain_allowlist extrinsic
// - block_fees transaction_byte_fee_extrinsic
// - domain_sudo extrinsic
// since we use `push_front` the extrinsic should be pushed in reversed order
// - evm_tracker contract_creation_allowed_by extrinsic
// Since we use `push_front`, the extrinsics should be pushed in reverse order.
// TODO: this will not be valid once we have a different runtime. To achive consistency across
// domains, we should define a runtime api for each domain that should order the extrinsics
// like inherent are derived while domain block is being built
// domains, we should define a runtime api for each domain, which orders the extrinsics the
// same way the inherents are derived while the domain block is being built.

if let Some(evm_domain_contract_creation_allowed_by_call_extrinsic) =
maybe_evm_domain_contract_creation_allowed_by_call_extrinsic
{
let evm_domain_contract_creation_allowed_by_call_extrinsic =
ExtrinsicDigest::new::<LayoutV1<HeaderHashingFor<DomainHeader>>>(
evm_domain_contract_creation_allowed_by_call_extrinsic,
);
ordered_extrinsics.push_front(evm_domain_contract_creation_allowed_by_call_extrinsic);
}

if let Some(domain_sudo_call_extrinsic) = maybe_domain_sudo_call_extrinsic {
let domain_sudo_call_extrinsic = ExtrinsicDigest::new::<
Expand Down
5 changes: 5 additions & 0 deletions crates/subspace-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,11 @@ impl FraudProofStorageKeyProvider<NumberFor<Block>> for StorageKeyProvider {
FraudProofStorageKeyRequest::DomainSudoCall(domain_id) => {
pallet_domains::DomainSudoCalls::<Runtime>::hashed_key_for(domain_id)
}
FraudProofStorageKeyRequest::EvmDomainContractCreationAllowedByCall(domain_id) => {
pallet_domains::EvmDomainContractCreationAllowedByCalls::<Runtime>::hashed_key_for(
domain_id,
)
}
FraudProofStorageKeyRequest::MmrRoot(block_number) => {
pallet_subspace_mmr::MmrRootHashes::<Runtime>::hashed_key_for(block_number)
}
Expand Down
11 changes: 11 additions & 0 deletions domains/client/block-preprocessor/src/stateless_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,4 +407,15 @@ where
inner_call,
)
}

pub fn construct_evm_contract_creation_allowed_by_extrinsic(
&self,
inner_call: Vec<u8>,
) -> Result<Block::Extrinsic, ApiError> {
<Self as EvmTrackerApi<Block>>::construct_evm_contract_creation_allowed_by_extrinsic(
self,
Default::default(),
inner_call,
)
}
}
9 changes: 9 additions & 0 deletions domains/client/domain-operator/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,14 @@ where
&self.storage_key_provider,
)?;

let evm_domain_contract_creation_allowed_by_call_proof =
EvmDomainContractCreationAllowedByCallStorageProof::generate(
self.consensus_client.as_ref(),
consensus_block_hash,
domain_id,
&self.storage_key_provider,
)?;

let invalid_domain_extrinsics_root_proof = FraudProof {
domain_id,
bad_receipt_hash,
Expand All @@ -422,6 +430,7 @@ where
maybe_domain_runtime_upgraded_proof,
domain_chain_allowlist_proof,
domain_sudo_call_proof,
evm_domain_contract_creation_allowed_by_call_proof,
}),
};

Expand Down
3 changes: 3 additions & 0 deletions domains/primitives/evm-tracker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,8 @@ sp_api::decl_runtime_apis! {
pub trait EvmTrackerApi {
/// Returns true if evm-tracker exists in the runtime, and extrinsic is valid.
fn is_valid_evm_contract_creation_allowed_by_call(extrinsic: Vec<u8>) -> bool;

/// Returns an encoded extrinsic for domain "set contract creation allowed by" call.
fn construct_evm_contract_creation_allowed_by_extrinsic(extrinsic: Vec<u8>) -> Block::Extrinsic;
}
}
26 changes: 24 additions & 2 deletions domains/runtime/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,7 @@ fn is_valid_sudo_call(encoded_ext: Vec<u8>) -> bool {
UncheckedExtrinsic::decode(&mut encoded_ext.as_slice()).is_ok()
}

/// Constructs a domain-sudo call extrinsic from the given encoded extrinsic.
fn construct_sudo_call_extrinsic(encoded_ext: Vec<u8>) -> <Block as BlockT>::Extrinsic {
let ext = UncheckedExtrinsic::decode(&mut encoded_ext.as_slice())
.expect("must always be an valid extrinsic due to the check above; qed");
Expand All @@ -929,18 +930,35 @@ fn construct_sudo_call_extrinsic(encoded_ext: Vec<u8>) -> <Block as BlockT>::Ext
/// Returns `true` if this is a valid pallet-evm-tracker "contract creation allowed by" inherent
/// call.
fn is_valid_evm_contract_creation_allowed_by_call(encoded_ext: Vec<u8>) -> bool {
let Ok(call) = UncheckedExtrinsic::decode(&mut encoded_ext.as_slice()) else {
let Ok(ext) = UncheckedExtrinsic::decode(&mut encoded_ext.as_slice()) else {
return false;
};

matches!(
call.0.function,
ext.0.function,
RuntimeCall::EVMNoncetracker(
pallet_evm_tracker::Call::inherent_set_contract_creation_allowed_by { .. }
)
)
}

/// Constructs an evm-tracker call extrinsic from the given encoded extrinsic.
fn construct_evm_contract_creation_allowed_by_extrinsic(
encoded_ext: Vec<u8>,
) -> <Block as BlockT>::Extrinsic {
let ext = UncheckedExtrinsic::decode(&mut encoded_ext.as_slice())
.expect("must always be an valid evm-tracker extrinsic due to the check above; qed");

match ext.0.function {
RuntimeCall::EVMNoncetracker(
inner_call @ pallet_evm_tracker::Call::inherent_set_contract_creation_allowed_by {
..
},
) => UncheckedExtrinsic::new_unsigned(inner_call.into()),
_ => panic!("must always be an valid evm-tracker extrinsic due to the check above; qed"),
}
}

fn extract_signer_inner<Lookup>(
ext: &UncheckedExtrinsic,
lookup: &Lookup,
Expand Down Expand Up @@ -1661,6 +1679,10 @@ impl_runtime_apis! {
fn is_valid_evm_contract_creation_allowed_by_call(extrinsic: Vec<u8>) -> bool {
is_valid_evm_contract_creation_allowed_by_call(extrinsic)
}

fn construct_evm_contract_creation_allowed_by_extrinsic(extrinsic: Vec<u8>) -> <Block as BlockT>::Extrinsic {
construct_evm_contract_creation_allowed_by_extrinsic(extrinsic)
}
}

impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
Expand Down
26 changes: 24 additions & 2 deletions domains/test/runtime/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,7 @@ fn is_valid_sudo_call(encoded_ext: Vec<u8>) -> bool {
UncheckedExtrinsic::decode(&mut encoded_ext.as_slice()).is_ok()
}

/// Constructs a domain-sudo call extrinsic from the given encoded extrinsic.
fn construct_sudo_call_extrinsic(encoded_ext: Vec<u8>) -> <Block as BlockT>::Extrinsic {
let ext = UncheckedExtrinsic::decode(&mut encoded_ext.as_slice())
.expect("must always be an valid extrinsic due to the check above; qed");
Expand All @@ -968,18 +969,35 @@ fn construct_sudo_call_extrinsic(encoded_ext: Vec<u8>) -> <Block as BlockT>::Ext
/// Returns `true` if this is a valid pallet-evm-tracker "contract creation allowed by" inherent
/// call.
fn is_valid_evm_contract_creation_allowed_by_call(encoded_ext: Vec<u8>) -> bool {
let Ok(call) = UncheckedExtrinsic::decode(&mut encoded_ext.as_slice()) else {
let Ok(ext) = UncheckedExtrinsic::decode(&mut encoded_ext.as_slice()) else {
return false;
};

matches!(
call.0.function,
ext.0.function,
RuntimeCall::EVMNoncetracker(
pallet_evm_tracker::Call::inherent_set_contract_creation_allowed_by { .. }
)
)
}

/// Constructs an evm-tracker call extrinsic from the given encoded extrinsic.
fn construct_evm_contract_creation_allowed_by_extrinsic(
encoded_ext: Vec<u8>,
) -> <Block as BlockT>::Extrinsic {
let ext = UncheckedExtrinsic::decode(&mut encoded_ext.as_slice())
.expect("must always be an valid evm-tracker extrinsic due to the check above; qed");

match ext.0.function {
RuntimeCall::EVMNoncetracker(
inner_call @ pallet_evm_tracker::Call::inherent_set_contract_creation_allowed_by {
..
},
) => UncheckedExtrinsic::new_unsigned(inner_call.into()),
_ => panic!("must always be an valid evm-tracker extrinsic due to the check above; qed"),
}
}

fn extract_signer_inner<Lookup>(
ext: &UncheckedExtrinsic,
lookup: &Lookup,
Expand Down Expand Up @@ -1700,6 +1718,10 @@ impl_runtime_apis! {
fn is_valid_evm_contract_creation_allowed_by_call(extrinsic: Vec<u8>) -> bool {
is_valid_evm_contract_creation_allowed_by_call(extrinsic)
}

fn construct_evm_contract_creation_allowed_by_extrinsic(extrinsic: Vec<u8>) -> <Block as BlockT>::Extrinsic {
construct_evm_contract_creation_allowed_by_extrinsic(extrinsic)
}
}

impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
Expand Down
5 changes: 5 additions & 0 deletions test/subspace-test-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,11 @@ impl FraudProofStorageKeyProvider<NumberFor<Block>> for StorageKeyProvider {
FraudProofStorageKeyRequest::DomainSudoCall(domain_id) => {
pallet_domains::DomainSudoCalls::<Runtime>::hashed_key_for(domain_id)
}
FraudProofStorageKeyRequest::EvmDomainContractCreationAllowedByCall(domain_id) => {
pallet_domains::EvmDomainContractCreationAllowedByCalls::<Runtime>::hashed_key_for(
domain_id,
)
}
FraudProofStorageKeyRequest::MmrRoot(block_number) => {
pallet_subspace_mmr::MmrRootHashes::<Runtime>::hashed_key_for(block_number)
}
Expand Down

0 comments on commit 97cebce

Please sign in to comment.