Contract storage limit (#3126)

* srml-contracts: Remove hard-coded code hashes from tests.

This makes it easier to update existing and add new test Wasm modules.

* Test maximum contract storage write size.

* Implement storage value limit for contracts.

* Bump node runtime spec version.
This commit is contained in:
Jim Posen
2019-07-16 16:23:06 +02:00
committed by Sergei Pepyakin
parent 95061beb79
commit 768eb1af4d
6 changed files with 203 additions and 55 deletions
+3 -2
View File
@@ -69,8 +69,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to equal spec_version. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 111,
impl_version: 111,
spec_version: 112,
impl_version: 112,
apis: RUNTIME_API_VERSIONS,
};
@@ -370,6 +370,7 @@ impl contracts::Trait for Runtime {
type CallBaseFee = contracts::DefaultCallBaseFee;
type CreateBaseFee = contracts::DefaultCreateBaseFee;
type MaxDepth = contracts::DefaultMaxDepth;
type MaxValueSize = contracts::DefaultMaxValueSize;
type BlockGasLimit = contracts::DefaultBlockGasLimit;
}
+19 -6
View File
@@ -61,10 +61,9 @@ pub trait Ext {
/// was deleted.
fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>>;
/// Sets the storage entry by the given key to the specified value.
///
/// If `value` is `None` then the storage entry is deleted.
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>);
/// Sets the storage entry by the given key to the specified value. If `value` is `None` then
/// the storage entry is deleted. Returns an Err if the value size is too large.
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> Result<(), &'static str>;
/// Instantiate a contract from the given code.
///
@@ -124,6 +123,9 @@ pub trait Ext {
/// Returns the current block number.
fn block_number(&self) -> BlockNumberOf<Self::T>;
/// Returns the maximum allowed size of a storage item.
fn max_value_size(&self) -> u32;
}
/// Loader is a companion of the `Vm` trait. It loads an appropriate abstract
@@ -606,10 +608,17 @@ where
self.ctx.overlay.get_storage(&self.ctx.self_account, self.ctx.self_trie_id.as_ref(), key)
}
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) {
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) -> Result<(), &'static str> {
if let Some(ref value) = value {
if self.max_value_size() < value.len() as u32 {
return Err("value size exceeds maximum");
}
}
self.ctx
.overlay
.set_storage(&self.ctx.self_account, key, value)
.set_storage(&self.ctx.self_account, key, value);
Ok(())
}
fn instantiate(
@@ -682,6 +691,10 @@ where
}
fn block_number(&self) -> T::BlockNumber { self.block_number }
fn max_value_size(&self) -> u32 {
self.ctx.config.max_value_size
}
}
/// These tests exercise the executive layer.
+10
View File
@@ -310,6 +310,8 @@ parameter_types! {
pub const DefaultCreateBaseFee: u32 = 1000;
/// A resonable default value for [`Trait::MaxDepth`].
pub const DefaultMaxDepth: u32 = 1024;
/// A resonable default value for [`Trait::MaxValueSize`].
pub const DefaultMaxValueSize: u32 = 16_384;
/// A resonable default value for [`Trait::BlockGasLimit`].
pub const DefaultBlockGasLimit: u32 = 10_000_000;
}
@@ -391,6 +393,9 @@ pub trait Trait: timestamp::Trait {
/// The maximum nesting level of a call/create stack.
type MaxDepth: Get<u32>;
/// The maximum size of a storage value in bytes.
type MaxValueSize: Get<u32>;
/// The maximum amount of gas that could be expended per block.
type BlockGasLimit: Get<Gas>;
}
@@ -490,6 +495,9 @@ decl_module! {
/// value is 100.
const MaxDepth: u32 = T::MaxDepth::get();
/// The maximum size of a storage value in bytes. A reasonable default is 16 KiB.
const MaxValueSize: u32 = T::MaxValueSize::get();
/// The maximum amount of gas that could be expended per block. A reasonable
/// default value is 10_000_000.
const BlockGasLimit: Gas = T::BlockGasLimit::get();
@@ -831,6 +839,7 @@ pub struct Config<T: Trait> {
pub schedule: Schedule,
pub existential_deposit: BalanceOf<T>,
pub max_depth: u32,
pub max_value_size: u32,
pub contract_account_instantiate_fee: BalanceOf<T>,
pub account_create_fee: BalanceOf<T>,
pub transfer_fee: BalanceOf<T>,
@@ -842,6 +851,7 @@ impl<T: Trait> Config<T> {
schedule: <Module<T>>::current_schedule(),
existential_deposit: T::Currency::minimum_balance(),
max_depth: T::MaxDepth::get(),
max_value_size: T::MaxValueSize::get(),
contract_account_instantiate_fee: T::ContractFee::get(),
account_create_fee: T::CreationFee::get(),
transfer_fee: T::TransferFee::get(),
+160 -45
View File
@@ -30,7 +30,7 @@ use parity_codec::{Decode, Encode, KeyedVec};
use runtime_io;
use runtime_io::with_externalities;
use runtime_primitives::testing::{Digest, DigestItem, Header, UintAuthorityId, H256};
use runtime_primitives::traits::{BlakeTwo256, IdentityLookup};
use runtime_primitives::traits::{BlakeTwo256, Hash, IdentityLookup};
use runtime_primitives::BuildStorage;
use srml_support::{
assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types,
@@ -148,6 +148,7 @@ parameter_types! {
pub const CallBaseFee: u64 = 135;
pub const CreateBaseFee: u64 = 175;
pub const MaxDepth: u32 = 100;
pub const MaxValueSize: u32 = 16_384;
}
impl Trait for Test {
type Currency = Balances;
@@ -171,6 +172,7 @@ impl Trait for Test {
type CallBaseFee = CallBaseFee;
type CreateBaseFee = CreateBaseFee;
type MaxDepth = MaxDepth;
type MaxValueSize = MaxValueSize;
type BlockGasLimit = BlockGasLimit;
}
@@ -277,6 +279,16 @@ impl ExtBuilder {
}
}
/// Generate Wasm binary and code hash from wabt source.
fn compile_module<T>(wabt_module: &str)
-> Result<(Vec<u8>, <T::Hashing as Hash>::Output), wabt::Error>
where T: system::Trait
{
let wasm = wabt::wat2wasm(wabt_module)?;
let code_hash = T::Hashing::hash(&wasm);
Ok((wasm, code_hash))
}
// Perform a simple transfer to a non-existent account supplying way more gas than needed.
// Then we check that the all unused gas is refunded.
#[test]
@@ -388,11 +400,10 @@ const CODE_RETURN_FROM_START_FN: &str = r#"
(data (i32.const 8) "\01\02\03\04")
)
"#;
const HASH_RETURN_FROM_START_FN: [u8; 32] = hex!("66c45bd7c473a1746e1d241176166ef53b1f207f56c5e87d1b6650140704181b");
#[test]
fn instantiate_and_call_and_deposit_event() {
let wasm = wabt::wat2wasm(CODE_RETURN_FROM_START_FN).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_RETURN_FROM_START_FN).unwrap();
with_externalities(
&mut ExtBuilder::default().existential_deposit(100).build(),
@@ -406,7 +417,7 @@ fn instantiate_and_call_and_deposit_event() {
Origin::signed(ALICE),
100,
100_000,
HASH_RETURN_FROM_START_FN.into(),
code_hash.into(),
vec![],
);
@@ -418,7 +429,7 @@ fn instantiate_and_call_and_deposit_event() {
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::CodeStored(HASH_RETURN_FROM_START_FN.into())),
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
topics: vec![],
},
EventRecord {
@@ -467,7 +478,6 @@ const CODE_DISPATCH_CALL: &str = r#"
(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() {
@@ -476,7 +486,7 @@ fn dispatch_call() {
let encoded = Encode::encode(&Call::Balances(balances::Call::transfer(CHARLIE, 50)));
assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]);
let wasm = wabt::wat2wasm(CODE_DISPATCH_CALL).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_DISPATCH_CALL).unwrap();
with_externalities(
&mut ExtBuilder::default().existential_deposit(50).build(),
@@ -495,7 +505,7 @@ fn dispatch_call() {
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())),
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
topics: vec![],
},
]);
@@ -504,7 +514,7 @@ fn dispatch_call() {
Origin::signed(ALICE),
100,
100_000,
HASH_DISPATCH_CALL.into(),
code_hash.into(),
vec![],
));
@@ -524,7 +534,7 @@ fn dispatch_call() {
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())),
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
topics: vec![],
},
EventRecord {
@@ -589,7 +599,6 @@ const CODE_DISPATCH_CALL_THEN_TRAP: &str = r#"
(data (i32.const 8) "\00\00\03\00\00\00\00\00\00\00\C8")
)
"#;
const HASH_DISPATCH_CALL_THEN_TRAP: [u8; 32] = hex!("55fe5c142dfe2519ca76c7c9b9f05012bd2624b7dcc128d2ce5a7af9d2da1846");
#[test]
fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
@@ -598,7 +607,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
let encoded = Encode::encode(&Call::Balances(balances::Call::transfer(CHARLIE, 50)));
assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]);
let wasm = wabt::wat2wasm(CODE_DISPATCH_CALL_THEN_TRAP).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_DISPATCH_CALL_THEN_TRAP).unwrap();
with_externalities(
&mut ExtBuilder::default().existential_deposit(50).build(),
@@ -617,7 +626,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL_THEN_TRAP.into())),
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
topics: vec![],
},
]);
@@ -626,7 +635,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
Origin::signed(ALICE),
100,
100_000,
HASH_DISPATCH_CALL_THEN_TRAP.into(),
code_hash.into(),
vec![],
));
@@ -650,7 +659,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL_THEN_TRAP.into())),
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
topics: vec![],
},
EventRecord {
@@ -784,9 +793,6 @@ const CODE_SET_RENT: &str = r#"
)
"#;
// Use test_hash_and_code test to get the actual hash if the code changed.
const HASH_SET_RENT: [u8; 32] = hex!("69aedfb4f6c1c398e97f8a5204de0f95ad5e7dc3540960beab11a86c569fbfcf");
/// Input data for each call in set_rent code
mod call {
pub fn set_storage_4_byte() -> Vec<u8> { vec![] }
@@ -804,7 +810,7 @@ fn test_set_rent_code_and_hash() {
let encoded = Encode::encode(&Call::Balances(balances::Call::transfer(CHARLIE, 50)));
assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]);
let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_SET_RENT).unwrap();
with_externalities(
&mut ExtBuilder::default().existential_deposit(50).build(),
@@ -822,7 +828,7 @@ fn test_set_rent_code_and_hash() {
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::CodeStored(HASH_SET_RENT.into())),
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
topics: vec![],
},
]);
@@ -832,7 +838,7 @@ fn test_set_rent_code_and_hash() {
#[test]
fn storage_size() {
let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_SET_RENT).unwrap();
// Storage size
with_externalities(
@@ -844,7 +850,7 @@ fn storage_size() {
assert_ok!(Contract::create(
Origin::signed(ALICE),
30_000,
100_000, HASH_SET_RENT.into(),
100_000, code_hash.into(),
<Test as balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
@@ -863,7 +869,7 @@ fn storage_size() {
#[test]
fn deduct_blocks() {
let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_SET_RENT).unwrap();
with_externalities(
&mut ExtBuilder::default().existential_deposit(50).build(),
@@ -874,7 +880,7 @@ fn deduct_blocks() {
assert_ok!(Contract::create(
Origin::signed(ALICE),
30_000,
100_000, HASH_SET_RENT.into(),
100_000, code_hash.into(),
<Test as balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
@@ -960,7 +966,7 @@ fn claim_surcharge_malus() {
/// Claim surcharge with the given trigger_call at the given blocks.
/// if removes is true then assert that the contract is a tombstonedead
fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) {
let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_SET_RENT).unwrap();
with_externalities(
&mut ExtBuilder::default().existential_deposit(50).build(),
@@ -971,7 +977,7 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool)
assert_ok!(Contract::create(
Origin::signed(ALICE),
100,
100_000, HASH_SET_RENT.into(),
100_000, code_hash.into(),
<Test as balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
@@ -995,7 +1001,7 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool)
/// * if allowance is exceeded
/// * if balance is reached and balance < subsistence threshold
fn removals(trigger_call: impl Fn() -> bool) {
let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_SET_RENT).unwrap();
// Balance reached and superior to subsistence threshold
with_externalities(
@@ -1007,7 +1013,7 @@ fn removals(trigger_call: impl Fn() -> bool) {
assert_ok!(Contract::create(
Origin::signed(ALICE),
100,
100_000, HASH_SET_RENT.into(),
100_000, code_hash.into(),
<Test as balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
@@ -1046,7 +1052,7 @@ fn removals(trigger_call: impl Fn() -> bool) {
assert_ok!(Contract::create(
Origin::signed(ALICE),
1_000,
100_000, HASH_SET_RENT.into(),
100_000, code_hash.into(),
<Test as balances::Trait>::Balance::from(100u32).encode() // rent allowance
));
@@ -1084,7 +1090,7 @@ fn removals(trigger_call: impl Fn() -> bool) {
assert_ok!(Contract::create(
Origin::signed(ALICE),
50+Balances::minimum_balance(),
100_000, HASH_SET_RENT.into(),
100_000, code_hash.into(),
<Test as balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
@@ -1119,7 +1125,7 @@ fn removals(trigger_call: impl Fn() -> bool) {
#[test]
fn call_removed_contract() {
let wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_SET_RENT).unwrap();
// Balance reached and superior to subsistence threshold
with_externalities(
@@ -1131,7 +1137,7 @@ fn call_removed_contract() {
assert_ok!(Contract::create(
Origin::signed(ALICE),
100,
100_000, HASH_SET_RENT.into(),
100_000, code_hash.into(),
<Test as balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
@@ -1205,12 +1211,10 @@ const CODE_CHECK_DEFAULT_RENT_ALLOWANCE: &str = r#"
)
)
"#;
const HASH_CHECK_DEFAULT_RENT_ALLOWANCE: [u8; 32] =
hex!("4f9ec2b94eea522cfff10b77ef4056c631045c00978a457d283950521ecf07b6");
#[test]
fn default_rent_allowance_on_create() {
let wasm = wabt::wat2wasm(CODE_CHECK_DEFAULT_RENT_ALLOWANCE).unwrap();
let (wasm, code_hash) = compile_module::<Test>(CODE_CHECK_DEFAULT_RENT_ALLOWANCE).unwrap();
with_externalities(
&mut ExtBuilder::default().existential_deposit(50).build(),
@@ -1222,7 +1226,7 @@ fn default_rent_allowance_on_create() {
Origin::signed(ALICE),
30_000,
100_000,
HASH_CHECK_DEFAULT_RENT_ALLOWANCE.into(),
code_hash.into(),
vec![],
));
@@ -1293,7 +1297,6 @@ const CODE_RESTORATION: &str = r#"
)
)
"#;
const HASH_RESTORATION: [u8; 32] = hex!("02988182efba70fe605031f5c55bfa59e47f72c0a4707f22b6b74fffbf7803dc");
#[test]
fn restorations_dirty_storage_and_different_storage() {
@@ -1316,6 +1319,10 @@ fn restoration_success() {
}
fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: bool) {
let (restoration_wasm, restoration_code_hash) =
compile_module::<Test>(CODE_RESTORATION).unwrap();
let (set_rent_wasm, set_rent_code_hash) = compile_module::<Test>(CODE_SET_RENT).unwrap();
let acl_key = {
let mut s = [0u8; 32];
s[0] = 1;
@@ -1326,7 +1333,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
// let's rewrite so as we use this module controlled call or we serialize it in runtime.
let encoded = hex::encode(Encode::encode(&Call::Contract(super::Call::restore_to(
BOB,
HASH_SET_RENT.into(),
set_rent_code_hash.into(),
<Test as balances::Trait>::Balance::from(50u32),
vec![acl_key, acl_key],
))));
@@ -1351,8 +1358,6 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
"The size of the literal was changed and requires updating in `CODE_RESTORATION`",
);
let restoration_wasm = wabt::wat2wasm(CODE_RESTORATION).unwrap();
let set_rent_wasm = wabt::wat2wasm(CODE_SET_RENT).unwrap();
with_externalities(
&mut ExtBuilder::default().existential_deposit(50).build(),
@@ -1371,23 +1376,23 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::CodeStored(HASH_RESTORATION.into())),
event: MetaEvent::contract(RawEvent::CodeStored(restoration_code_hash.into())),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::CodeStored(HASH_SET_RENT.into())),
event: MetaEvent::contract(RawEvent::CodeStored(set_rent_code_hash.into())),
topics: vec![],
},
]);
// Create an account with address `BOB` with code `HASH_SET_RENT`.
// Create an account with address `BOB` with code `CODE_SET_RENT`.
// The input parameter sets the rent allowance to 0.
assert_ok!(Contract::create(
Origin::signed(ALICE),
30_000,
100_000,
HASH_SET_RENT.into(),
set_rent_code_hash.into(),
<Test as balances::Trait>::Balance::from(0u32).encode()
));
@@ -1424,7 +1429,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
Origin::signed(CHARLIE),
30_000,
100_000,
HASH_RESTORATION.into(),
restoration_code_hash.into(),
<Test as balances::Trait>::Balance::from(0u32).encode()
));
@@ -1470,3 +1475,113 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
}
);
}
const CODE_STORAGE_SIZE: &str = r#"
(module
(import "env" "ext_get_storage" (func $ext_get_storage (param i32) (result i32)))
(import "env" "ext_set_storage" (func $ext_set_storage (param i32 i32 i32 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 16 16))
(func $assert (param i32)
(block $ok
(br_if $ok
(get_local 0)
)
(unreachable)
)
)
(func (export "call")
;; assert $ext_scratch_size == 8
(call $assert
(i32.eq
(call $ext_scratch_size)
(i32.const 4)
)
)
;; copy contents of the scratch buffer into the contract's memory.
(call $ext_scratch_copy
(i32.const 32) ;; Pointer in memory to the place where to copy.
(i32.const 0) ;; Offset from the start of the scratch buffer.
(i32.const 4) ;; Count of bytes to copy.
)
;; place a garbage value in storage, the size of which is specified by the call input.
(call $ext_set_storage
(i32.const 0) ;; Pointer to storage key
(i32.const 1) ;; Value is not null
(i32.const 0) ;; Pointer to value
(i32.load (i32.const 32)) ;; Size of value
)
(call $assert
(i32.eq
(call $ext_get_storage
(i32.const 0) ;; Pointer to storage key
)
(i32.const 0)
)
)
(call $assert
(i32.eq
(call $ext_scratch_size)
(i32.load (i32.const 32))
)
)
)
(func (export "deploy"))
(data (i32.const 0) "\01") ;; Storage key (32 B)
)
"#;
#[test]
fn storage_max_value_limit() {
let (wasm, code_hash) = compile_module::<Test>(CODE_STORAGE_SIZE).unwrap();
with_externalities(
&mut ExtBuilder::default().existential_deposit(50).build(),
|| {
// Create
Balances::deposit_creating(&ALICE, 1_000_000);
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
assert_ok!(Contract::create(
Origin::signed(ALICE),
30_000,
100_000,
code_hash.into(),
vec![],
));
// Check creation
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
assert_eq!(bob_contract.rent_allowance, <BalanceOf<Test>>::max_value());
// Call contract with allowed storage value.
assert_ok!(Contract::call(
Origin::signed(ALICE),
BOB,
0,
100_000,
Encode::encode(&self::MaxValueSize::get()),
));
// Call contract with too large a storage value.
assert_err!(
Contract::call(
Origin::signed(ALICE),
BOB,
0,
100_000,
Encode::encode(&(self::MaxValueSize::get() + 1)),
),
"during execution"
);
}
);
}
+6 -1
View File
@@ -215,8 +215,11 @@ mod tests {
fn get_storage(&self, key: &StorageKey) -> Option<Vec<u8>> {
self.storage.get(key).cloned()
}
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>) {
fn set_storage(&mut self, key: StorageKey, value: Option<Vec<u8>>)
-> Result<(), &'static str>
{
*self.storage.entry(key).or_insert(Vec::new()) = value.unwrap_or(Vec::new());
Ok(())
}
fn instantiate(
&mut self,
@@ -293,6 +296,8 @@ mod tests {
}
fn block_number(&self) -> u64 { 121 }
fn max_value_size(&self) -> u32 { 16_384 }
}
fn execute<E: Ext>(
+5 -1
View File
@@ -254,6 +254,7 @@ define_env!(Env, <E: Ext>,
},
// Change the value at the given key in the storage or remove the entry.
// The value length must not exceed the maximum defined by the Contracts module parameters.
//
// - key_ptr: pointer into the linear
// memory where the location of the requested value is placed.
@@ -263,6 +264,9 @@ define_env!(Env, <E: Ext>,
// where the value to set is placed. If `value_non_null` is set to 0, then this parameter is ignored.
// - value_len: the length of the value. If `value_non_null` is set to 0, then this parameter is ignored.
ext_set_storage(ctx, key_ptr: u32, value_non_null: u32, value_ptr: u32, value_len: u32) => {
if value_non_null != 0 && ctx.ext.max_value_size() < value_len {
return Err(sandbox::HostError);
}
let mut key: StorageKey = [0; 32];
read_sandbox_memory_into_buf(ctx, key_ptr, &mut key)?;
let value =
@@ -271,7 +275,7 @@ define_env!(Env, <E: Ext>,
} else {
None
};
ctx.ext.set_storage(key, value);
ctx.ext.set_storage(key, value).map_err(|_| sandbox::HostError)?;
Ok(())
},