From 84d89e379b6aa18cdf038049d3c6703008d6c8cf Mon Sep 17 00:00:00 2001
From: Louis Merlin <hello@louismerl.in>
Date: Thu, 8 Feb 2024 14:05:00 +0100
Subject: [PATCH] Add try_state and integrity_test to XCM simulator fuzzer
 (#3222)

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.
---
 Cargo.lock                                    |  2 +
 polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml  | 13 ++++++
 polkadot/xcm/xcm-simulator/fuzzer/README.md   |  6 +--
 polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs | 42 +++++++++++++++--
 .../xcm/xcm-simulator/fuzzer/src/parachain.rs | 44 +++++++-----------
 .../xcm-simulator/fuzzer/src/relay_chain.rs   | 45 ++++++++-----------
 6 files changed, 93 insertions(+), 59 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 27a1efc3809..db9a2ca3564 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -22192,8 +22192,10 @@ name = "xcm-simulator-fuzzer"
 version = "1.0.0"
 dependencies = [
  "arbitrary",
+ "frame-executive",
  "frame-support",
  "frame-system",
+ "frame-try-runtime",
  "honggfuzz",
  "pallet-balances",
  "pallet-message-queue",
diff --git a/polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml b/polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml
index 13b6e7b8652..30644dc0e0a 100644
--- a/polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml
+++ b/polkadot/xcm/xcm-simulator/fuzzer/Cargo.toml
@@ -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" }
@@ -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",
diff --git a/polkadot/xcm/xcm-simulator/fuzzer/README.md b/polkadot/xcm/xcm-simulator/fuzzer/README.md
index 0b3fdd8ec77..9c15ee881c1 100644
--- a/polkadot/xcm/xcm-simulator/fuzzer/README.md
+++ b/polkadot/xcm/xcm-simulator/fuzzer/README.md
@@ -14,7 +14,7 @@ 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
@@ -22,7 +22,7 @@ cargo hfuzz run xcm-fuzzer
 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
@@ -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
diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs
index 7026d5467c8..adf6cacd278 100644
--- a/polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs
+++ b/polkadot/xcm/xcm-simulator/fuzzer/src/fuzz.rs
@@ -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};
@@ -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)
 	}
@@ -148,6 +150,21 @@ 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();
 
@@ -155,6 +172,11 @@ fn run_input(xcm_messages: [XcmMessage; 5]) {
 	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;
@@ -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() {
diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs
index d8327c9b401..fbb60a25f44 100644
--- a/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs
+++ b/polkadot/xcm/xcm-simulator/fuzzer/src/parachain.rs
@@ -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::*;
 
@@ -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! {
@@ -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
 	{
diff --git a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs
index 7e42f558dd6..e8294560dfc 100644
--- a/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs
+++ b/polkadot/xcm/xcm-simulator/fuzzer/src/relay_chain.rs
@@ -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::{
@@ -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! {
@@ -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);
-- 
GitLab