Skip to content

Commit

Permalink
Add try_state and integrity_test to XCM simulator fuzzer (#3222)
Browse files Browse the repository at this point in the history
This adds `try_state()` and `integrity_test()` to the four runtimes of
the XCM-simulator fuzzer.

With this, we are able to stress-test [message-queue's
try_state](https://github.com/paritytech/polkadot-sdk/blob/7df1ae3b8111d534cce108b2b405b6a33fcdedc3/substrate/frame/message-queue/src/lib.rs#L1245-L1347).

This also adds the `Transact` block-listing from #2424 to avoid
false-positives.

Thank you @ggwpez for the help with the runtime configurations.
  • Loading branch information
louismerlin authored Feb 8, 2024
1 parent 2ea6bcf commit 84d89e3
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 59 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

13 changes: 13 additions & 0 deletions polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ scale-info = { version = "2.10.0", features = ["derive"] }

frame-system = { path = "../../../../substrate/frame/system" }
frame-support = { path = "../../../../substrate/frame/support" }
frame-executive = { path = "../../../../substrate/frame/executive" }
frame-try-runtime = { path = "../../../../substrate/frame/try-runtime" }
pallet-balances = { path = "../../../../substrate/frame/balances" }
pallet-message-queue = { path = "../../../../substrate/frame/message-queue" }
sp-std = { path = "../../../../substrate/primitives/std" }
Expand All @@ -35,6 +37,17 @@ polkadot-runtime-parachains = { path = "../../../runtime/parachains" }
polkadot-parachain-primitives = { path = "../../../parachain" }

[features]
try-runtime = [
"frame-executive/try-runtime",
"frame-support/try-runtime",
"frame-system/try-runtime",
"frame-try-runtime/try-runtime",
"pallet-balances/try-runtime",
"pallet-message-queue/try-runtime",
"pallet-xcm/try-runtime",
"polkadot-runtime-parachains/try-runtime",
"sp-runtime/try-runtime",
]
runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
Expand Down
6 changes: 3 additions & 3 deletions polkadot/xcm/xcm-simulator/fuzzer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ cargo install honggfuzz
In this directory, run this command:

```
cargo hfuzz run xcm-fuzzer
HFUZZ_BUILD_ARGS="--features=try-runtime" cargo hfuzz run xcm-fuzzer
```

## Run a single input

In this directory, run this command:

```
cargo hfuzz run-debug xcm-fuzzer hfuzz_workspace/xcm-fuzzer/fuzzer_input_file
cargo run --features=try-runtime -- hfuzz_workspace/xcm-fuzzer/fuzzer_input_file
```

## Generate coverage
Expand All @@ -31,7 +31,7 @@ In this directory, run these four commands:

```
RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" \
CARGO_INCREMENTAL=0 SKIP_WASM_BUILD=1 CARGO_HOME=./cargo cargo build
CARGO_INCREMENTAL=0 SKIP_WASM_BUILD=1 CARGO_HOME=./cargo cargo build --features=try-runtime
../../../target/debug/xcm-fuzzer hfuzz_workspace/xcm-fuzzer/input/
zip -0 ccov.zip `find ../../../target/ \( -name "*.gc*" -o -name "test-*.gc*" \) -print`
grcov ccov.zip -s ../../../ -t html --llvm --branch --ignore-not-existing -o ./coverage
Expand Down
42 changes: 39 additions & 3 deletions polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ use polkadot_parachain_primitives::primitives::Id as ParaId;
use sp_runtime::{traits::AccountIdConversion, BuildStorage};
use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt};

use frame_support::assert_ok;
#[cfg(feature = "try-runtime")]
use frame_support::traits::{TryState, TryStateSelect::All};
use frame_support::{assert_ok, traits::IntegrityTest};
use xcm::{latest::prelude::*, MAX_XCM_DECODE_DEPTH};

use arbitrary::{Arbitrary, Error, Unstructured};
Expand Down Expand Up @@ -98,7 +100,7 @@ impl<'a> Arbitrary<'a> for XcmMessage {
if let Ok(message) =
DecodeLimit::decode_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut encoded_message)
{
return Ok(XcmMessage { source, destination, message })
return Ok(XcmMessage { source, destination, message });
}
Err(Error::IncorrectFormat)
}
Expand Down Expand Up @@ -148,13 +150,33 @@ pub fn relay_ext() -> sp_io::TestExternalities {
pub type RelayChainPalletXcm = pallet_xcm::Pallet<relay_chain::Runtime>;
pub type ParachainPalletXcm = pallet_xcm::Pallet<parachain::Runtime>;

// We check XCM messages recursively for blocklisted messages
fn recursively_matches_blocklisted_messages(message: &Instruction<()>) -> bool {
match message {
DepositReserveAsset { xcm, .. } |
ExportMessage { xcm, .. } |
InitiateReserveWithdraw { xcm, .. } |
InitiateTeleport { xcm, .. } |
TransferReserveAsset { xcm, .. } |
SetErrorHandler(xcm) |
SetAppendix(xcm) => xcm.iter().any(recursively_matches_blocklisted_messages),
// The blocklisted message is the Transact instruction.
m => matches!(m, Transact { .. }),
}
}

fn run_input(xcm_messages: [XcmMessage; 5]) {
MockNet::reset();

#[cfg(not(fuzzing))]
println!();

for xcm_message in xcm_messages {
if xcm_message.message.iter().any(recursively_matches_blocklisted_messages) {
println!(" skipping message\n");
continue;
}

if xcm_message.source % 4 == 0 {
// We get the destination for the message
let parachain_id = (xcm_message.destination % 3) + 1;
Expand Down Expand Up @@ -197,8 +219,22 @@ fn run_input(xcm_messages: [XcmMessage; 5]) {
}
#[cfg(not(fuzzing))]
println!();
// We run integrity tests and try_runtime invariants
[ParaA::execute_with, ParaB::execute_with, ParaC::execute_with].iter().for_each(
|execute_with| {
execute_with(|| {
#[cfg(feature = "try-runtime")]
parachain::AllPalletsWithSystem::try_state(Default::default(), All).unwrap();
parachain::AllPalletsWithSystem::integrity_test();
});
},
);
Relay::execute_with(|| {
#[cfg(feature = "try-runtime")]
relay_chain::AllPalletsWithSystem::try_state(Default::default(), All).unwrap();
relay_chain::AllPalletsWithSystem::integrity_test();
});
}
Relay::execute_with(|| {});
}

fn main() {
Expand Down
44 changes: 17 additions & 27 deletions polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ use frame_support::{
};

use frame_system::EnsureRoot;
use sp_core::{ConstU32, H256};
use sp_core::ConstU32;
use sp_runtime::{
traits::{Hash, IdentityLookup},
AccountId32,
generic,
traits::{AccountIdLookup, BlakeTwo256, Hash, IdentifyAccount, Verify},
MultiAddress, MultiSignature,
};
use sp_std::prelude::*;

Expand All @@ -47,38 +48,29 @@ use xcm_builder::{
};
use xcm_executor::{Config, XcmExecutor};

pub type AccountId = AccountId32;
pub type SignedExtra = (frame_system::CheckNonZeroSender<Runtime>,);

pub type BlockNumber = u64;
pub type Address = MultiAddress<AccountId, ()>;
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type UncheckedExtrinsic =
generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;

pub type Signature = MultiSignature;
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
pub type Balance = u128;

parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const BlockHashCount: u32 = 250;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Nonce = u64;
type Hash = H256;
type Hashing = ::sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Lookup = AccountIdLookup<AccountId, ()>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type BlockWeights = ();
type BlockLength = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = ();
type BaseCallFilter = Everything;
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}

parameter_types! {
Expand Down Expand Up @@ -356,8 +348,6 @@ impl pallet_xcm::Config for Runtime {
type AdminOrigin = EnsureRoot<AccountId>;
}

type Block = frame_system::mocking::MockBlock<Runtime>;

construct_runtime!(
pub enum Runtime
{
Expand Down
45 changes: 19 additions & 26 deletions polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ use frame_support::{
};

use frame_system::EnsureRoot;
use sp_core::{ConstU32, H256};
use sp_runtime::{traits::IdentityLookup, AccountId32};
use sp_core::ConstU32;
use sp_runtime::{
generic,
traits::{BlakeTwo256, IdentifyAccount, Verify},
MultiAddress, MultiSignature,
};

use polkadot_parachain_primitives::primitives::Id as ParaId;
use polkadot_runtime_parachains::{
Expand All @@ -43,38 +47,29 @@ use xcm_builder::{
};
use xcm_executor::{Config, XcmExecutor};

pub type AccountId = AccountId32;
pub type SignedExtra = (frame_system::CheckNonZeroSender<Runtime>,);

pub type BlockNumber = u64;
pub type Address = MultiAddress<AccountId, ()>;
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type UncheckedExtrinsic =
generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;

pub type Signature = MultiSignature;
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
pub type Balance = u128;

parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const BlockHashCount: u32 = 250;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Nonce = u64;
type Hash = H256;
type Hashing = ::sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Lookup = sp_runtime::traits::AccountIdLookup<AccountId, ()>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type BlockWeights = ();
type BlockLength = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = ();
type BaseCallFilter = Everything;
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}

parameter_types! {
Expand Down Expand Up @@ -202,8 +197,6 @@ parameter_types! {

impl origin::Config for Runtime {}

type Block = frame_system::mocking::MockBlock<Runtime>;

parameter_types! {
/// Amount of weight that can be spent per block to service messages.
pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000);
Expand Down

0 comments on commit 84d89e3

Please sign in to comment.