// Copyright 2018 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 . // TODO: #1417 Add more integration tests // also remove the #![allow(unused)] below. #![allow(unused)] use runtime_io::with_externalities; use runtime_primitives::testing::{Digest, DigestItem, H256, Header, UintAuthorityId}; use runtime_primitives::traits::{BlakeTwo256, IdentityLookup}; use runtime_primitives::BuildStorage; use runtime_io; use srml_support::{StorageMap, StorageDoubleMap, assert_ok, impl_outer_event, impl_outer_dispatch, impl_outer_origin}; use substrate_primitives::{Blake2Hasher}; use system::{self, Phase, EventRecord}; use fees; use {wabt, balances, consensus}; use hex_literal::*; use assert_matches::assert_matches; use crate::{ ContractAddressFor, GenesisConfig, Module, RawEvent, StorageOf, Trait, ComputeDispatchFee }; mod contract { // Re-export contents of the root. This basically // needs to give a name for the current crate. // This hack is required for `impl_outer_event!`. pub use super::super::*; use srml_support::impl_outer_event; } impl_outer_event! { pub enum MetaEvent for Test { balances, contract, fees, } } impl_outer_origin! { pub enum Origin for Test { } } impl_outer_dispatch! { pub enum Call for Test where origin: Origin { balances::Balances, contract::Contract, } } #[derive(Clone, Eq, PartialEq)] pub struct Test; impl system::Trait for Test { type Origin = Origin; type Index = u64; type BlockNumber = u64; type Hash = H256; type Hashing = BlakeTwo256; type Digest = Digest; type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; type Event = MetaEvent; type Log = DigestItem; } impl balances::Trait for Test { type Balance = u64; type OnFreeBalanceZero = Contract; type OnNewAccount = (); type EnsureAccountLiquid = (); type Event = MetaEvent; } impl timestamp::Trait for Test { type Moment = u64; type OnTimestampSet = (); } impl consensus::Trait for Test { type Log = DigestItem; type SessionKey = UintAuthorityId; type InherentOfflineReport = (); } impl fees::Trait for Test { type Event = MetaEvent; type TransferAsset = Balances; } impl Trait for Test { type Call = Call; type Gas = u64; type DetermineContractAddress = DummyContractAddressFor; type Event = MetaEvent; type ComputeDispatchFee = DummyComputeDispatchFee; } type Balances = balances::Module; type Contract = Module; type System = system::Module; pub struct DummyContractAddressFor; impl ContractAddressFor for DummyContractAddressFor { fn contract_address_for(_code_hash: &H256, _data: &[u8], origin: &u64) -> u64 { *origin + 1 } } pub struct DummyComputeDispatchFee; impl ComputeDispatchFee for DummyComputeDispatchFee { fn compute_dispatch_fee(call: &Call) -> u64 { 69 } } const ALICE: u64 = 1; const BOB: u64 = 2; const CHARLIE: u64 = 3; pub struct ExtBuilder { existential_deposit: u64, gas_price: u64, block_gas_limit: u64, transfer_fee: u64, creation_fee: u64, } impl Default for ExtBuilder { fn default() -> Self { Self { existential_deposit: 0, gas_price: 2, block_gas_limit: 100_000_000, transfer_fee: 0, creation_fee: 0, } } } impl ExtBuilder { pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { self.existential_deposit = existential_deposit; self } pub fn gas_price(mut self, gas_price: u64) -> Self { self.gas_price = gas_price; self } pub fn block_gas_limit(mut self, block_gas_limit: u64) -> Self { self.block_gas_limit = block_gas_limit; self } pub fn transfer_fee(mut self, transfer_fee: u64) -> Self { self.transfer_fee = transfer_fee; self } pub fn creation_fee(mut self, creation_fee: u64) -> Self { self.creation_fee = creation_fee; self } pub fn build(self) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::::default() .build_storage() .unwrap() .0; t.extend( balances::GenesisConfig:: { balances: vec![], existential_deposit: self.existential_deposit, transfer_fee: self.transfer_fee, creation_fee: self.creation_fee, vesting: vec![], } .build_storage() .unwrap() .0, ); t.extend( GenesisConfig:: { contract_fee: 21, call_base_fee: 135, create_base_fee: 175, gas_price: self.gas_price, max_depth: 100, block_gas_limit: self.block_gas_limit, current_schedule: Default::default(), } .build_storage() .unwrap() .0, ); runtime_io::TestExternalities::new(t) } } #[test] fn refunds_unused_gas() { with_externalities(&mut ExtBuilder::default().build(), || { Balances::set_free_balance(&0, 100_000_000); Balances::increase_total_stake_by(100_000_000); assert_ok!(Contract::call( Origin::signed(0), 1, 0, 100_000, Vec::new() )); assert_eq!(Balances::free_balance(&0), 100_000_000 - (2 * 135)); }); } #[test] fn account_removal_removes_storage() { with_externalities( &mut ExtBuilder::default().existential_deposit(100).build(), || { // Setup two accounts with free balance above than exsistential threshold. { Balances::set_free_balance(&1, 110); Balances::increase_total_stake_by(110); >::insert(1, b"foo".to_vec(), b"1".to_vec()); >::insert(1, b"bar".to_vec(), b"2".to_vec()); Balances::set_free_balance(&2, 110); Balances::increase_total_stake_by(110); >::insert(2, b"hello".to_vec(), b"3".to_vec()); >::insert(2, b"world".to_vec(), b"4".to_vec()); } // Transfer funds from account 1 of such amount that after this transfer // the balance of account 1 is will be below than exsistential threshold. // // This should lead to the removal of all storage associated with this account. assert_ok!(Balances::transfer(Origin::signed(1), 2, 20)); // Verify that all entries from account 1 is removed, while // entries from account 2 is in place. { assert_eq!(>::get(1, b"foo".to_vec()), None); assert_eq!(>::get(1, b"bar".to_vec()), None); assert_eq!( >::get(2, b"hello".to_vec()), Some(b"3".to_vec()) ); assert_eq!( >::get(2, b"world".to_vec()), Some(b"4".to_vec()) ); } }, ); } const CODE_RETURN_FROM_START_FN: &str = r#" (module (import "env" "ext_return" (func $ext_return (param i32 i32))) (import "env" "memory" (memory 1 1)) (start $start) (func $start (call $ext_return (i32.const 8) (i32.const 4) ) (unreachable) ) (func (export "call") (unreachable) ) (func (export "deploy")) (data (i32.const 8) "\01\02\03\04") ) "#; const HASH_RETURN_FROM_START_FN: [u8; 32] = hex!("e6411d12daa2a19e4e9c7d8306c31c7d53a352cb8ed84385c8a1d48fc232e708"); #[test] fn instantiate_and_call() { let wasm = wabt::wat2wasm(CODE_RETURN_FROM_START_FN).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(100).build(), || { Balances::set_free_balance(&ALICE, 1_000_000); Balances::increase_total_stake_by(1_000_000); assert_ok!(Contract::put_code( Origin::signed(ALICE), 100_000, wasm, )); assert_ok!(Contract::create( Origin::signed(ALICE), 100, 100_000, HASH_RETURN_FROM_START_FN.into(), vec![], )); assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_RETURN_FROM_START_FN.into())), }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( balances::RawEvent::NewAccount(BOB, 100) ) }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)) }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)) } ]); }, ); } const CODE_DISPATCH_CALL: &str = r#" (module (import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32))) (import "env" "memory" (memory 1 1)) (func (export "call") (call $ext_dispatch_call (i32.const 8) ;; Pointer to the start of encoded call buffer (i32.const 11) ;; Length of the buffer ) ) (func (export "deploy")) (data (i32.const 8) "\00\00\03\00\00\00\00\00\00\00\C8") ) "#; const HASH_DISPATCH_CALL: [u8; 32] = hex!("49dfdcaf9c1553be10634467e95b8e71a3bc15a4f8bf5563c0312b0902e0afb9"); #[test] fn dispatch_call() { // This test can fail due to the encoding changes. In case it becomes too annoying // let's rewrite so as we use this module controlled call or we serialize it in runtime. let encoded = parity_codec::Encode::encode(&Call::Balances(balances::Call::transfer(CHARLIE, 50))); assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]); let wasm = wabt::wat2wasm(CODE_DISPATCH_CALL).unwrap(); with_externalities( &mut ExtBuilder::default().existential_deposit(50).build(), || { Balances::set_free_balance(&ALICE, 1_000_000); Balances::increase_total_stake_by(1_000_000); assert_ok!(Contract::put_code( Origin::signed(ALICE), 100_000, wasm, )); // Let's keep this assert even though it's redundant. If you ever need to update the // wasm source this test will fail and will show you the actual hash. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())), }, ]); assert_ok!(Contract::create( Origin::signed(ALICE), 100, 100_000, HASH_DISPATCH_CALL.into(), vec![], )); assert_ok!(Contract::call( Origin::signed(ALICE), BOB, // newly created account 0, 100_000, vec![], )); assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())), }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( balances::RawEvent::NewAccount(BOB, 100) ) }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)) }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)) }, // Dispatching the call. EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( balances::RawEvent::NewAccount(CHARLIE, 50) ) }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( balances::RawEvent::Transfer(BOB, CHARLIE, 50, 0) ) }, // Event emited as a result of dispatch. EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::Dispatched(BOB, true)) } ]); }, ); }