Skip to content

Commit

Permalink
Merge pull request #1983 from subspace/valid-bundle-proof
Browse files Browse the repository at this point in the history
Introduce fraud proof for `ER::valid_bundles`
  • Loading branch information
NingLin-P authored Oct 23, 2023
2 parents 225dbd6 + 6ec6f04 commit 1756a8e
Show file tree
Hide file tree
Showing 15 changed files with 438 additions and 50 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion crates/pallet-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ use sp_domains_fraud_proof::fraud_proof::{
use sp_domains_fraud_proof::verification::{
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_invalid_total_rewards_fraud_proof, verify_valid_bundle_fraud_proof,
};
use sp_runtime::traits::{BlakeTwo256, CheckedSub, Hash, Header, One, Zero};
use sp_runtime::{RuntimeAppPublic, SaturatedConversion, Saturating};
Expand Down Expand Up @@ -614,6 +614,8 @@ mod pallet {
InvalidStateTransitionFraudProof,
/// Parent receipt not found.
ParentReceiptNotFound,
/// Bad/Invalid valid bundle fraud proof
BadValidBundleFraudProof,
}

impl<T> From<FraudProofError> for Error<T> {
Expand Down Expand Up @@ -1608,6 +1610,19 @@ impl<T: Config> Pallet<T> {
FraudProofError::InvalidStateTransitionFraudProof
})?;
}
FraudProof::ValidBundle(proof) => verify_valid_bundle_fraud_proof::<
T::Block,
DomainBlockNumberFor<T>,
T::DomainHash,
BalanceOf<T>,
>(bad_receipt, proof)
.map_err(|err| {
log::error!(
target: "runtime::domains",
"Valid bundle proof verification failed: {err:?}"
);
FraudProofError::BadValidBundleFraudProof
})?,
_ => {}
}

Expand Down
12 changes: 12 additions & 0 deletions crates/pallet-domains/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,9 @@ impl FraudProofHostFunctions for MockDomainFraudProofExtension {
.encode(),
)
}
FraudProofVerificationInfoRequest::DomainBundleBody { .. } => {
FraudProofVerificationInfoResponse::DomainBundleBody(Default::default())
}
FraudProofVerificationInfoRequest::DomainRuntimeCode(_) => {
FraudProofVerificationInfoResponse::DomainRuntimeCode(Default::default())
}
Expand All @@ -298,6 +301,15 @@ impl FraudProofHostFunctions for MockDomainFraudProofExtension {
Some(response)
}

fn derive_bundle_digest(
&self,
_consensus_block_hash: H256,
_domain_id: DomainId,
_bundle_body: Vec<OpaqueExtrinsic>,
) -> Option<H256> {
Some(H256::random())
}

fn execution_proof_check(
&self,
_pre_state_root: H256,
Expand Down
3 changes: 3 additions & 0 deletions crates/sp-domains-fraud-proof/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ domain-runtime-primitives = { version = "0.1.0", default-features = false, path
frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "892bf8e938c6bd2b893d3827d1093cd81baa59a1" }
hash-db = { version = "0.16.0", default-features = false }
scale-info = { version = "2.7.0", default-features = false, features = ["derive"] }
sc-client-api = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "892bf8e938c6bd2b893d3827d1093cd81baa59a1", optional = true }
domain-block-preprocessor = { version = "0.1.0", default-features = false, path = "../../domains/client/block-preprocessor", optional = true }
sc-executor = { version = "0.10.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "892bf8e938c6bd2b893d3827d1093cd81baa59a1", default-features = false, optional = true }
sp-api = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "892bf8e938c6bd2b893d3827d1093cd81baa59a1" }
Expand Down Expand Up @@ -43,6 +44,8 @@ std = [
"frame-support/std",
"hash-db/std",
"scale-info/std",
"sc-client-api",
"sc-executor/std",
"domain-block-preprocessor",
"sc-executor",
"sp-api/std",
Expand Down
42 changes: 34 additions & 8 deletions crates/sp-domains-fraud-proof/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,18 @@ pub enum VerificationError {
error("Failed to derive domain set code extrinsic")
)]
FailedToDeriveDomainSetCodeExtrinsic,
/// Failed to get the bundle body
#[cfg_attr(feature = "thiserror", error("Failed to get the bundle body"))]
FailedToGetDomainBundleBody,
/// Failed to derive bundle digest
#[cfg_attr(feature = "thiserror", error("Failed to derive bundle digest"))]
FailedToDeriveBundleDigest,
/// The target valid bundle not found from the target bad receipt
#[cfg_attr(
feature = "thiserror",
error("The target valid bundle not found from the target bad receipt")
)]
TargetValidBundleNotFound,
}

// TODO: Define rest of the fraud proof fields
Expand Down Expand Up @@ -349,6 +361,7 @@ pub enum FraudProof<Number, Hash> {
ImproperTransactionSortition(ImproperTransactionSortitionProof),
InvalidTotalRewards(InvalidTotalRewardsProof),
InvalidExtrinsicsRoot(InvalidExtrinsicsRootProof),
ValidBundle(ValidBundleProof),
InvalidDomainBlockHash(InvalidDomainBlockHashProof),
// Dummy fraud proof only used in test and benchmark
#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
Expand All @@ -370,10 +383,11 @@ impl<Number, Hash> FraudProof<Number, Hash> {
Self::ImproperTransactionSortition(proof) => proof.domain_id,
#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
Self::Dummy { domain_id, .. } => *domain_id,
FraudProof::InvalidTotalRewards(proof) => proof.domain_id(),
FraudProof::InvalidBundles(proof) => proof.domain_id(),
FraudProof::InvalidExtrinsicsRoot(proof) => proof.domain_id,
FraudProof::InvalidDomainBlockHash(proof) => proof.domain_id,
Self::InvalidTotalRewards(proof) => proof.domain_id(),
Self::InvalidExtrinsicsRoot(proof) => proof.domain_id,
Self::InvalidBundles(proof) => proof.domain_id(),
Self::ValidBundle(proof) => proof.domain_id,
Self::InvalidDomainBlockHash(proof) => proof.domain_id,
}
}

Expand All @@ -390,11 +404,12 @@ impl<Number, Hash> FraudProof<Number, Hash> {
Self::Dummy {
bad_receipt_hash, ..
} => *bad_receipt_hash,
FraudProof::InvalidTotalRewards(proof) => proof.bad_receipt_hash(),
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
FraudProof::InvalidBundles(_) => Default::default(),
FraudProof::InvalidExtrinsicsRoot(proof) => proof.bad_receipt_hash,
FraudProof::InvalidDomainBlockHash(proof) => proof.bad_receipt_hash,
Self::InvalidBundles(_) => Default::default(),
Self::InvalidDomainBlockHash(proof) => proof.bad_receipt_hash,
}
}

Expand Down Expand Up @@ -589,6 +604,17 @@ pub fn operator_block_rewards_final_key() -> Vec<u8> {
.to_vec()
}

/// Fraud proof for the valid bundles in `ExecutionReceipt::inboxed_bundles`
#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
pub struct ValidBundleProof {
/// The id of the domain this fraud proof targeted
pub domain_id: DomainId,
/// The targetted bad receipt
pub bad_receipt_hash: H256,
/// The index of the targetted bundle
pub bundle_index: u32,
}

/// Digest storage key in frame_system.
/// Unfortunately, the digest storage is private and not possible to derive the key from it directly.
pub fn system_digest_final_key() -> Vec<u8> {
Expand Down
95 changes: 90 additions & 5 deletions crates/sp-domains-fraud-proof/src/host_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ use crate::{
};
use codec::{Decode, Encode};
use domain_block_preprocessor::inherents::extract_domain_runtime_upgrade_code;
use domain_block_preprocessor::runtime_api::{SetCodeConstructor, TimestampExtrinsicConstructor};
use domain_block_preprocessor::runtime_api::{
SetCodeConstructor, SignerExtractor, TimestampExtrinsicConstructor,
};
use domain_block_preprocessor::runtime_api_light::RuntimeApiLight;
use sc_client_api::BlockBackend;
use sc_executor::RuntimeVersionOf;
use sp_api::{BlockT, ProvideRuntimeApi};
use sp_api::{BlockT, HashT, ProvideRuntimeApi};
use sp_blockchain::HeaderBackend;
use sp_core::traits::{CodeExecutor, FetchRuntimeCode, RuntimeCode};
use sp_core::H256;
use sp_domains::{DomainId, DomainsApi};
use sp_runtime::traits::{Header as HeaderT, NumberFor};
use sp_runtime::OpaqueExtrinsic;
use sp_std::vec::Vec;
use sp_trie::StorageProof;
use std::borrow::Cow;
use std::marker::PhantomData;
Expand All @@ -35,6 +40,15 @@ pub trait FraudProofHostFunctions: Send + Sync {
fraud_proof_verification_req: FraudProofVerificationInfoRequest,
) -> Option<FraudProofVerificationInfoResponse>;

/// Derive the bundle digest for the given bundle body.
fn derive_bundle_digest(
&self,
consensus_block_hash: H256,
domain_id: DomainId,
bundle_body: Vec<OpaqueExtrinsic>,
) -> Option<H256>;

/// Check the execution proof
fn execution_proof_check(
&self,
pre_state_root: H256,
Expand Down Expand Up @@ -83,7 +97,7 @@ where
Block: BlockT,
Block::Hash: From<H256>,
DomainBlock: BlockT,
Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
Client: BlockBackend<Block> + HeaderBackend<Block> + ProvideRuntimeApi<Block>,
Client::Api: DomainsApi<Block, NumberFor<DomainBlock>, DomainBlock::Hash>,
Executor: CodeExecutor + RuntimeVersionOf,
{
Expand Down Expand Up @@ -118,6 +132,30 @@ where
.map(|ext| ext.encode())
}

fn get_domain_bundle_body(
&self,
consensus_block_hash: H256,
domain_id: DomainId,
bundle_index: u32,
) -> Option<Vec<OpaqueExtrinsic>> {
let consensus_block_hash = consensus_block_hash.into();
let consensus_extrinsics = self
.consensus_client
.block_body(consensus_block_hash)
.ok()??;
let mut bundles = self
.consensus_client
.runtime_api()
.extract_successful_bundles(consensus_block_hash, domain_id, consensus_extrinsics)
.ok()?;

if bundle_index < bundles.len() as u32 {
Some(bundles.swap_remove(bundle_index as usize).extrinsics)
} else {
None
}
}

fn derive_domain_set_code_extrinsic(
&self,
consensus_block_hash: H256,
Expand Down Expand Up @@ -176,8 +214,8 @@ where
Block: BlockT,
Block::Hash: From<H256>,
DomainBlock: BlockT,
DomainBlock::Hash: From<H256>,
Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
DomainBlock::Hash: Into<H256> + From<H256>,
Client: BlockBackend<Block> + HeaderBackend<Block> + ProvideRuntimeApi<Block>,
Client::Api: DomainsApi<Block, NumberFor<DomainBlock>, DomainBlock::Hash>,
Executor: CodeExecutor + RuntimeVersionOf,
{
Expand All @@ -199,6 +237,14 @@ where
domain_timestamp_extrinsic,
)
}),
FraudProofVerificationInfoRequest::DomainBundleBody {
domain_id,
bundle_index,
} => self
.get_domain_bundle_body(consensus_block_hash, domain_id, bundle_index)
.map(|domain_bundle_body| {
FraudProofVerificationInfoResponse::DomainBundleBody(domain_bundle_body)
}),
FraudProofVerificationInfoRequest::DomainRuntimeCode(domain_id) => self
.get_domain_runtime_code(consensus_block_hash, domain_id)
.map(|domain_runtime_code| {
Expand All @@ -214,6 +260,45 @@ where
}
}

fn derive_bundle_digest(
&self,
consensus_block_hash: H256,
domain_id: DomainId,
bundle_body: Vec<OpaqueExtrinsic>,
) -> Option<H256> {
let mut extrinsics = Vec::with_capacity(bundle_body.len());
for opaque_extrinsic in bundle_body {
let ext = <<DomainBlock as BlockT>::Extrinsic>::decode(
&mut opaque_extrinsic.encode().as_slice(),
)
.ok()?;
extrinsics.push(ext);
}

let domain_runtime_code = self.get_domain_runtime_code(consensus_block_hash, domain_id)?;
let domain_runtime_api_light =
RuntimeApiLight::new(self.executor.clone(), domain_runtime_code.into());

let ext_signers: Vec<_> = SignerExtractor::<DomainBlock>::extract_signer(
&domain_runtime_api_light,
// `extract_signer` is a stateless runtime api thus it is okay to use
// default block hash
Default::default(),
extrinsics,
)
.ok()?
.into_iter()
.map(|(signer, tx)| {
(
signer,
<DomainBlock::Header as HeaderT>::Hashing::hash_of(&tx),
)
})
.collect();

Some(<DomainBlock::Header as HeaderT>::Hashing::hash_of(&ext_signers).into())
}

fn execution_proof_check(
&self,
pre_state_root: H256,
Expand Down
15 changes: 15 additions & 0 deletions crates/sp-domains-fraud-proof/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub use runtime_interface::fraud_proof_runtime_interface;
pub use runtime_interface::fraud_proof_runtime_interface::HostFunctions;
use sp_api::scale_info::TypeInfo;
use sp_domains::DomainId;
use sp_runtime::OpaqueExtrinsic;
use sp_runtime_interface::pass_by;
use sp_runtime_interface::pass_by::PassBy;
use sp_std::vec::Vec;
Expand All @@ -46,6 +47,11 @@ pub enum FraudProofVerificationInfoRequest {
BlockRandomness,
/// Domain timestamp extrinsic using the timestamp at a given consensus block hash.
DomainTimestampExtrinsic(DomainId),
/// The body of domain bundle included in a given consensus block at a given index
DomainBundleBody {
domain_id: DomainId,
bundle_index: u32,
},
/// The domain runtime code
DomainRuntimeCode(DomainId),
/// Domain set_code extrinsic if there is a runtime upgrade at a given consensus block hash.
Expand All @@ -72,6 +78,8 @@ pub enum FraudProofVerificationInfoResponse {
BlockRandomness(Randomness),
/// Encoded domain timestamp extrinsic using the timestamp from consensus state at a specific block hash.
DomainTimestampExtrinsic(Vec<u8>),
/// Domain block body fetch from a specific consensus block body
DomainBundleBody(Vec<OpaqueExtrinsic>),
/// The domain runtime code
DomainRuntimeCode(Vec<u8>),
/// Encoded domain set_code extrinsic if there is a runtime upgrade at given consensus block hash.
Expand Down Expand Up @@ -108,4 +116,11 @@ impl FraudProofVerificationInfoResponse {
_ => SetCodeExtrinsic::None,
}
}

pub fn into_bundle_body(self) -> Option<Vec<OpaqueExtrinsic>> {
match self {
Self::DomainBundleBody(bb) => Some(bb),
_ => None,
}
}
}
14 changes: 14 additions & 0 deletions crates/sp-domains-fraud-proof/src/runtime_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
use crate::FraudProofExtension;
use crate::{FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse};
use sp_core::H256;
use sp_domains::DomainId;
#[cfg(feature = "std")]
use sp_externalities::ExternalitiesExt;
use sp_runtime::OpaqueExtrinsic;
use sp_runtime_interface::runtime_interface;
use sp_std::vec::Vec;

Expand All @@ -21,6 +23,18 @@ pub trait FraudProofRuntimeInterface {
.get_fraud_proof_verification_info(consensus_block_hash, fraud_proof_verification_req)
}

/// Derive the bundle digest for the given bundle body.
fn derive_bundle_digest(
&mut self,
consensus_block_hash: H256,
domain_id: DomainId,
bundle_body: Vec<OpaqueExtrinsic>,
) -> Option<H256> {
self.extension::<FraudProofExtension>()
.expect("No `FraudProofExtension` associated for the current context!")
.derive_bundle_digest(consensus_block_hash, domain_id, bundle_body)
}

/// Check the execution proof
fn execution_proof_check(
&mut self,
Expand Down
Loading

0 comments on commit 1756a8e

Please sign in to comment.