mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 01:51:09 +00:00
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:
committed by
Sergei Pepyakin
parent
95061beb79
commit
768eb1af4d
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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(())
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user