Skip to content

Commit

Permalink
Merge pull request #2666 from subspace/invalid_xdm_variant
Browse files Browse the repository at this point in the history
XDM: Accept the fraud proof if the bad receipt marks a non XDM containing bundle as invalid bundle with InvalidXDM
  • Loading branch information
vedhavyas authored Apr 8, 2024
2 parents 417c996 + 8fe3aff commit 287305a
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 10 deletions.
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 {
// 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

0 comments on commit 287305a

Please sign in to comment.