mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-01 18:17:56 +00:00
Contracts: Per block gas limit (#506)
* Add block gas limit check * Fix formatting * Add block_gas_limit test. * Use ExtBuilder in tests * Docs and clean-up. * Correct style
This commit is contained in:
committed by
Gav Wood
parent
3e63009ac7
commit
20655af97b
@@ -14,8 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use {Trait, Module};
|
||||
use {Trait, Module, GasSpent};
|
||||
use runtime_primitives::traits::{As, CheckedMul, CheckedSub, Zero};
|
||||
use runtime_support::StorageValue;
|
||||
use staking;
|
||||
|
||||
#[must_use]
|
||||
@@ -35,6 +36,8 @@ impl GasMeterResult {
|
||||
}
|
||||
|
||||
pub struct GasMeter<T: Trait> {
|
||||
limit: T::Gas,
|
||||
/// Amount of gas left from initial gas limit. Can reach zero.
|
||||
gas_left: T::Gas,
|
||||
gas_price: T::Balance,
|
||||
}
|
||||
@@ -42,6 +45,7 @@ impl<T: Trait> GasMeter<T> {
|
||||
#[cfg(test)]
|
||||
pub fn with_limit(gas_limit: T::Gas, gas_price: T::Balance) -> GasMeter<T> {
|
||||
GasMeter {
|
||||
limit: gas_limit,
|
||||
gas_left: gas_limit,
|
||||
gas_price,
|
||||
}
|
||||
@@ -101,6 +105,7 @@ impl<T: Trait> GasMeter<T> {
|
||||
} else {
|
||||
self.gas_left = self.gas_left - amount;
|
||||
let mut nested = GasMeter {
|
||||
limit: amount,
|
||||
gas_left: amount,
|
||||
gas_price: self.gas_price,
|
||||
};
|
||||
@@ -117,6 +122,11 @@ impl<T: Trait> GasMeter<T> {
|
||||
pub fn gas_left(&self) -> T::Gas {
|
||||
self.gas_left
|
||||
}
|
||||
|
||||
/// Returns how much gas was spent.
|
||||
fn spent(&self) -> T::Gas {
|
||||
self.limit - self.gas_left
|
||||
}
|
||||
}
|
||||
|
||||
/// Buy the given amount of gas.
|
||||
@@ -127,6 +137,14 @@ pub fn buy_gas<T: Trait>(
|
||||
transactor: &T::AccountId,
|
||||
gas_limit: T::Gas,
|
||||
) -> Result<GasMeter<T>, &'static str> {
|
||||
// Check if the specified amount of gas is available in the current block.
|
||||
// This cannot underflow since `gas_spent` is never greater than `block_gas_limit`.
|
||||
let gas_available = <Module<T>>::block_gas_limit() - <Module<T>>::gas_spent();
|
||||
if gas_limit > gas_available {
|
||||
return Err("block gas limit is reached");
|
||||
}
|
||||
|
||||
// Buy the specified amount of gas.
|
||||
let gas_price = <Module<T>>::gas_price();
|
||||
let b = <staking::Module<T>>::free_balance(transactor);
|
||||
let cost = <T::Gas as As<T::Balance>>::as_(gas_limit.clone())
|
||||
@@ -138,6 +156,7 @@ pub fn buy_gas<T: Trait>(
|
||||
<staking::Module<T>>::set_free_balance(transactor, b - cost);
|
||||
<staking::Module<T>>::decrease_total_stake_by(cost);
|
||||
Ok(GasMeter {
|
||||
limit: gas_limit,
|
||||
gas_left: gas_limit,
|
||||
gas_price,
|
||||
})
|
||||
@@ -145,6 +164,13 @@ pub fn buy_gas<T: Trait>(
|
||||
|
||||
/// Refund the unused gas.
|
||||
pub fn refund_unused_gas<T: Trait>(transactor: &T::AccountId, gas_meter: GasMeter<T>) {
|
||||
// Increase total spent gas.
|
||||
// This cannot overflow, since `gas_spent` is never greater than `block_gas_limit`, which
|
||||
// also has T::Gas type.
|
||||
let gas_spent = <Module<T>>::gas_spent() + gas_meter.spent();
|
||||
<GasSpent<T>>::put(gas_spent);
|
||||
|
||||
// Refund gas left by the price it was bought.
|
||||
let b = <staking::Module<T>>::free_balance(transactor);
|
||||
let refund = <T::Gas as As<T::Balance>>::as_(gas_meter.gas_left) * gas_meter.gas_price;
|
||||
<staking::Module<T>>::set_free_balance(transactor, b + refund);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
//! Build the contract module part of the genesis block storage.
|
||||
|
||||
use {Trait, ContractFee, CallBaseFee, CreateBaseFee, GasPrice, MaxDepth};
|
||||
use {Trait, ContractFee, CallBaseFee, CreateBaseFee, GasPrice, MaxDepth, BlockGasLimit};
|
||||
|
||||
use runtime_primitives;
|
||||
use runtime_io::{self, twox_128};
|
||||
@@ -34,6 +34,7 @@ pub struct GenesisConfig<T: Trait> {
|
||||
pub create_base_fee: T::Gas,
|
||||
pub gas_price: T::Balance,
|
||||
pub max_depth: u32,
|
||||
pub block_gas_limit: T::Gas,
|
||||
}
|
||||
|
||||
impl<T: Trait> runtime_primitives::BuildStorage for GenesisConfig<T> {
|
||||
@@ -43,7 +44,8 @@ impl<T: Trait> runtime_primitives::BuildStorage for GenesisConfig<T> {
|
||||
twox_128(<CallBaseFee<T>>::key()).to_vec() => self.call_base_fee.encode(),
|
||||
twox_128(<CreateBaseFee<T>>::key()).to_vec() => self.create_base_fee.encode(),
|
||||
twox_128(<GasPrice<T>>::key()).to_vec() => self.gas_price.encode(),
|
||||
twox_128(<MaxDepth<T>>::key()).to_vec() => self.max_depth.encode()
|
||||
twox_128(<MaxDepth<T>>::key()).to_vec() => self.max_depth.encode(),
|
||||
twox_128(<BlockGasLimit<T>>::key()).to_vec() => self.block_gas_limit.encode()
|
||||
];
|
||||
Ok(r.into())
|
||||
}
|
||||
|
||||
@@ -25,13 +25,31 @@
|
||||
//! create smart-contracts or send messages to existing smart-contracts.
|
||||
//!
|
||||
//! For any actions invoked by the smart-contracts fee must be paid. The fee is paid in gas.
|
||||
//! Gas is bought upfront. Any unused is refunded after the transaction (regardless of the
|
||||
//! execution outcome). If all gas is used, then changes made for the specific call or create
|
||||
//! are reverted (including balance transfers).
|
||||
//! Gas is bought upfront up to the, specified in transaction, limit. Any unused gas is refunded
|
||||
//! after the transaction (regardless of the execution outcome). If all gas is used,
|
||||
//! then changes made for the specific call or create are reverted (including balance transfers).
|
||||
//!
|
||||
//! Failures are typically not cascading. That, for example, means that if contract A calls B and B errors
|
||||
//! somehow, then A can decide if it should proceed or error.
|
||||
//! TODO: That is not the case now, since call/create externalities traps on any error now.
|
||||
//!
|
||||
//! # Interaction with the system
|
||||
//!
|
||||
//! ## Finalization
|
||||
//!
|
||||
//! This module requires performing some finalization steps at the end of the block. If not performed
|
||||
//! the module will have incorrect behavior.
|
||||
//!
|
||||
//! Call [`Module::execute`] at the end of the block. The order in relation to
|
||||
//! the other module doesn't matter.
|
||||
//!
|
||||
//! ## Account killing
|
||||
//!
|
||||
//! When `staking` module determines that account is dead (e.g. account's balance fell below
|
||||
//! exsistential deposit) then it reaps the account. That will lead to deletion of the associated
|
||||
//! code and storage of the account.
|
||||
//!
|
||||
//! [`Module::execute`]: struct.Module.html#impl-Executable
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
@@ -90,16 +108,16 @@ use account_db::{AccountDb, OverlayAccountDb};
|
||||
use double_map::StorageDoubleMap;
|
||||
|
||||
use codec::Codec;
|
||||
use runtime_primitives::traits::{As, RefInto, SimpleArithmetic};
|
||||
use runtime_primitives::traits::{As, RefInto, SimpleArithmetic, Executable};
|
||||
use runtime_support::dispatch::Result;
|
||||
use runtime_support::{Parameter, StorageMap};
|
||||
use runtime_support::{Parameter, StorageMap, StorageValue};
|
||||
|
||||
pub trait Trait: system::Trait + staking::Trait + consensus::Trait {
|
||||
/// Function type to get the contract address given the creator.
|
||||
type DetermineContractAddress: ContractAddressFor<Self::AccountId>;
|
||||
|
||||
// As<u32> is needed for wasm-utils
|
||||
type Gas: Parameter + Codec + SimpleArithmetic + Copy + As<Self::Balance> + As<u64> + As<u32>;
|
||||
type Gas: Parameter + Default + Codec + SimpleArithmetic + Copy + As<Self::Balance> + As<u64> + As<u32>;
|
||||
}
|
||||
|
||||
pub trait ContractAddressFor<AccountId: Sized> {
|
||||
@@ -144,9 +162,13 @@ decl_storage! {
|
||||
GasPrice get(gas_price): b"con:gas_price" => required T::Balance;
|
||||
// The maximum nesting level of a call/create stack.
|
||||
MaxDepth get(max_depth): b"con:max_depth" => required u32;
|
||||
// The maximum amount of gas that could be expended per block.
|
||||
BlockGasLimit get(block_gas_limit): b"con:block_gas_limit" => required T::Gas;
|
||||
// Gas spent so far in this block.
|
||||
GasSpent get(gas_spent): b"con:gas_spent" => default T::Gas;
|
||||
|
||||
// The code associated with an account.
|
||||
pub CodeOf: b"con:cod:" => default map [ T::AccountId => Vec<u8> ]; // TODO Vec<u8> values should be optimised to not do a length prefix.
|
||||
CodeOf: b"con:cod:" => default map [ T::AccountId => Vec<u8> ]; // TODO Vec<u8> values should be optimised to not do a length prefix.
|
||||
}
|
||||
|
||||
// TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime
|
||||
@@ -253,3 +275,10 @@ impl<T: Trait> staking::OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
<StorageOf<T>>::remove_prefix(who.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Finalization hook for the smart-contract module.
|
||||
impl<T: Trait> Executable for Module<T> {
|
||||
fn execute() {
|
||||
<GasSpent<T>>::kill();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,61 +77,90 @@ impl ContractAddressFor<u64> for DummyContractAddressFor {
|
||||
}
|
||||
}
|
||||
|
||||
fn new_test_ext(existential_deposit: u64, gas_price: u64) -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default()
|
||||
.build_storage()
|
||||
.unwrap();
|
||||
t.extend(
|
||||
consensus::GenesisConfig::<Test> {
|
||||
code: vec![],
|
||||
authorities: vec![],
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
session::GenesisConfig::<Test> {
|
||||
session_length: 1,
|
||||
validators: vec![10, 20],
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
staking::GenesisConfig::<Test> {
|
||||
sessions_per_era: 1,
|
||||
current_era: 0,
|
||||
balances: vec![],
|
||||
intentions: vec![],
|
||||
validator_count: 2,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 0,
|
||||
transaction_base_fee: 0,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: existential_deposit,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
early_era_slash: 0,
|
||||
session_reward: 0,
|
||||
offline_slash_grace: 0,
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
timestamp::GenesisConfig::<Test>::default()
|
||||
struct ExtBuilder {
|
||||
existential_deposit: u64,
|
||||
gas_price: u64,
|
||||
block_gas_limit: u64,
|
||||
}
|
||||
impl Default for ExtBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
existential_deposit: 0,
|
||||
gas_price: 2,
|
||||
block_gas_limit: 100_000_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ExtBuilder {
|
||||
fn existential_deposit(mut self, existential_deposit: u64) -> Self {
|
||||
self.existential_deposit = existential_deposit;
|
||||
self
|
||||
}
|
||||
fn gas_price(mut self, gas_price: u64) -> Self {
|
||||
self.gas_price = gas_price;
|
||||
self
|
||||
}
|
||||
fn block_gas_limit(mut self, block_gas_limit: u64) -> Self {
|
||||
self.block_gas_limit = block_gas_limit;
|
||||
self
|
||||
}
|
||||
fn build(self) -> runtime_io::TestExternalities<KeccakHasher> {
|
||||
let mut t = system::GenesisConfig::<Test>::default()
|
||||
.build_storage()
|
||||
.unwrap();
|
||||
t.extend(
|
||||
consensus::GenesisConfig::<Test> {
|
||||
code: vec![],
|
||||
authorities: vec![],
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
GenesisConfig::<Test> {
|
||||
contract_fee: 21,
|
||||
call_base_fee: 135,
|
||||
create_base_fee: 175,
|
||||
gas_price,
|
||||
max_depth: 100,
|
||||
}.build_storage()
|
||||
);
|
||||
t.extend(
|
||||
session::GenesisConfig::<Test> {
|
||||
session_length: 1,
|
||||
validators: vec![10, 20],
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.into()
|
||||
);
|
||||
t.extend(
|
||||
staking::GenesisConfig::<Test> {
|
||||
sessions_per_era: 1,
|
||||
current_era: 0,
|
||||
balances: vec![],
|
||||
intentions: vec![],
|
||||
validator_count: 2,
|
||||
minimum_validator_count: 0,
|
||||
bonding_duration: 0,
|
||||
transaction_base_fee: 0,
|
||||
transaction_byte_fee: 0,
|
||||
existential_deposit: self.existential_deposit,
|
||||
transfer_fee: 0,
|
||||
creation_fee: 0,
|
||||
reclaim_rebate: 0,
|
||||
early_era_slash: 0,
|
||||
session_reward: 0,
|
||||
offline_slash_grace: 0,
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
timestamp::GenesisConfig::<Test>::default()
|
||||
.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.extend(
|
||||
GenesisConfig::<Test> {
|
||||
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,
|
||||
}.build_storage()
|
||||
.unwrap(),
|
||||
);
|
||||
t.into()
|
||||
}
|
||||
}
|
||||
|
||||
const CODE_TRANSFER: &str = r#"
|
||||
@@ -163,7 +192,7 @@ fn contract_transfer() {
|
||||
|
||||
let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap();
|
||||
|
||||
with_externalities(&mut new_test_ext(0, 2), || {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(1, code_transfer.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
@@ -198,7 +227,7 @@ fn contract_transfer_oog() {
|
||||
|
||||
let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap();
|
||||
|
||||
with_externalities(&mut new_test_ext(0, 2), || {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(1, code_transfer.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
@@ -219,16 +248,9 @@ fn contract_transfer_oog() {
|
||||
// 2 * 135 - base gas fee for call (by contract)
|
||||
100_000_000 - (2 * 6) - (2 * 135) - (2 * 135),
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::free_balance(&1),
|
||||
11,
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO),
|
||||
0,
|
||||
);
|
||||
assert_eq!(Staking::free_balance(&1), 11);
|
||||
assert_eq!(Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 0);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -237,7 +259,7 @@ fn contract_transfer_max_depth() {
|
||||
|
||||
let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap();
|
||||
|
||||
with_externalities(&mut new_test_ext(0, 2), || {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(CONTRACT_SHOULD_TRANSFER_TO, code_transfer.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
@@ -258,10 +280,7 @@ fn contract_transfer_max_depth() {
|
||||
// 2 * 135 * 100 - base gas fee for call (by transaction) multiplied by max depth (100).
|
||||
100_000_000 - (2 * 135 * 100) - (2 * 6 * 100),
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO),
|
||||
11,
|
||||
);
|
||||
assert_eq!(Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 11);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -340,7 +359,7 @@ fn contract_create() {
|
||||
let code_ctor_transfer = wabt::wat2wasm(&code_ctor(&code_transfer)).unwrap();
|
||||
let code_create = wabt::wat2wasm(&code_create(&code_ctor_transfer)).unwrap();
|
||||
|
||||
with_externalities(&mut new_test_ext(0, 2), || {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
Staking::set_free_balance(&1, 0);
|
||||
@@ -389,7 +408,7 @@ fn top_level_create() {
|
||||
let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap();
|
||||
let code_ctor_transfer = wabt::wat2wasm(&code_ctor(&code_transfer)).unwrap();
|
||||
|
||||
with_externalities(&mut new_test_ext(0, 3), || {
|
||||
with_externalities(&mut ExtBuilder::default().gas_price(3).build(), || {
|
||||
let derived_address = <Test as Trait>::DetermineContractAddress::contract_address_for(
|
||||
&code_ctor_transfer,
|
||||
&0,
|
||||
@@ -434,29 +453,29 @@ const CODE_NOP: &'static str = r#"
|
||||
fn refunds_unused_gas() {
|
||||
let code_nop = wabt::wat2wasm(CODE_NOP).unwrap();
|
||||
|
||||
with_externalities(&mut new_test_ext(0, 2), || {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(1, code_nop.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new(),));
|
||||
assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(Staking::free_balance(&0), 100_000_000 - 4 - (2 * 135),);
|
||||
assert_eq!(Staking::free_balance(&0), 100_000_000 - 4 - (2 * 135));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_with_zero_value() {
|
||||
with_externalities(&mut new_test_ext(0, 2), || {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
<CodeOf<Test>>::insert(1, vec![]);
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new(),));
|
||||
assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new()));
|
||||
|
||||
assert_eq!(Staking::free_balance(&0), 100_000_000 - (2 * 135),);
|
||||
assert_eq!(Staking::free_balance(&0), 100_000_000 - (2 * 135));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -464,11 +483,11 @@ fn call_with_zero_value() {
|
||||
fn create_with_zero_endowment() {
|
||||
let code_nop = wabt::wat2wasm(CODE_NOP).unwrap();
|
||||
|
||||
with_externalities(&mut new_test_ext(0, 2), || {
|
||||
with_externalities(&mut ExtBuilder::default().build(), || {
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
|
||||
assert_ok!(Contract::create(&0, 0, 100_000, code_nop, Vec::new(),));
|
||||
assert_ok!(Contract::create(&0, 0, 100_000, code_nop, Vec::new()));
|
||||
|
||||
assert_eq!(
|
||||
Staking::free_balance(&0),
|
||||
@@ -481,42 +500,45 @@ fn create_with_zero_endowment() {
|
||||
|
||||
#[test]
|
||||
fn account_removal_removes_storage() {
|
||||
with_externalities(&mut new_test_ext(100, 2), || {
|
||||
// Setup two accounts with free balance above than exsistential threshold.
|
||||
{
|
||||
Staking::set_free_balance(&1, 110);
|
||||
Staking::increase_total_stake_by(110);
|
||||
<StorageOf<Test>>::insert(1, b"foo".to_vec(), b"1".to_vec());
|
||||
<StorageOf<Test>>::insert(1, b"bar".to_vec(), b"2".to_vec());
|
||||
with_externalities(
|
||||
&mut ExtBuilder::default().existential_deposit(100).build(),
|
||||
|| {
|
||||
// Setup two accounts with free balance above than exsistential threshold.
|
||||
{
|
||||
Staking::set_free_balance(&1, 110);
|
||||
Staking::increase_total_stake_by(110);
|
||||
<StorageOf<Test>>::insert(1, b"foo".to_vec(), b"1".to_vec());
|
||||
<StorageOf<Test>>::insert(1, b"bar".to_vec(), b"2".to_vec());
|
||||
|
||||
Staking::set_free_balance(&2, 110);
|
||||
Staking::increase_total_stake_by(110);
|
||||
<StorageOf<Test>>::insert(2, b"hello".to_vec(), b"3".to_vec());
|
||||
<StorageOf<Test>>::insert(2, b"world".to_vec(), b"4".to_vec());
|
||||
}
|
||||
Staking::set_free_balance(&2, 110);
|
||||
Staking::increase_total_stake_by(110);
|
||||
<StorageOf<Test>>::insert(2, b"hello".to_vec(), b"3".to_vec());
|
||||
<StorageOf<Test>>::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!(Staking::transfer(&1, 2.into(), 20));
|
||||
// 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!(Staking::transfer(&1, 2.into(), 20));
|
||||
|
||||
// Verify that all entries from account 1 is removed, while
|
||||
// entries from account 2 is in place.
|
||||
{
|
||||
assert_eq!(<StorageOf<Test>>::get(1, b"foo".to_vec()), None);
|
||||
assert_eq!(<StorageOf<Test>>::get(1, b"bar".to_vec()), None);
|
||||
// Verify that all entries from account 1 is removed, while
|
||||
// entries from account 2 is in place.
|
||||
{
|
||||
assert_eq!(<StorageOf<Test>>::get(1, b"foo".to_vec()), None);
|
||||
assert_eq!(<StorageOf<Test>>::get(1, b"bar".to_vec()), None);
|
||||
|
||||
assert_eq!(
|
||||
<StorageOf<Test>>::get(2, b"hello".to_vec()),
|
||||
Some(b"3".to_vec())
|
||||
);
|
||||
assert_eq!(
|
||||
<StorageOf<Test>>::get(2, b"world".to_vec()),
|
||||
Some(b"4".to_vec())
|
||||
);
|
||||
}
|
||||
});
|
||||
assert_eq!(
|
||||
<StorageOf<Test>>::get(2, b"hello".to_vec()),
|
||||
Some(b"3".to_vec())
|
||||
);
|
||||
assert_eq!(
|
||||
<StorageOf<Test>>::get(2, b"world".to_vec()),
|
||||
Some(b"4".to_vec())
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const CODE_UNREACHABLE: &'static str = r#"
|
||||
@@ -531,7 +553,7 @@ const CODE_UNREACHABLE: &'static str = r#"
|
||||
#[test]
|
||||
fn top_level_call_refunds_even_if_fails() {
|
||||
let code_unreachable = wabt::wat2wasm(CODE_UNREACHABLE).unwrap();
|
||||
with_externalities(&mut new_test_ext(0, 4), || {
|
||||
with_externalities(&mut ExtBuilder::default().gas_price(4).build(), || {
|
||||
<CodeOf<Test>>::insert(1, code_unreachable.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
@@ -542,6 +564,48 @@ fn top_level_call_refunds_even_if_fails() {
|
||||
"vm execute returned error while call"
|
||||
);
|
||||
|
||||
assert_eq!(Staking::free_balance(&0), 100_000_000 - (4 * 3) - (4 * 135),);
|
||||
assert_eq!(Staking::free_balance(&0), 100_000_000 - (4 * 3) - (4 * 135));
|
||||
});
|
||||
}
|
||||
|
||||
const CODE_LOOP: &'static str = r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
(loop
|
||||
(br 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn block_gas_limit() {
|
||||
let code_loop = wabt::wat2wasm(CODE_LOOP).unwrap();
|
||||
with_externalities(
|
||||
&mut ExtBuilder::default().block_gas_limit(100_000).build(),
|
||||
|| {
|
||||
<CodeOf<Test>>::insert(1, code_loop.to_vec());
|
||||
|
||||
Staking::set_free_balance(&0, 100_000_000);
|
||||
Staking::increase_total_stake_by(100_000_000);
|
||||
|
||||
// Spend 50_000 units of gas (OOG).
|
||||
assert_err!(
|
||||
Contract::call(&0, 1, 0, 50_000, Vec::new()),
|
||||
"vm execute returned error while call"
|
||||
);
|
||||
|
||||
// Ensure we can't spend more gas than available in block gas limit.
|
||||
assert_err!(
|
||||
Contract::call(&0, 1, 0, 50_001, Vec::new()),
|
||||
"block gas limit is reached"
|
||||
);
|
||||
|
||||
// However, we can spend another 50_000
|
||||
assert_err!(
|
||||
Contract::call(&0, 1, 0, 50_000, Vec::new()),
|
||||
"vm execute returned error while call"
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user