diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 601f49566b..355eaaab36 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -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; } diff --git a/substrate/srml/contracts/src/exec.rs b/substrate/srml/contracts/src/exec.rs index d2abbb8ee7..49fb741e29 100644 --- a/substrate/srml/contracts/src/exec.rs +++ b/substrate/srml/contracts/src/exec.rs @@ -61,10 +61,9 @@ pub trait Ext { /// was deleted. fn get_storage(&self, key: &StorageKey) -> Option>; - /// 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>); + /// 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>) -> 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; + + /// 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>) { + fn set_storage(&mut self, key: StorageKey, value: Option>) -> 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. diff --git a/substrate/srml/contracts/src/lib.rs b/substrate/srml/contracts/src/lib.rs index ff97f6e102..f38010a355 100644 --- a/substrate/srml/contracts/src/lib.rs +++ b/substrate/srml/contracts/src/lib.rs @@ -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; + /// The maximum size of a storage value in bytes. + type MaxValueSize: Get; + /// The maximum amount of gas that could be expended per block. type BlockGasLimit: Get; } @@ -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 { pub schedule: Schedule, pub existential_deposit: BalanceOf, pub max_depth: u32, + pub max_value_size: u32, pub contract_account_instantiate_fee: BalanceOf, pub account_create_fee: BalanceOf, pub transfer_fee: BalanceOf, @@ -842,6 +851,7 @@ impl Config { schedule: >::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(), diff --git a/substrate/srml/contracts/src/tests.rs b/substrate/srml/contracts/src/tests.rs index bd7ed1f4ff..3e63bc0def 100644 --- a/substrate/srml/contracts/src/tests.rs +++ b/substrate/srml/contracts/src/tests.rs @@ -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(wabt_module: &str) + -> Result<(Vec, ::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::(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::(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::(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 { 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::(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::(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(), ::Balance::from(1_000u32).encode() // rent allowance )); let bob_contract = ContractInfoOf::::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::(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(), ::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::(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(), ::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::(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(), ::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(), ::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(), ::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::(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(), ::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::(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::(CODE_RESTORATION).unwrap(); + let (set_rent_wasm, set_rent_code_hash) = compile_module::(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(), ::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(), ::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(), ::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::(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::::get(BOB).unwrap().get_alive().unwrap(); + assert_eq!(bob_contract.rent_allowance, >::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" + ); + } + ); +} diff --git a/substrate/srml/contracts/src/wasm/mod.rs b/substrate/srml/contracts/src/wasm/mod.rs index 1f63b5d9df..3a6d3ad566 100644 --- a/substrate/srml/contracts/src/wasm/mod.rs +++ b/substrate/srml/contracts/src/wasm/mod.rs @@ -215,8 +215,11 @@ mod tests { fn get_storage(&self, key: &StorageKey) -> Option> { self.storage.get(key).cloned() } - fn set_storage(&mut self, key: StorageKey, value: Option>) { + fn set_storage(&mut self, key: StorageKey, value: Option>) + -> 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( diff --git a/substrate/srml/contracts/src/wasm/runtime.rs b/substrate/srml/contracts/src/wasm/runtime.rs index 29965c9676..92d9b98acf 100644 --- a/substrate/srml/contracts/src/wasm/runtime.rs +++ b/substrate/srml/contracts/src/wasm/runtime.rs @@ -254,6 +254,7 @@ define_env!(Env, , }, // 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, , // 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, , } else { None }; - ctx.ext.set_storage(key, value); + ctx.ext.set_storage(key, value).map_err(|_| sandbox::HostError)?; Ok(()) },