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

XDM: Accept the fraud proof if the bad receipt marks a non XDM containing bundle as invalid bundle with InvalidXDM #2666

Merged
merged 1 commit into from
Apr 8, 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
32 changes: 22 additions & 10 deletions crates/sp-domains-fraud-proof/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,23 +667,35 @@ where
invalid_bundles_fraud_proof.proof_data.clone(),
)?;

let is_valid_xdm = get_fraud_proof_verification_info(
let maybe_is_valid_xdm = get_fraud_proof_verification_info(
H256::from_slice(bad_receipt.consensus_block_hash.as_ref()),
FraudProofVerificationInfoRequest::XDMValidationCheck {
domain_id: invalid_bundles_fraud_proof.domain_id,
opaque_extrinsic: extrinsic,
},
)
.and_then(FraudProofVerificationInfoResponse::into_xdm_validation_check)
.ok_or(VerificationError::FailedToValidateXDM)?;

// Proof to be considered valid only,
// If it is true invalid fraud proof then extrinsic must be an invalid xdm and
// If it is false invalid fraud proof then extrinsic must be a valid xdm
if is_valid_xdm != invalid_bundles_fraud_proof.is_true_invalid_fraud_proof {
Ok(())
.and_then(FraudProofVerificationInfoResponse::into_xdm_validation_check);

if let Some(is_valid_xdm) = maybe_is_valid_xdm {
dastansam marked this conversation as resolved.
Show resolved Hide resolved
// Proof to be considered valid only,
// If it is true invalid fraud proof then extrinsic must be an invalid xdm and
// If it is false invalid fraud proof then extrinsic must be a valid xdm
if is_valid_xdm != invalid_bundles_fraud_proof.is_true_invalid_fraud_proof {
Ok(())
} else {
Err(VerificationError::InvalidProof)
}
} else {
Err(VerificationError::InvalidProof)
// If this extrinsic is not an XDM,
// If it is false invalid, then bad receipt marked this extrinsic as InvalidXDM
// even though it is not an XDM, if so accept the fraud proof
if !invalid_bundles_fraud_proof.is_true_invalid_fraud_proof {
Ok(())
} else {
// If this is a true invalid but the extrinsic is not an XDM, then reject fraud proof.
// this can happen if there is a bug in the challenger node implementation.
Err(VerificationError::InvalidProof)
}
}
}
}
Expand Down
116 changes: 116 additions & 0 deletions domains/client/domain-operator/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1953,6 +1953,122 @@ async fn test_false_invalid_bundles_illegal_extrinsic_proof_creation_and_verific
assert!(!ferdie.does_receipt_exist(bad_receipt_hash).unwrap());
}

#[tokio::test(flavor = "multi_thread")]
async fn test_false_invalid_xdm_extrinsic_proof_creation_and_verification() {
let directory = TempDir::new().expect("Must be able to create temporary directory");

let mut builder = sc_cli::LoggerBuilder::new("");
builder.with_colors(false);
let _ = builder.init();

let tokio_handle = tokio::runtime::Handle::current();

// Start Ferdie
let mut ferdie = MockConsensusNode::run(
tokio_handle.clone(),
Ferdie,
BasePath::new(directory.path().join("ferdie")),
);

// Run Alice (a evm domain authority node)
let mut alice = domain_test_service::DomainNodeBuilder::new(
tokio_handle.clone(),
Alice,
BasePath::new(directory.path().join("alice")),
)
.build_evm_node(Role::Authority, GENESIS_DOMAIN_ID, &mut ferdie)
.await;

let bundle_to_tx = |opaque_bundle| {
subspace_test_runtime::UncheckedExtrinsic::new_unsigned(
pallet_domains::Call::submit_bundle { opaque_bundle }.into(),
)
.into()
};

produce_blocks!(ferdie, alice, 5).await.unwrap();

// transfer some balance from alice
let alice_balance = alice.free_balance(Alice.to_account_id());
let alice_nonce = alice.account_nonce();

let transfer_to_charlie_with_tip = alice.construct_extrinsic_with_tip(
alice_nonce,
alice_balance / 3,
pallet_balances::Call::transfer_allow_death {
dest: Charlie.to_account_id(),
value: 1,
},
);

alice
.send_extrinsic(transfer_to_charlie_with_tip)
.await
.expect("Failed to send extrinsic");

// Produce a bundle that contains the previously sent extrinsic and record that bundle for later use
let (slot, target_bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await;
assert_eq!(target_bundle.extrinsics.len(), 1);
let bundle_extrinsic_root = target_bundle.extrinsics_root();
produce_block_with!(ferdie.produce_block_with_slot(slot), alice)
.await
.unwrap();

// produce another bundle that marks the previous valid extrinsic as invalid.
let (slot, mut opaque_bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await;

let (bad_receipt_hash, bad_submit_bundle_tx) = {
let bad_receipt = &mut opaque_bundle.sealed_header.header.receipt;
// bad receipt marks this particular bundle as invalid even though the call is not XDM
bad_receipt.inboxed_bundles = vec![InboxedBundle::invalid(
InvalidBundleType::InvalidXDM(0),
bundle_extrinsic_root,
)];

opaque_bundle.sealed_header.signature = Sr25519Keyring::Alice
.pair()
.sign(opaque_bundle.sealed_header.pre_hash().as_ref())
.into();
(
opaque_bundle.receipt().hash::<BlakeTwo256>(),
bundle_to_tx(opaque_bundle),
)
};

// Wait for the fraud proof that target the bad ER
let wait_for_fraud_proof_fut = ferdie.wait_for_fraud_proof(move |fp| {
if let FraudProof::InvalidBundles(proof) = fp {
if let InvalidBundleType::InvalidXDM(extrinsic_index) = proof.invalid_bundle_type {
assert!(!proof.is_true_invalid_fraud_proof);
assert_eq!(extrinsic_index, 0);
return true;
}
}
false
});

// Produce a consensus block that contains the `bad_submit_bundle_tx` and the bad receipt should
// be added to the consensus chain block tree
produce_block_with!(
ferdie.produce_block_with_slot_at(
slot,
ferdie.client.info().best_hash,
Some(vec![bad_submit_bundle_tx])
),
alice
)
.await
.unwrap();
assert!(ferdie.does_receipt_exist(bad_receipt_hash).unwrap());

let _ = wait_for_fraud_proof_fut.await;

// Produce a consensus block that contains the fraud proof, the fraud proof wil be verified
// and executed, thus pruned the bad receipt from the block tree
ferdie.produce_blocks(1).await.unwrap();
assert!(!ferdie.does_receipt_exist(bad_receipt_hash).unwrap());
}

#[tokio::test(flavor = "multi_thread")]
async fn test_invalid_block_fees_proof_creation() {
let directory = TempDir::new().expect("Must be able to create temporary directory");
Expand Down
Loading