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

Keep domain compatible with gemini-3h #2638

Merged
merged 4 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion crates/pallet-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1549,7 +1549,7 @@ mod pallet {
if let Err(e) = Self::validate_fraud_proof(fraud_proof) {
log::warn!(
target: "runtime::domains",
"Bad fraud proof {:?}, error: {e:?}", fraud_proof.domain_id(),
"Bad fraud proof {fraud_proof:?}, error: {e:?}",
);
return InvalidTransactionCode::FraudProof.into();
}
Expand Down
77 changes: 76 additions & 1 deletion crates/sp-domains-fraud-proof/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::verification::InvalidBundleEquivocationError;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use codec::{Decode, Encode};
use core::fmt;
use scale_info::TypeInfo;
use sp_consensus_slots::Slot;
use sp_core::H256;
Expand Down Expand Up @@ -454,7 +455,7 @@ impl<ReceiptHash> InvalidBundlesFraudProof<ReceiptHash> {
/// Fraud proof.
// TODO: Revisit when fraud proof v2 is implemented.
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
#[derive(Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub enum FraudProof<Number, Hash, DomainHeader: HeaderT> {
InvalidStateTransition(InvalidStateTransitionProof<HeaderHashFor<DomainHeader>>),
InvalidTransaction(InvalidTransactionProof<HeaderHashFor<DomainHeader>>),
Expand Down Expand Up @@ -550,6 +551,80 @@ where
}
}

impl<Number, Hash, DomainHeader: HeaderT> fmt::Debug for FraudProof<Number, Hash, DomainHeader> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let domain_id = self.domain_id();
let bad_receipt_hash = self.targeted_bad_receipt_hash();
let bad_operator = self.targeted_bad_operator_and_slot_for_bundle_equivocation();
match self {
Self::InvalidStateTransition(_) => {
write!(
f,
"InvalidStateTransitionFraudProof({domain_id:?}#{bad_receipt_hash:?})"
)
}
Self::InvalidTransaction(_) => {
write!(
f,
"InvalidTransactionFraudProof({domain_id:?}#{bad_receipt_hash:?})"
)
}
Self::ImproperTransactionSortition(_) => {
write!(
f,
"ImproperTransactionSortitionFraudProof({domain_id:?}#{bad_receipt_hash:?})"
)
}
Self::BundleEquivocation(_) => {
write!(
f,
"BundleEquivocationFraudProof({domain_id:?}#{bad_operator:?})"
)
}
Self::InvalidExtrinsicsRoot(_) => {
write!(
f,
"InvalidExtrinsicsRootFraudProof({domain_id:?}#{bad_receipt_hash:?})"
)
}
Self::InvalidBlockFees(_) => {
write!(
f,
"InvalidBlockFeesFraudProof({domain_id:?}#{bad_receipt_hash:?})"
)
}
Self::ValidBundle(_) => {
write!(
f,
"ValidBundleFraudProof({domain_id:?}#{bad_receipt_hash:?})"
)
}
Self::InvalidBundles(_) => {
write!(
f,
"InvalidBundlesFraudProof({domain_id:?}#{bad_receipt_hash:?})"
)
}
Self::InvalidDomainBlockHash(_) => {
write!(
f,
"InvalidDomainBlockHashFraudProof({domain_id:?}#{bad_receipt_hash:?})"
)
}
Self::InvalidTransfers(_) => {
write!(
f,
"InvalidTransfersFraudProof({domain_id:?}#{bad_receipt_hash:?})"
)
}
#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
Self::Dummy { .. } => {
write!(f, "DummyFraudProof({domain_id:?}#{bad_receipt_hash:?})")
}
}
}
}

/// Proves an invalid state transition by challenging the trace at specific index in a bad receipt.
#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
pub struct InvalidStateTransitionProof<ReceiptHash> {
Expand Down
24 changes: 21 additions & 3 deletions domains/client/block-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ where
backend: &'a B,
mut extrinsics: VecDeque<Block::Extrinsic>,
maybe_inherent_data: Option<sp_inherents::InherentData>,
is_gemini_3h: bool,
) -> Result<Self, Error> {
let header = <<Block as BlockT>::Header as HeaderT>::new(
parent_number + One::one(),
Expand All @@ -179,9 +180,26 @@ where

if let Some(inherent_data) = maybe_inherent_data {
let inherent_extrinsics = Self::create_inherents(parent_hash, &api, inherent_data)?;
// reverse and push the inherents so that order is maintained
for inherent_extrinsic in inherent_extrinsics.into_iter().rev() {
extrinsics.push_front(inherent_extrinsic)

// TODO: This is used to keep compatible with gemini-3h, remove before next network
//
// HACK: ideally, any network should maintain the inherent extrinsic order to keep consistency
// with the order in the fraud proof verifiaction side, but in gemini-3h, the domain inherent
// extrinsic order is changed in the ER that derived from the consensus block #168431, we have
// to follow this change in the client side to ensure every domain node that sync from genesis
// will produce the same ER and hence can successfully submit ER to exend the previous ER.
let maintain_runtime_inherent_extrinsic_order =
!is_gemini_3h || parent_number >= 168430u32.into();

if maintain_runtime_inherent_extrinsic_order {
// reverse and push the inherents so that order is maintained
for inherent_extrinsic in inherent_extrinsics.into_iter().rev() {
extrinsics.push_front(inherent_extrinsic)
}
} else {
for inherent_extrinsic in inherent_extrinsics {
extrinsics.push_front(inherent_extrinsic)
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions domains/client/domain-operator/src/domain_block_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, One, Zer
use sp_runtime::{Digest, Saturating};
use std::cmp::Ordering;
use std::collections::VecDeque;
use std::str::FromStr;
use std::sync::Arc;

struct DomainBlockBuildResult<Block>
Expand Down Expand Up @@ -419,6 +420,15 @@ where
inherent_digests: Digest,
inherent_data: sp_inherents::InherentData,
) -> Result<DomainBlockBuildResult<Block>, sp_blockchain::Error> {
// TODO: This is used to keep compatible with gemini-3h, remove before next network
let is_gemini_3h = self.consensus_client.info().genesis_hash
== FromStr::from_str(
// The genesis hash of gemini-3h
"0c121c75f4ef450f40619e1fca9d1e8e7fbabc42c895bc4790801e85d5a91c34",
)
.map_err(|_| ())
.expect("parsing consensus block hash should success");
Comment on lines +424 to +430
Copy link
Member

Choose a reason for hiding this comment

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

Assuming parsing works, this should do the job, but even better way would be to define genesis has as [u8; 32] constant and store boolean result as a property of the data structure to not query info all the time since genesis hash never changes.


let block_builder = BlockBuilder::new(
&*self.client,
parent_hash,
Expand All @@ -428,6 +438,7 @@ where
&*self.backend,
extrinsics,
Some(inherent_data),
is_gemini_3h,
)?;

let BuiltBlock {
Expand Down Expand Up @@ -749,6 +760,7 @@ where
if let Some(mismatched_receipts) = self.find_mismatch_receipt(consensus_block_hash)? {
let fraud_proof = self.generate_fraud_proof(mismatched_receipts)?;

tracing::info!("Submit fraud proof: {fraud_proof:?}");
let consensus_best_hash = self.consensus_client.info().best_hash;
let mut runtime_api = self.consensus_client.runtime_api();
runtime_api.register_extension(
Expand Down
5 changes: 5 additions & 0 deletions domains/client/domain-operator/src/fraud_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,12 @@ where
inherent_digests.clone(),
&*self.backend,
extrinsics.into(),
// NOTE: the inherent extrinsic is already contained in the above `extrinsics`, which
// is getting from the block body, thus it is okay to pass `maybe_inherent_data` as
// `None` and `is_gemini_3h` as `false`, the latter is only used when `maybe_inherent_data`
// is `Some`.
None,
false,
)?;

let (storage_changes, call_data) = match &execution_phase {
Expand Down
Loading