// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot 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. // Polkadot 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 Polkadot. If not, see . //! PVF host integration tests checking the chain production pipeline. use super::TestHost; use codec::{Decode, Encode}; use polkadot_node_primitives::PoV; use polkadot_parachain_primitives::primitives::{ BlockData as GenericBlockData, HeadData as GenericHeadData, }; use polkadot_primitives::PersistedValidationData; use sp_core::H256; use test_parachain_adder::{hash_state, BlockData, HeadData}; #[tokio::test] async fn execute_good_block_on_parent() { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; let pvd = PersistedValidationData { parent_head: GenericHeadData(parent_head.encode()), relay_parent_number: 1u32, relay_parent_storage_root: H256::default(), max_pov_size: 4096 * 1024, }; let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let host = TestHost::new().await; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), pvd, pov, Default::default(), ) .await .unwrap(); let new_head = HeadData::decode(&mut &ret.head_data.0[..]).unwrap(); assert_eq!(new_head.number, 1); assert_eq!(new_head.parent_hash, parent_head.hash()); assert_eq!(new_head.post_state, hash_state(512)); } #[tokio::test] async fn execute_good_chain_on_parent() { let mut parent_hash = [0; 32]; let mut last_state = 0; let host = TestHost::new().await; for (number, add) in (0..10).enumerate() { let parent_head = HeadData { number: number as u64, parent_hash, post_state: hash_state(last_state) }; let block_data = BlockData { state: last_state, add }; let pvd = PersistedValidationData { parent_head: GenericHeadData(parent_head.encode()), relay_parent_number: 1u32, relay_parent_storage_root: H256::default(), max_pov_size: 4096 * 1024, }; let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), pvd, pov, Default::default(), ) .await .unwrap(); let new_head = HeadData::decode(&mut &ret.head_data.0[..]).unwrap(); assert_eq!(new_head.number, number as u64 + 1); assert_eq!(new_head.parent_hash, parent_head.hash()); assert_eq!(new_head.post_state, hash_state(last_state + add)); parent_hash = new_head.hash(); last_state += add; } } #[tokio::test] async fn execute_bad_block_on_parent() { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 256, // start state is wrong. add: 256, }; let pvd = PersistedValidationData { parent_head: GenericHeadData(parent_head.encode()), relay_parent_number: 1u32, relay_parent_storage_root: H256::default(), max_pov_size: 4096 * 1024, }; let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let host = TestHost::new().await; let _err = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), pvd, pov, Default::default(), ) .await .unwrap_err(); } #[tokio::test] async fn stress_spawn() { let host = std::sync::Arc::new(TestHost::new().await); async fn execute(host: std::sync::Arc) { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; let pvd = PersistedValidationData { parent_head: GenericHeadData(parent_head.encode()), relay_parent_number: 1u32, relay_parent_storage_root: H256::default(), max_pov_size: 4096 * 1024, }; let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), pvd, pov, Default::default(), ) .await .unwrap(); let new_head = HeadData::decode(&mut &ret.head_data.0[..]).unwrap(); assert_eq!(new_head.number, 1); assert_eq!(new_head.parent_hash, parent_head.hash()); assert_eq!(new_head.post_state, hash_state(512)); } futures::future::join_all((0..100).map(|_| execute(host.clone()))).await; } // With one worker, run multiple execution jobs serially. They should not conflict. #[tokio::test] async fn execute_can_run_serially() { let host = std::sync::Arc::new( TestHost::new_with_config(|cfg| { cfg.execute_workers_max_num = 1; }) .await, ); async fn execute(host: std::sync::Arc) { let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) }; let block_data = BlockData { state: 0, add: 512 }; let pvd = PersistedValidationData { parent_head: GenericHeadData(parent_head.encode()), relay_parent_number: 1u32, relay_parent_storage_root: H256::default(), max_pov_size: 4096 * 1024, }; let pov = PoV { block_data: GenericBlockData(block_data.encode()) }; let ret = host .validate_candidate( test_parachain_adder::wasm_binary_unwrap(), pvd, pov, Default::default(), ) .await .unwrap(); let new_head = HeadData::decode(&mut &ret.head_data.0[..]).unwrap(); assert_eq!(new_head.number, 1); assert_eq!(new_head.parent_hash, parent_head.hash()); assert_eq!(new_head.post_state, hash_state(512)); } futures::future::join_all((0..5).map(|_| execute(host.clone()))).await; }