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

Domains: Fix inconsistency between Block body and Trace roots #2343

Merged
merged 4 commits into from
Dec 22, 2023
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
20 changes: 17 additions & 3 deletions crates/pallet-domains/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ use crate::{
use codec::{Decode, Encode, MaxEncodedLen};
use domain_runtime_primitives::opaque::Header as DomainHeader;
use domain_runtime_primitives::BlockNumber as DomainBlockNumber;
use frame_support::dispatch::RawOrigin;
use frame_support::dispatch::{DispatchInfo, RawOrigin};
use frame_support::traits::{ConstU16, ConstU32, ConstU64, Currency, Hooks};
use frame_support::weights::constants::RocksDbWeight;
use frame_support::weights::Weight;
use frame_support::weights::{IdentityFee, Weight};
use frame_support::{assert_err, assert_ok, parameter_types, PalletId};
use frame_system::mocking::MockUncheckedExtrinsic;
use frame_system::pallet_prelude::*;
use scale_info::TypeInfo;
use sp_core::crypto::Pair;
Expand Down Expand Up @@ -72,6 +73,7 @@ frame_support::construct_runtime!(

type BlockNumber = u64;
type Hash = H256;
type AccountId = u64;

parameter_types! {
pub const ExtrinsicsRootStateVersion: StateVersion = StateVersion::V0;
Expand All @@ -87,7 +89,7 @@ impl frame_system::Config for Test {
type Nonce = u64;
type Hash = Hash;
type Hashing = BlakeTwo256;
type AccountId = u64;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
Expand Down Expand Up @@ -244,9 +246,21 @@ impl pallet_domains::Config for Test {
type SudoId = ();
}

pub struct ExtrinsicStorageFees;
impl domain_pallet_executive::ExtrinsicStorageFees<Test> for ExtrinsicStorageFees {
fn extract_signer(_xt: MockUncheckedExtrinsic<Test>) -> (Option<AccountId>, DispatchInfo) {
(None, DispatchInfo::default())
}

fn on_storage_fees_charged(_charged_fees: Balance) {}
}

impl domain_pallet_executive::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Currency = Balances;
type LengthToFee = IdentityFee<Balance>;
type ExtrinsicStorageFees = ExtrinsicStorageFees;
}

pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
Expand Down
199 changes: 199 additions & 0 deletions domains/client/domain-operator/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1789,6 +1789,205 @@ async fn test_bundle_equivocation_fraud_proof() {
ferdie.produce_blocks(1).await.unwrap();
}

#[tokio::test(flavor = "multi_thread")]
async fn test_domain_block_builder_include_ext_with_failed_execution() {
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;
produce_blocks!(ferdie, alice, 1).await.unwrap();

// produce a transfer from alice to bob
// we send two transactions, first one must succeed, while second one will fail
// second extrinsic fails due to not enough balance but should still be included in the domain
// block body
let pre_alice_free_balance = alice.free_balance(Alice.to_account_id());
let alice_account_nonce = alice.account_nonce();

// first ext that must succeed
let tx = alice.construct_extrinsic(
alice_account_nonce,
pallet_balances::Call::transfer_allow_death {
dest: Bob.to_account_id(),
// we send half of alice balance to bob
value: pre_alice_free_balance / 2,
},
);
alice
.send_extrinsic(tx)
.await
.expect("Failed to send extrinsic");

// second ext that will fail due to balance restrictions.
let tx = alice.construct_extrinsic(
alice_account_nonce + 1,
pallet_balances::Call::transfer_allow_death {
dest: Bob.to_account_id(),
// try to send value that is more than what alice has
value: pre_alice_free_balance,
},
);
alice
.send_extrinsic(tx)
.await
.expect("Failed to send extrinsic");

// Produce a bundle and submit to the tx pool of the consensus node
let (_slot, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await;
assert!(bundle.is_some());
let bundle = bundle.unwrap();
assert_eq!(bundle.extrinsics.len(), 2);

// produce block and import domain block
produce_blocks!(ferdie, alice, 1).await.unwrap();

// domain block body should have 3 extrinsics
// timestamp inherent, successful transfer, failed transfer
let best_hash = alice.client.info().best_hash;
let domain_block_extrinsics = alice.client.block_body(best_hash).unwrap();
assert_eq!(domain_block_extrinsics.unwrap().len(), 3);

// next bundle should have er which should a total of 5 trace roots
// pre_timestamp_root + pre_success_ext_root + pre_failed_ext_root + pre_finalize_block_root
// + post_finalize_block_root
let (_slot, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await;
assert!(bundle.is_some());
let bundle = bundle.unwrap();
let er = bundle.receipt();
assert_eq!(er.execution_trace.len(), 5);
assert_eq!(er.execution_trace[4], er.final_state_root);
}

#[tokio::test(flavor = "multi_thread")]
async fn test_domain_block_builder_include_ext_with_failed_predispatch() {
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;
produce_blocks!(ferdie, alice, 1).await.unwrap();

// produce a transfer from alice to bob in one bundle, which should succeed
let pre_alice_free_balance = alice.free_balance(Alice.to_account_id());
let alice_account_nonce = alice.account_nonce();

let tx = alice.construct_extrinsic(
alice_account_nonce,
pallet_balances::Call::transfer_allow_death {
dest: Bob.to_account_id(),
// we send half of alice balance to bob
value: pre_alice_free_balance / 2,
},
);
alice
.send_extrinsic(tx)
.await
.expect("Failed to send extrinsic");

// Produce a bundle and submit to the tx pool of the consensus node
let (_slot, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await;
assert!(bundle.is_some());
let bundle = bundle.unwrap();
assert_eq!(bundle.extrinsics.len(), 1);

// we produce another bundle with similar transaction
// second one will fail at pre dispatch since we use same nonce
// we change the value to something else so that ext is different
// send the tip so that previous ext is replaced with current in Domain tx pool
let tip = 1000000;
let tx = alice.construct_extrinsic_with_tip(
// use the same nonce
alice_account_nonce,
tip,
pallet_balances::Call::transfer_allow_death {
dest: Bob.to_account_id(),
value: pre_alice_free_balance / 3,
},
);
alice
.send_extrinsic(tx)
.await
.expect("Failed to send extrinsic");

// Produce a bundle and submit to the tx pool of the consensus node
let (slot, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await;
assert!(bundle.is_some());
let bundle = bundle.unwrap();
assert_eq!(bundle.extrinsics.len(), 1);

// produce block and import domain block
produce_block_with!(ferdie.produce_block_with_slot(slot), alice)
.await
.unwrap();

// domain block body should have 3 extrinsics
// timestamp inherent, successful transfer, failed transfer
let best_hash = alice.client.info().best_hash;
let domain_block_extrinsics = alice.client.block_body(best_hash).unwrap();
assert_eq!(domain_block_extrinsics.clone().unwrap().len(), 3);

// next bundle should have er which should a total of 5 trace roots
// pre_timestamp_root + pre_success_ext_root + pre_failed_ext_root + pre_finalize_block_root
// + post_finalize_block_root
let (_slot, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await;
assert!(bundle.is_some());
let bundle = bundle.unwrap();
let er = bundle.sealed_header.header.receipt;

assert_eq!(er.execution_trace.len(), 5);
assert_eq!(er.execution_trace[4], er.final_state_root);
vedhavyas marked this conversation as resolved.
Show resolved Hide resolved

let header = alice.client.header(best_hash).unwrap().unwrap();
assert_eq!(
*header.extrinsics_root(),
BlakeTwo256::ordered_trie_root(
domain_block_extrinsics
.unwrap()
.iter()
.map(Encode::encode)
.collect(),
sp_core::storage::StateVersion::V1
)
);
}

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