// Copyright 2018-2019 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 Substrate. If not, see . //! A `CodeExecutor` specialization which uses natively compiled runtime when the wasm to be //! executed is equivalent to the natively compiled code. #![cfg_attr(feature = "benchmarks", feature(test))] #[cfg(feature = "benchmarks")] extern crate test; pub use substrate_executor::NativeExecutor; use substrate_executor::native_executor_instance; // Declare an instance of the native executor named `Executor`. Include the wasm binary as the // equivalent wasm code. native_executor_instance!( pub Executor, node_runtime::api::dispatch, node_runtime::native_version, node_runtime::WASM_BINARY ); #[cfg(test)] mod tests { use runtime_io; use super::Executor; use substrate_executor::{WasmExecutor, NativeExecutionDispatch}; use parity_codec::{Encode, Decode, Joiner}; use keyring::{AccountKeyring, Ed25519Keyring, Sr25519Keyring}; use runtime_support::{Hashable, StorageValue, StorageMap, traits::{Currency, Get}}; use state_machine::{CodeExecutor, Externalities, TestExternalities as CoreTestExternalities}; use primitives::{ twox_128, blake2_256, Blake2Hasher, ChangesTrieConfiguration, NeverNativeValue, NativeOrEncoded }; use node_primitives::{Hash, BlockNumber, AccountId, Balance, Index}; use runtime_primitives::traits::{Header as HeaderT, Hash as HashT}; use runtime_primitives::{generic::Era, ApplyOutcome, ApplyError, ApplyResult, Perbill}; use runtime_primitives::weights::{WeightMultiplier, SimpleDispatchInfo, WeighData}; use {balances, contracts, indices, staking, system, timestamp}; use contracts::ContractAddressFor; use system::{EventRecord, Phase}; use node_runtime::{ Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, System, SystemConfig, GrandpaConfig, IndicesConfig, ContractsConfig, Event, SessionKeys, CreationFee, CENTS, DOLLARS, MILLICENTS, SignedExtra, TransactionBaseFee, TransactionByteFee, MaximumBlockWeight, }; use wabt; use primitives::map; /// The wasm runtime code. /// /// `compact` since it is after post-processing with wasm-gc which performs tree-shaking thus /// making the binary slimmer. There is a convention to use compact version of the runtime /// as canonical. This is why `native_executor_instance` also uses the compact version of the /// runtime. const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY; /// The wasm runtime binary which hasn't undergone the compacting process. /// /// The idea here is to pass it as the current runtime code to the executor so the executor will /// have to execute provided wasm code instead of the native equivalent. This trick is used to /// test code paths that differ between native and wasm versions. const BLOATY_CODE: &[u8] = node_runtime::WASM_BINARY_BLOATY; const GENESIS_HASH: [u8; 32] = [69u8; 32]; type TestExternalities = CoreTestExternalities; fn transfer_fee(extrinsic: &E) -> Balance { let length_fee = >::get() + >::get() * (extrinsic.encode().len() as Balance); let weight_fee = SimpleDispatchInfo::default().weigh_data(()) as Balance; length_fee + weight_fee } fn multiplier_ideal() -> u32 { >::get() / 4 / 4 } fn creation_fee() -> Balance { >::get() } fn alice() -> AccountId { AccountKeyring::Alice.into() } fn bob() -> AccountId { AccountKeyring::Bob.into() } fn charlie() -> AccountId { AccountKeyring::Charlie.into() } fn dave() -> AccountId { AccountKeyring::Dave.into() } fn eve() -> AccountId { AccountKeyring::Eve.into() } fn ferdie() -> AccountId { AccountKeyring::Ferdie.into() } fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic { match xt.signed { Some((signed, extra)) => { let payload = (xt.function, extra.clone(), GENESIS_HASH); let key = AccountKeyring::from_public(&signed).unwrap(); let signature = payload.using_encoded(|b| { if b.len() > 256 { key.sign(&runtime_io::blake2_256(b)) } else { key.sign(b) } }).into(); UncheckedExtrinsic { signature: Some((indices::address::Address::Id(signed), signature, extra)), function: payload.0, } } None => UncheckedExtrinsic { signature: None, function: xt.function, }, } } fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra { ( system::CheckEra::from(Era::mortal(256, 0)), system::CheckNonce::from(nonce), system::CheckWeight::from(), balances::TakeFees::from(extra_fee) ) } fn xt() -> UncheckedExtrinsic { sign(CheckedExtrinsic { signed: Some((alice(), signed_extra(0, 0))), function: Call::Balances(balances::Call::transfer::(bob().into(), 69 * DOLLARS)), }) } fn from_block_number(n: u64) -> Header { Header::new(n, Default::default(), Default::default(), [69; 32].into(), Default::default()) } fn executor() -> ::substrate_executor::NativeExecutor { substrate_executor::NativeExecutor::new(None) } #[test] fn panic_execution_with_foreign_code_gives_error() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { 69_u128.encode() }, twox_128(>::key()).to_vec() => { 69_u128.encode() }, twox_128(>::key()).to_vec() => { 0_u128.encode() }, blake2_256(&>::key_for(0)).to_vec() => { vec![0u8; 32] } ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u64)), true, None, ).0; assert!(r.is_ok()); let v = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), true, None, ).0.unwrap(); let r = ApplyResult::decode(&mut &v.as_encoded()[..]).unwrap(); assert_eq!(r, Err(ApplyError::CantPay)); } #[test] fn bad_extrinsic_with_native_equivalent_code_gives_error() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { 69_u128.encode() }, twox_128(>::key()).to_vec() => { 69_u128.encode() }, twox_128(>::key()).to_vec() => { 0_u128.encode() }, blake2_256(&>::key_for(0)).to_vec() => { vec![0u8; 32] } ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u64)), true, None, ).0; assert!(r.is_ok()); let v = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), true, None, ).0.unwrap(); let r = ApplyResult::decode(&mut &v.as_encoded()[..]).unwrap(); assert_eq!(r, Err(ApplyError::CantPay)); } #[test] fn successful_execution_with_native_equivalent_code_gives_ok() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { (111 * DOLLARS).encode() }, twox_128(>::key()).to_vec() => { (111 * DOLLARS).encode() }, twox_128(>::key()).to_vec() => vec![0u8; 16], blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u64)), true, None, ).0; assert!(r.is_ok()); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), true, None, ).0; assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } #[test] fn successful_execution_with_foreign_code_gives_ok() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { (111 * DOLLARS).encode() }, twox_128(>::key()).to_vec() => { (111 * DOLLARS).encode() }, twox_128(>::key()).to_vec() => vec![0u8; 16], blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u64)), true, None, ).0; assert!(r.is_ok()); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()), true, None, ).0; assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } fn to_session_keys( ed25519_keyring: &Ed25519Keyring, sr25519_keyring: &Sr25519Keyring, ) -> SessionKeys { SessionKeys { ed25519: ed25519_keyring.to_owned().into(), sr25519: sr25519_keyring.to_owned().into(), } } fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalities { let mut ext = TestExternalities::new_with_code_with_children(code, GenesisConfig { babe: Some(Default::default()), system: Some(SystemConfig { changes_trie_config: if support_changes_trie { Some(ChangesTrieConfiguration { digest_interval: 2, digest_levels: 2, }) } else { None }, ..Default::default() }), indices: Some(IndicesConfig { ids: vec![alice(), bob(), charlie(), dave(), eve(), ferdie()], }), balances: Some(BalancesConfig { balances: vec![ (alice(), 111 * DOLLARS), (bob(), 100 * DOLLARS), (charlie(), 100_000_000 * DOLLARS), (dave(), 111 * DOLLARS), (eve(), 101 * DOLLARS), (ferdie(), 100 * DOLLARS), ], vesting: vec![], }), session: Some(SessionConfig { keys: vec![ (alice(), to_session_keys( &Ed25519Keyring::Alice, &Sr25519Keyring::Alice, )), (bob(), to_session_keys( &Ed25519Keyring::Bob, &Sr25519Keyring::Bob, )), (charlie(), to_session_keys( &Ed25519Keyring::Charlie, &Sr25519Keyring::Charlie, )), ] }), staking: Some(StakingConfig { current_era: 0, stakers: vec![ (dave(), alice(), 111 * DOLLARS, staking::StakerStatus::Validator), (eve(), bob(), 100 * DOLLARS, staking::StakerStatus::Validator), (ferdie(), charlie(), 100 * DOLLARS, staking::StakerStatus::Validator) ], validator_count: 3, minimum_validator_count: 0, offline_slash: Perbill::zero(), offline_slash_grace: 0, invulnerables: vec![alice(), bob(), charlie()], }), democracy: Some(Default::default()), collective_Instance1: Some(Default::default()), collective_Instance2: Some(Default::default()), elections: Some(Default::default()), contracts: Some(ContractsConfig { current_schedule: Default::default(), gas_price: 1 * MILLICENTS, }), sudo: Some(Default::default()), im_online: Some(Default::default()), grandpa: Some(GrandpaConfig { authorities: vec![], }), }.build_storage().unwrap()); ext.changes_trie_storage().insert(0, GENESIS_HASH.into(), Default::default()); ext } fn construct_block( env: &mut TestExternalities, number: BlockNumber, parent_hash: Hash, extrinsics: Vec, ) -> (Vec, Hash) { use trie::ordered_trie_root; // sign extrinsics. let extrinsics = extrinsics.into_iter().map(sign).collect::>(); // calculate the header fields that we can. let extrinsics_root = ordered_trie_root::( extrinsics.iter().map(Encode::encode) ).to_fixed_bytes() .into(); let header = Header { parent_hash, number, extrinsics_root, state_root: Default::default(), digest: Default::default(), }; // execute the block to get the real header. Executor::new(None).call::<_, NeverNativeValue, fn() -> _>( env, "Core_initialize_block", &header.encode(), true, None, ).0.unwrap(); for i in extrinsics.iter() { Executor::new(None).call::<_, NeverNativeValue, fn() -> _>( env, "BlockBuilder_apply_extrinsic", &i.encode(), true, None, ).0.unwrap(); } let header = match Executor::new(None).call::<_, NeverNativeValue, fn() -> _>( env, "BlockBuilder_finalize_block", &[0u8;0], true, None, ).0.unwrap() { NativeOrEncoded::Native(_) => unreachable!(), NativeOrEncoded::Encoded(h) => Header::decode(&mut &h[..]).unwrap(), }; let hash = header.blake2_256(); (Block { header, extrinsics }.encode(), hash.into()) } fn changes_trie_block() -> (Vec, Hash) { construct_block( &mut new_test_ext(COMPACT_CODE, true), 1, GENESIS_HASH.into(), vec![ CheckedExtrinsic { signed: None, function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { signed: Some((alice(), signed_extra(0, 0))), function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), }, ] ) } // block 1 and 2 must be created together to ensure transactions are only signed once (since they // are not guaranteed to be deterministic) and to ensure that the correct state is propagated // from block1's execution to block2 to derive the correct storage_root. fn blocks() -> ((Vec, Hash), (Vec, Hash)) { let mut t = new_test_ext(COMPACT_CODE, false); let block1 = construct_block( &mut t, 1, GENESIS_HASH.into(), vec![ CheckedExtrinsic { signed: None, function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { signed: Some((alice(), signed_extra(0, 0))), function: Call::Balances(balances::Call::transfer(bob().into(), 69 * DOLLARS)), }, ] ); let block2 = construct_block( &mut t, 2, block1.1.clone(), vec![ CheckedExtrinsic { signed: None, function: Call::Timestamp(timestamp::Call::set(52)), }, CheckedExtrinsic { signed: Some((bob(), signed_extra(0, 0))), function: Call::Balances(balances::Call::transfer(alice().into(), 5 * DOLLARS)), }, CheckedExtrinsic { signed: Some((alice(), signed_extra(1, 0))), function: Call::Balances(balances::Call::transfer(bob().into(), 15 * DOLLARS)), } ] ); // session change => consensus authorities change => authorities change digest item appears let digest = Header::decode(&mut &block2.0[..]).unwrap().digest; assert_eq!(digest.logs().len(), 0); (block1, block2) } fn block_with_size(time: u64, nonce: u64, size: usize) -> (Vec, Hash) { construct_block( &mut new_test_ext(COMPACT_CODE, false), 1, GENESIS_HASH.into(), vec![ CheckedExtrinsic { signed: None, function: Call::Timestamp(timestamp::Call::set(time)), }, CheckedExtrinsic { signed: Some((alice(), signed_extra(nonce, 0))), function: Call::System(system::Call::remark(vec![0; size])), } ] ) } #[test] fn full_native_block_import_works() { let mut t = new_test_ext(COMPACT_CODE, false); let (block1, block2) = blocks(); executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", &block1.0, true, None, ).0.unwrap(); runtime_io::with_externalities(&mut t, || { assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), event: Event::system(system::Event::ExtrinsicSuccess), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), event: Event::balances(balances::RawEvent::Transfer( alice().into(), bob().into(), 69 * DOLLARS, 1 * CENTS )), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), event: Event::system(system::Event::ExtrinsicSuccess), topics: vec![], }, ]; assert_eq!(System::events(), events); }); executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", &block2.0, true, None, ).0.unwrap(); runtime_io::with_externalities(&mut t, || { // TODO TODO: this needs investigating: why are we deducting creation fee twice here? and why bob also pays it? assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(&xt()) - 2 * creation_fee()); assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - transfer_fee(&xt()) - creation_fee()); let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), event: Event::system(system::Event::ExtrinsicSuccess), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), event: Event::balances( balances::RawEvent::Transfer( bob().into(), alice().into(), 5 * DOLLARS, 1 * CENTS, ) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), event: Event::system(system::Event::ExtrinsicSuccess), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), event: Event::balances( balances::RawEvent::Transfer( alice().into(), bob().into(), 15 * DOLLARS, 1 * CENTS, ) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), event: Event::system(system::Event::ExtrinsicSuccess), topics: vec![], }, ]; assert_eq!(System::events(), events); }); } #[test] fn full_wasm_block_import_works() { let mut t = new_test_ext(COMPACT_CODE, false); let (block1, block2) = blocks(); WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block1.0).unwrap(); runtime_io::with_externalities(&mut t, || { assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); }); WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block2.0).unwrap(); runtime_io::with_externalities(&mut t, || { assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * transfer_fee(&xt()) - 2 * creation_fee()); assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * transfer_fee(&xt()) - creation_fee()); }); } const CODE_TRANSFER: &str = r#" (module ;; ext_call( ;; callee_ptr: u32, ;; callee_len: u32, ;; gas: u64, ;; value_ptr: u32, ;; value_len: u32, ;; input_data_ptr: u32, ;; input_data_len: u32 ;; ) -> u32 (import "env" "ext_call" (func $ext_call (param i32 i32 i64 i32 i32 i32 i32) (result i32))) (import "env" "ext_scratch_size" (func $ext_scratch_size (result i32))) (import "env" "ext_scratch_copy" (func $ext_scratch_copy (param i32 i32 i32))) (import "env" "memory" (memory 1 1)) (func (export "deploy") ) (func (export "call") (block $fail ;; load and check the input data (which is stored in the scratch buffer). ;; fail if the input size is not != 4 (br_if $fail (i32.ne (i32.const 4) (call $ext_scratch_size) ) ) (call $ext_scratch_copy (i32.const 0) (i32.const 0) (i32.const 4) ) (br_if $fail (i32.ne (i32.load8_u (i32.const 0)) (i32.const 0) ) ) (br_if $fail (i32.ne (i32.load8_u (i32.const 1)) (i32.const 1) ) ) (br_if $fail (i32.ne (i32.load8_u (i32.const 2)) (i32.const 2) ) ) (br_if $fail (i32.ne (i32.load8_u (i32.const 3)) (i32.const 3) ) ) (drop (call $ext_call (i32.const 4) ;; Pointer to "callee" address. (i32.const 32) ;; Length of "callee" address. (i64.const 0) ;; How much gas to devote for the execution. 0 = all. (i32.const 36) ;; Pointer to the buffer with value to transfer (i32.const 16) ;; Length of the buffer with value to transfer. (i32.const 0) ;; Pointer to input data buffer address (i32.const 0) ;; Length of input data buffer ) ) (return) ) unreachable ) ;; Destination AccountId to transfer the funds. ;; Represented by H256 (32 bytes long) in little endian. (data (i32.const 4) "\09\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" "\00\00\00\00" ) ;; Amount of value to transfer. ;; Represented by u128 (16 bytes long) in little endian. (data (i32.const 36) "\06\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" "\00\00" ) ) "#; #[test] fn deploying_wasm_contract_should_work() { let transfer_code = wabt::wat2wasm(CODE_TRANSFER).unwrap(); let transfer_ch = ::Hashing::hash(&transfer_code); let addr = ::DetermineContractAddress::contract_address_for( &transfer_ch, &[], &charlie(), ); let b = construct_block( &mut new_test_ext(COMPACT_CODE, false), 1, GENESIS_HASH.into(), vec![ CheckedExtrinsic { signed: None, function: Call::Timestamp(timestamp::Call::set(42)), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(0, 0))), function: Call::Contracts( contracts::Call::put_code::(10_000, transfer_code) ), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(1, 0))), function: Call::Contracts( contracts::Call::create::(1 * DOLLARS, 10_000, transfer_ch, Vec::new()) ), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(2, 0))), function: Call::Contracts( contracts::Call::call::( indices::address::Address::Id(addr.clone()), 10, 10_000, vec![0x00, 0x01, 0x02, 0x03] ) ), }, ] ); let mut t = new_test_ext(COMPACT_CODE, false); WasmExecutor::new().call(&mut t, 8, COMPACT_CODE,"Core_execute_block", &b.0).unwrap(); runtime_io::with_externalities(&mut t, || { // Verify that the contract constructor worked well and code of TRANSFER contract is actually deployed. assert_eq!( &contracts::ContractInfoOf::::get(addr) .and_then(|c| c.get_alive()) .unwrap() .code_hash, &transfer_ch ); }); } #[test] fn wasm_big_block_import_fails() { let mut t = new_test_ext(COMPACT_CODE, false); assert!( WasmExecutor::new().call( &mut t, 4, COMPACT_CODE, "Core_execute_block", &block_with_size(42, 0, 120_000).0 ).is_err() ); } #[test] fn native_big_block_import_succeeds() { let mut t = new_test_ext(COMPACT_CODE, false); Executor::new(None).call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", &block_with_size(42, 0, 120_000).0, true, None, ).0.unwrap(); } #[test] fn native_big_block_import_fails_on_fallback() { let mut t = new_test_ext(COMPACT_CODE, false); assert!( Executor::new(None).call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", &block_with_size(42, 0, 120_000).0, false, None, ).0.is_err() ); } #[test] fn panic_execution_gives_error() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { 0_u128.encode() }, twox_128(>::key()).to_vec() => { 0_u128.encode() }, twox_128(>::key()).to_vec() => vec![0u8; 16], blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32] ]); let r = WasmExecutor::new() .call(&mut t, 8, COMPACT_CODE, "Core_initialize_block", &vec![].and(&from_block_number(1u64))); assert!(r.is_ok()); let r = WasmExecutor::new() .call(&mut t, 8, COMPACT_CODE, "BlockBuilder_apply_extrinsic", &vec![].and(&xt())).unwrap(); let r = ApplyResult::decode(&mut &r[..]).unwrap(); assert_eq!(r, Err(ApplyError::CantPay)); } #[test] fn successful_execution_gives_ok() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, map![ blake2_256(&>::key_for(alice())).to_vec() => { (111 * DOLLARS).encode() }, twox_128(>::key()).to_vec() => { (111 * DOLLARS).encode() }, twox_128(>::key()).to_vec() => vec![0u8; 16], blake2_256(&>::key_for(0)).to_vec() => vec![0u8; 32] ]); let r = WasmExecutor::new() .call(&mut t, 8, COMPACT_CODE, "Core_initialize_block", &vec![].and(&from_block_number(1u64))); assert!(r.is_ok()); let r = WasmExecutor::new() .call(&mut t, 8, COMPACT_CODE, "BlockBuilder_apply_extrinsic", &vec![].and(&xt())).unwrap(); let r = ApplyResult::decode(&mut &r[..]).unwrap(); assert_eq!(r, Ok(ApplyOutcome::Success)); runtime_io::with_externalities(&mut t, || { assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt()) - creation_fee()); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } #[test] fn full_native_block_import_works_with_changes_trie() { let block1 = changes_trie_block(); let block_data = block1.0; let block = Block::decode(&mut &block_data[..]).unwrap(); let mut t = new_test_ext(COMPACT_CODE, true); Executor::new(None).call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", &block.encode(), true, None, ).0.unwrap(); assert!(t.storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); } #[test] fn full_wasm_block_import_works_with_changes_trie() { let block1 = changes_trie_block(); let mut t = new_test_ext(COMPACT_CODE, true); WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block1.0).unwrap(); assert!(t.storage_changes_root(GENESIS_HASH.into()).unwrap().is_some()); } #[test] fn should_import_block_with_test_client() { use test_client::{ClientExt, TestClientBuilder, consensus::BlockOrigin}; let client = TestClientBuilder::default() .build_with_native_executor::(executor()) .0; let block1 = changes_trie_block(); let block_data = block1.0; let block = Block::decode(&mut &block_data[..]).unwrap(); client.import(BlockOrigin::Own, block).unwrap(); } #[test] #[ignore] fn weight_multiplier_increases_and_decreases_on_big_weight() { let mut t = new_test_ext(COMPACT_CODE, false); let mut prev_multiplier = WeightMultiplier::default(); runtime_io::with_externalities(&mut t, || { assert_eq!(System::next_weight_multiplier(), prev_multiplier); }); let mut tt = new_test_ext(COMPACT_CODE, false); // NOTE: This assumes that system::remark has the default. let num_to_exhaust = multiplier_ideal() * 2 / SimpleDispatchInfo::default().weigh_data(()); println!("++ Generating {} transactions to fill {} weight units", num_to_exhaust, multiplier_ideal() * 2); let mut xts = (0..num_to_exhaust).map(|i| CheckedExtrinsic { signed: Some((charlie(), signed_extra(i.into(), 0))), function: Call::System(system::Call::remark(vec![0; 1])), }).collect::>(); xts.insert(0, CheckedExtrinsic { signed: None, function: Call::Timestamp(timestamp::Call::set(42)), }); // big one in terms of weight. let block1 = construct_block( &mut tt, 1, GENESIS_HASH.into(), xts ); // small one in terms of weight. let block2 = construct_block( &mut tt, 2, block1.1.clone(), vec![ CheckedExtrinsic { signed: None, function: Call::Timestamp(timestamp::Call::set(52)), }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(num_to_exhaust.into(), 0))), function: Call::System(system::Call::remark(vec![0; 1])), } ] ); // execute a big block. executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", &block1.0, true, None, ).0.unwrap(); // weight multiplier is increased for next block. runtime_io::with_externalities(&mut t, || { let fm = System::next_weight_multiplier(); println!("After a big block: {:?} -> {:?}", prev_multiplier, fm); assert!(fm > prev_multiplier); prev_multiplier = fm; }); // execute a big block. executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", &block2.0, true, None, ).0.unwrap(); // weight multiplier is increased for next block. runtime_io::with_externalities(&mut t, || { let fm = System::next_weight_multiplier(); println!("After a small block: {:?} -> {:?}", prev_multiplier, fm); assert!(fm < prev_multiplier); }); } #[cfg(feature = "benchmarks")] mod benches { use super::*; use test::Bencher; #[bench] fn wasm_execute_block(b: &mut Bencher) { let (block1, block2) = blocks(); b.iter(|| { let mut t = new_test_ext(COMPACT_CODE, false); WasmExecutor::new().call(&mut t, "Core_execute_block", &block1.0).unwrap(); WasmExecutor::new().call(&mut t, "Core_execute_block", &block2.0).unwrap(); }); } } }