Skip to content

Commit

Permalink
Merge pull request #2136 from subspace/revamp-invalid-bundles-fraud-p…
Browse files Browse the repository at this point in the history
…roof

OutOfRangeTx fraud proof - Part 1
  • Loading branch information
ParthDesai authored Oct 25, 2023
2 parents 83e2f72 + d3b74a1 commit a761302
Show file tree
Hide file tree
Showing 8 changed files with 340 additions and 59 deletions.
17 changes: 16 additions & 1 deletion crates/pallet-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use sp_domains_fraud_proof::fraud_proof::{
FraudProof, InvalidDomainBlockHashProof, InvalidTotalRewardsProof,
};
use sp_domains_fraud_proof::verification::{
verify_invalid_domain_block_hash_fraud_proof,
verify_invalid_bundles_fraud_proof, verify_invalid_domain_block_hash_fraud_proof,
verify_invalid_domain_extrinsics_root_fraud_proof, verify_invalid_state_transition_fraud_proof,
verify_invalid_total_rewards_fraud_proof, verify_valid_bundle_fraud_proof,
};
Expand Down Expand Up @@ -614,6 +614,8 @@ mod pallet {
InvalidStateTransitionFraudProof,
/// Parent receipt not found.
ParentReceiptNotFound,
/// Invalid bundles fraud proof
InvalidBundleFraudProof,
/// Bad/Invalid valid bundle fraud proof
BadValidBundleFraudProof,
}
Expand Down Expand Up @@ -1608,6 +1610,19 @@ impl<T: Config> Pallet<T> {
FraudProofError::InvalidStateTransitionFraudProof
})?;
}
FraudProof::InvalidBundles(invalid_bundles_fraud_proof) => {
verify_invalid_bundles_fraud_proof::<T::Block, T::DomainHeader, BalanceOf<T>>(
bad_receipt,
invalid_bundles_fraud_proof,
)
.map_err(|err| {
log::error!(
target: "runtime::domains",
"Invalid Bundle proof verification failed: {err:?}"
);
FraudProofError::InvalidBundleFraudProof
})?;
}
FraudProof::ValidBundle(proof) => verify_valid_bundle_fraud_proof::<
T::Block,
DomainBlockNumberFor<T>,
Expand Down
5 changes: 5 additions & 0 deletions crates/pallet-domains/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ pub(crate) struct MockDomainFraudProofExtension {
block_randomness: Randomness,
timestamp: Moment,
runtime_code: Vec<u8>,
tx_range: bool,
}

impl FraudProofHostFunctions for MockDomainFraudProofExtension {
Expand Down Expand Up @@ -297,6 +298,9 @@ impl FraudProofHostFunctions for MockDomainFraudProofExtension {
),
)
}
FraudProofVerificationInfoRequest::TxRangeCheck { .. } => {
FraudProofVerificationInfoResponse::TxRangeCheck(self.tx_range)
}
};

Some(response)
Expand Down Expand Up @@ -963,6 +967,7 @@ fn test_invalid_domain_extrinsic_root_proof() {
block_randomness: Randomness::from([1u8; 32]),
timestamp: 1000,
runtime_code: vec![1, 2, 3, 4],
tx_range: true,
}));
ext.register_extension(fraud_proof_ext);

Expand Down
4 changes: 2 additions & 2 deletions crates/sp-domains-fraud-proof/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ std = [
"frame-support/std",
"hash-db/std",
"scale-info/std",
"sc-client-api",
"sc-executor/std",
"domain-block-preprocessor",
"sc-client-api",
"sc-executor",
"sc-executor/std",
"sp-api/std",
"sp-blockchain",
"sp-consensus-slots/std",
Expand Down
101 changes: 61 additions & 40 deletions crates/sp-domains-fraud-proof/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use sp_consensus_slots::Slot;
use sp_core::H256;
use sp_domain_digests::AsPredigest;
use sp_domains::proof_provider_and_verifier::StorageProofVerifier;
use sp_domains::{DomainId, ExecutionReceipt, HeaderHashFor, HeaderHashingFor, SealedBundleHeader};
use sp_domains::{
BundleValidity, DomainId, ExecutionReceipt, HeaderHashFor, HeaderHashingFor, InvalidBundleType,
SealedBundleHeader,
};
use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, NumberFor};
use sp_runtime::{Digest, DigestItem};
use sp_std::vec::Vec;
Expand Down Expand Up @@ -284,6 +287,34 @@ pub enum VerificationError {
error("Failed to derive domain set code extrinsic")
)]
FailedToDeriveDomainSetCodeExtrinsic,
/// Bundle with requested index not found in execution receipt
#[cfg_attr(
feature = "thiserror",
error("Bundle with requested index not found in execution receipt")
)]
BundleNotFound,
/// Invalid bundle entry in bad receipt was expected to be valid but instead found invalid entry
#[cfg_attr(
feature = "thiserror",
error("Unexpected bundle entry at {bundle_index} in bad receipt found: {targeted_entry_bundle:?} with fraud proof's type of proof: {fraud_proof_invalid_type_of_proof:?}")
)]
UnexpectedTargetedBundleEntry {
bundle_index: u32,
fraud_proof_invalid_type_of_proof: InvalidBundleType,
targeted_entry_bundle: BundleValidity<H256>,
},
/// Tx range host function did not return response (returned None)
#[cfg_attr(
feature = "thiserror",
error("Tx range host function did not return a response (returned None)")
)]
FailedToGetResponseFromTxRangeHostFn,
/// Unable to receive tx range from host function
#[cfg_attr(
feature = "thiserror",
error("Received invalid information from tx range host function")
)]
ReceivedInvalidInfoFromTxRangeHostFn,
/// Failed to get the bundle body
#[cfg_attr(feature = "thiserror", error("Failed to get the bundle body"))]
FailedToGetDomainBundleBody,
Expand All @@ -298,50 +329,41 @@ pub enum VerificationError {
TargetValidBundleNotFound,
}

// TODO: Define rest of the fraud proof fields
/// Proof data specific to each *expected* invalid bundle type
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub struct MissingInvalidBundleEntryFraudProof {
domain_id: DomainId,
bundle_index: u32,
}

impl MissingInvalidBundleEntryFraudProof {
pub fn new(domain_id: DomainId, bundle_index: u32) -> Self {
Self {
domain_id,
bundle_index,
}
}
pub enum ProofDataPerInvalidBundleType {
OutOfRangeTx,
}

// TODO: Define rest of the fraud proof fields
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub struct ValidAsInvalidBundleEntryFraudProof {
domain_id: DomainId,
bundle_index: u32,
pub struct InvalidBundlesFraudProof<ReceiptHash> {
pub bad_receipt_hash: ReceiptHash,
pub domain_id: DomainId,
pub bundle_index: u32,
pub mismatched_extrinsic_index: u32,
pub extrinsic_inclusion_proof: Vec<Vec<u8>>,
pub is_true_invalid_fraud_proof: bool,
pub proof_data: ProofDataPerInvalidBundleType,
}

impl ValidAsInvalidBundleEntryFraudProof {
pub fn new(domain_id: DomainId, bundle_index: u32) -> Self {
impl<ReceiptHash> InvalidBundlesFraudProof<ReceiptHash> {
pub fn new(
bad_receipt_hash: ReceiptHash,
domain_id: DomainId,
bundle_index: u32,
mismatched_extrinsic_index: u32,
extrinsic_inclusion_proof: Vec<Vec<u8>>,
is_true_invalid_fraud_proof: bool,
proof_data: ProofDataPerInvalidBundleType,
) -> Self {
Self {
bad_receipt_hash,
domain_id,
bundle_index,
}
}
}

/// Fraud proof indicating that `invalid_bundles` field of the receipt is incorrect
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub enum InvalidBundlesFraudProof {
MissingInvalidBundleEntry(MissingInvalidBundleEntryFraudProof),
ValidAsInvalid(ValidAsInvalidBundleEntryFraudProof),
}

impl InvalidBundlesFraudProof {
pub fn domain_id(&self) -> DomainId {
match self {
InvalidBundlesFraudProof::MissingInvalidBundleEntry(proof) => proof.domain_id,
InvalidBundlesFraudProof::ValidAsInvalid(proof) => proof.domain_id,
mismatched_extrinsic_index,
extrinsic_inclusion_proof,
is_true_invalid_fraud_proof,
proof_data,
}
}
}
Expand All @@ -367,7 +389,7 @@ pub enum FraudProof<Number, Hash, DomainHeader: HeaderT> {
/// Hash of the bad receipt this fraud proof targeted
bad_receipt_hash: HeaderHashFor<DomainHeader>,
},
InvalidBundles(InvalidBundlesFraudProof),
InvalidBundles(InvalidBundlesFraudProof<HeaderHashFor<DomainHeader>>),
}

impl<Number, Hash, DomainHeader: HeaderT> FraudProof<Number, Hash, DomainHeader> {
Expand All @@ -381,7 +403,7 @@ impl<Number, Hash, DomainHeader: HeaderT> FraudProof<Number, Hash, DomainHeader>
Self::Dummy { domain_id, .. } => *domain_id,
Self::InvalidTotalRewards(proof) => proof.domain_id(),
Self::InvalidExtrinsicsRoot(proof) => proof.domain_id,
Self::InvalidBundles(proof) => proof.domain_id(),
Self::InvalidBundles(proof) => proof.domain_id,
Self::ValidBundle(proof) => proof.domain_id,
Self::InvalidDomainBlockHash(proof) => proof.domain_id,
}
Expand All @@ -403,8 +425,7 @@ impl<Number, Hash, DomainHeader: HeaderT> FraudProof<Number, Hash, DomainHeader>
Self::InvalidExtrinsicsRoot(proof) => proof.bad_receipt_hash,
Self::InvalidTotalRewards(proof) => proof.bad_receipt_hash(),
Self::ValidBundle(proof) => proof.bad_receipt_hash,
// TODO: Remove default value when invalid bundle proofs are fully expanded
Self::InvalidBundles(_) => Default::default(),
Self::InvalidBundles(proof) => proof.bad_receipt_hash,
Self::InvalidDomainBlockHash(proof) => proof.bad_receipt_hash,
}
}
Expand Down
63 changes: 62 additions & 1 deletion crates/sp-domains-fraud-proof/src/host_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use sp_trie::StorageProof;
use std::borrow::Cow;
use std::marker::PhantomData;
use std::sync::Arc;
use subspace_core_primitives::Randomness;
use subspace_core_primitives::{Randomness, U256};

struct DomainRuntimeCodeFetcher(Vec<u8>);

Expand Down Expand Up @@ -91,6 +91,7 @@ impl<Block, Client, DomainBlock, Executor>
}
}

// TODO: Revisit the host function implementation once we decide best strategy to structure them.
impl<Block, Client, DomainBlock, Executor>
FraudProofHostFunctionsImpl<Block, Client, DomainBlock, Executor>
where
Expand Down Expand Up @@ -206,6 +207,52 @@ where
.ok()
.flatten()
}

fn is_tx_in_range(
&self,
consensus_block_hash: H256,
domain_id: DomainId,
opaque_extrinsic: OpaqueExtrinsic,
bundle_index: u32,
) -> Option<bool> {
let runtime_api = self.consensus_client.runtime_api();
let runtime_code = self.get_domain_runtime_code(consensus_block_hash, domain_id)?;
let consensus_block_hash = consensus_block_hash.into();
let domain_tx_range = runtime_api
.domain_tx_range(consensus_block_hash, domain_id)
.ok()?;

let consensus_extrinsics = self
.consensus_client
.block_body(consensus_block_hash)
.ok()??;
let bundles = self
.consensus_client
.runtime_api()
.extract_successful_bundles(consensus_block_hash, domain_id, consensus_extrinsics)
.ok()?;

let bundle = bundles.get(bundle_index as usize)?;
let bundle_vrf_hash =
U256::from_be_bytes(bundle.sealed_header.header.proof_of_election.vrf_hash());

let domain_runtime_api_light =
RuntimeApiLight::new(self.executor.clone(), runtime_code.into());

let encoded_extrinsic = opaque_extrinsic.encode();
let extrinsic =
<DomainBlock as BlockT>::Extrinsic::decode(&mut encoded_extrinsic.as_slice()).ok()?;

<RuntimeApiLight<Executor> as domain_runtime_primitives::DomainCoreApi<
DomainBlock,
>>::is_within_tx_range(
&domain_runtime_api_light,
Default::default(), // Doesn't matter for RuntimeApiLight
&extrinsic,
&bundle_vrf_hash,
&domain_tx_range,
).ok()
}
}

impl<Block, Client, DomainBlock, Executor> FraudProofHostFunctions
Expand Down Expand Up @@ -257,6 +304,20 @@ where
maybe_domain_set_code_extrinsic,
)
}),
FraudProofVerificationInfoRequest::TxRangeCheck {
domain_id,
opaque_extrinsic,
bundle_index,
} => self
.is_tx_in_range(
consensus_block_hash,
domain_id,
opaque_extrinsic,
bundle_index,
)
.map(|is_tx_in_range| {
FraudProofVerificationInfoResponse::TxRangeCheck(is_tx_in_range)
}),
}
}

Expand Down
19 changes: 19 additions & 0 deletions crates/sp-domains-fraud-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ pub enum FraudProofVerificationInfoRequest {
DomainRuntimeCode(DomainId),
/// Domain set_code extrinsic if there is a runtime upgrade at a given consensus block hash.
DomainSetCodeExtrinsic(DomainId),
/// Request to check if particular extrinsic is in range for (domain, bundle) pair at given domain block
TxRangeCheck {
domain_id: DomainId,
/// Index of the bundle in which the extrinsic exists
bundle_index: u32,
/// Extrinsic for which we need to check the range
opaque_extrinsic: OpaqueExtrinsic,
},
}

impl PassBy for FraudProofVerificationInfoRequest {
Expand Down Expand Up @@ -112,6 +120,8 @@ pub enum FraudProofVerificationInfoResponse {
DomainRuntimeCode(Vec<u8>),
/// Encoded domain set_code extrinsic if there is a runtime upgrade at given consensus block hash.
DomainSetCodeExtrinsic(SetCodeExtrinsic),
/// if particular extrinsic is in range for (domain, bundle) pair at given domain block
TxRangeCheck(bool),
}

impl FraudProofVerificationInfoResponse {
Expand Down Expand Up @@ -145,6 +155,15 @@ impl FraudProofVerificationInfoResponse {
}
}

pub fn into_tx_range_check(self) -> Option<bool> {
match self {
FraudProofVerificationInfoResponse::TxRangeCheck(is_tx_in_range) => {
Some(is_tx_in_range)
}
_ => None,
}
}

pub fn into_bundle_body(self) -> Option<Vec<OpaqueExtrinsic>> {
match self {
Self::DomainBundleBody(bb) => Some(bb),
Expand Down
Loading

0 comments on commit a761302

Please sign in to comment.