Files
pezkuwi-subxt/pallets/parachain-system/src/validate_block/tests.rs
T
Bastian Köcher cea28704f7 Ensure that inherents are first and unsigned (#1654)
* Ensure that inherents are first and unsigned

* Update pallets/parachain-system/src/validate_block/tests.rs

Co-authored-by: Sergei Shulepov <sergei@parity.io>

Co-authored-by: Sergei Shulepov <sergei@parity.io>
2022-09-22 16:53:26 +01:00

291 lines
8.9 KiB
Rust

// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
use codec::{Decode, Encode};
use cumulus_primitives_core::{ParachainBlockData, PersistedValidationData};
use cumulus_test_client::{
generate_extrinsic,
runtime::{Block, Hash, Header, TestPalletCall, UncheckedExtrinsic, WASM_BINARY},
transfer, BlockData, BuildParachainBlockData, Client, DefaultTestClientBuilderExt, HeadData,
InitBlockBuilder, TestClientBuilder, TestClientBuilderExt, ValidationParams,
};
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
use sp_keyring::AccountKeyring::*;
use sp_runtime::{generic::BlockId, traits::Header as HeaderT};
use std::{env, process::Command};
fn call_validate_block_encoded_header(
parent_head: Header,
block_data: ParachainBlockData<Block>,
relay_parent_storage_root: Hash,
) -> cumulus_test_client::ExecutorResult<Vec<u8>> {
cumulus_test_client::validate_block(
ValidationParams {
block_data: BlockData(block_data.encode()),
parent_head: HeadData(parent_head.encode()),
relay_parent_number: 1,
relay_parent_storage_root,
},
&WASM_BINARY.expect("You need to build the WASM binaries to run the tests!"),
)
.map(|v| v.head_data.0)
}
fn call_validate_block(
parent_head: Header,
block_data: ParachainBlockData<Block>,
relay_parent_storage_root: Hash,
) -> cumulus_test_client::ExecutorResult<Header> {
call_validate_block_encoded_header(parent_head, block_data, relay_parent_storage_root)
.map(|v| Header::decode(&mut &v[..]).expect("Decodes `Header`."))
}
fn create_test_client() -> (Client, Header) {
let client = TestClientBuilder::new()
// NOTE: this allows easier debugging
.set_execution_strategy(sc_client_api::ExecutionStrategy::NativeWhenPossible)
.build();
let genesis_header = client
.header(&BlockId::number(0))
.ok()
.flatten()
.expect("Genesis header exists; qed");
(client, genesis_header)
}
struct TestBlockData {
block: ParachainBlockData<Block>,
validation_data: PersistedValidationData,
}
fn build_block_with_witness(
client: &Client,
extra_extrinsics: Vec<UncheckedExtrinsic>,
parent_head: Header,
sproof_builder: RelayStateSproofBuilder,
) -> TestBlockData {
let (relay_parent_storage_root, _) = sproof_builder.clone().into_state_root_and_proof();
let mut validation_data = PersistedValidationData {
relay_parent_number: 1,
parent_head: parent_head.encode().into(),
..Default::default()
};
let mut builder = client.init_block_builder(Some(validation_data.clone()), sproof_builder);
validation_data.relay_parent_storage_root = relay_parent_storage_root;
extra_extrinsics.into_iter().for_each(|e| builder.push(e).unwrap());
let block = builder.build_parachain_block(*parent_head.state_root());
TestBlockData { block, validation_data }
}
#[test]
fn validate_block_no_extra_extrinsics() {
sp_tracing::try_init_simple();
let (client, parent_head) = create_test_client();
let TestBlockData { block, validation_data } =
build_block_with_witness(&client, Vec::new(), parent_head.clone(), Default::default());
let header = block.header().clone();
let res_header =
call_validate_block(parent_head, block, validation_data.relay_parent_storage_root)
.expect("Calls `validate_block`");
assert_eq!(header, res_header);
}
#[test]
fn validate_block_with_extra_extrinsics() {
sp_tracing::try_init_simple();
let (client, parent_head) = create_test_client();
let extra_extrinsics = vec![
transfer(&client, Alice, Bob, 69),
transfer(&client, Bob, Charlie, 100),
transfer(&client, Charlie, Alice, 500),
];
let TestBlockData { block, validation_data } = build_block_with_witness(
&client,
extra_extrinsics,
parent_head.clone(),
Default::default(),
);
let header = block.header().clone();
let res_header =
call_validate_block(parent_head, block, validation_data.relay_parent_storage_root)
.expect("Calls `validate_block`");
assert_eq!(header, res_header);
}
#[test]
fn validate_block_returns_custom_head_data() {
sp_tracing::try_init_simple();
let expected_header = vec![1, 3, 3, 7, 4, 5, 6];
let (client, parent_head) = create_test_client();
let extra_extrinsics = vec![
transfer(&client, Alice, Bob, 69),
generate_extrinsic(
&client,
Charlie,
TestPalletCall::set_custom_validation_head_data {
custom_header: expected_header.clone(),
},
),
transfer(&client, Bob, Charlie, 100),
];
let TestBlockData { block, validation_data } = build_block_with_witness(
&client,
extra_extrinsics,
parent_head.clone(),
Default::default(),
);
let header = block.header().clone();
assert_ne!(expected_header, header.encode());
let res_header = call_validate_block_encoded_header(
parent_head,
block,
validation_data.relay_parent_storage_root,
)
.expect("Calls `validate_block`");
assert_eq!(expected_header, res_header);
}
#[test]
fn validate_block_invalid_parent_hash() {
sp_tracing::try_init_simple();
if env::var("RUN_TEST").is_ok() {
let (client, parent_head) = create_test_client();
let TestBlockData { block, validation_data } =
build_block_with_witness(&client, Vec::new(), parent_head.clone(), Default::default());
let (mut header, extrinsics, witness) = block.deconstruct();
header.set_parent_hash(Hash::from_low_u64_be(1));
let block_data = ParachainBlockData::new(header, extrinsics, witness);
call_validate_block(parent_head, block_data, validation_data.relay_parent_storage_root)
.unwrap_err();
} else {
let output = Command::new(env::current_exe().unwrap())
.args(&["validate_block_invalid_parent_hash", "--", "--nocapture"])
.env("RUN_TEST", "1")
.output()
.expect("Runs the test");
assert!(output.status.success());
assert!(dbg!(String::from_utf8(output.stderr).unwrap()).contains("Invalid parent hash"));
}
}
#[test]
fn validate_block_fails_on_invalid_validation_data() {
sp_tracing::try_init_simple();
if env::var("RUN_TEST").is_ok() {
let (client, parent_head) = create_test_client();
let TestBlockData { block, .. } =
build_block_with_witness(&client, Vec::new(), parent_head.clone(), Default::default());
call_validate_block(parent_head, block, Hash::random()).unwrap_err();
} else {
let output = Command::new(env::current_exe().unwrap())
.args(&["validate_block_fails_on_invalid_validation_data", "--", "--nocapture"])
.env("RUN_TEST", "1")
.output()
.expect("Runs the test");
assert!(output.status.success());
assert!(dbg!(String::from_utf8(output.stderr).unwrap())
.contains("Relay parent storage root doesn't match"));
}
}
#[test]
fn check_inherent_fails_on_validate_block_as_expected() {
sp_tracing::try_init_simple();
if env::var("RUN_TEST").is_ok() {
let (client, parent_head) = create_test_client();
let TestBlockData { block, validation_data } = build_block_with_witness(
&client,
Vec::new(),
parent_head.clone(),
RelayStateSproofBuilder { current_slot: 1337.into(), ..Default::default() },
);
call_validate_block(parent_head, block, validation_data.relay_parent_storage_root)
.unwrap_err();
} else {
let output = Command::new(env::current_exe().unwrap())
.args(&["check_inherent_fails_on_validate_block_as_expected", "--", "--nocapture"])
.env("RUN_TEST", "1")
.output()
.expect("Runs the test");
assert!(output.status.success());
assert!(
dbg!(String::from_utf8(output.stderr).unwrap()).contains("Checking inherents failed")
);
}
}
#[test]
fn check_inherents_are_unsigned_and_before_all_other_extrinsics() {
sp_tracing::try_init_simple();
if env::var("RUN_TEST").is_ok() {
let (client, parent_head) = create_test_client();
let TestBlockData { block, validation_data } =
build_block_with_witness(&client, Vec::new(), parent_head.clone(), Default::default());
let (header, mut extrinsics, proof) = block.deconstruct();
extrinsics.insert(0, transfer(&client, Alice, Bob, 69));
call_validate_block(
parent_head,
ParachainBlockData::new(header, extrinsics, proof),
validation_data.relay_parent_storage_root,
)
.unwrap_err();
} else {
let output = Command::new(env::current_exe().unwrap())
.args(&[
"check_inherents_are_unsigned_and_before_all_other_extrinsics",
"--",
"--nocapture",
])
.env("RUN_TEST", "1")
.output()
.expect("Runs the test");
assert!(output.status.success());
assert!(String::from_utf8(output.stderr).unwrap()
.contains("Could not find `set_validation_data` inherent"));
}
}