Skip to content

Commit 312c71f

Browse files
authored
seal: Fail instantiate if new contract is below subsistence threshold (#6719)
* seal: Fail instantiate if new contract is below subsistence threshold We need each contract that exists to be above the subsistence threshold in order to keep up the guarantuee that we always leave a tombstone behind with the exception of a contract that called `ext_terminate`. * Fixup executor test * Bump runtime
1 parent 1484a20 commit 312c71f

File tree

4 files changed

+19
-13
lines changed

4 files changed

+19
-13
lines changed

src/exec.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,10 @@ where
426426
)?;
427427

428428
// Error out if insufficient remaining balance.
429-
if T::Currency::free_balance(&dest) < nested.config.existential_deposit {
429+
// We need each contract that exists to be above the subsistence threshold
430+
// in order to keep up the guarantuee that we always leave a tombstone behind
431+
// with the exception of a contract that called `ext_terminate`.
432+
if T::Currency::free_balance(&dest) < nested.config.subsistence_threshold() {
430433
Err("insufficient remaining balance")?
431434
}
432435

@@ -1016,7 +1019,7 @@ mod tests {
10161019

10171020
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
10181021

1019-
let result = ctx.instantiate(1, &mut gas_meter, &code, vec![]);
1022+
let result = ctx.instantiate(cfg.subsistence_threshold(), &mut gas_meter, &code, vec![]);
10201023
assert_matches!(result, Ok(_));
10211024

10221025
let mut toks = gas_meter.tokens().iter();
@@ -1306,7 +1309,7 @@ mod tests {
13061309
set_balance(&ALICE, 100);
13071310

13081311
let result = ctx.instantiate(
1309-
1,
1312+
cfg.subsistence_threshold(),
13101313
&mut GasMeter::<Test>::new(GAS_LIMIT),
13111314
&input_data_ch,
13121315
vec![1, 2, 3, 4],
@@ -1549,7 +1552,7 @@ mod tests {
15491552
// Instantiate a contract and save it's address in `instantiated_contract_address`.
15501553
let (address, output) = ctx.ext.instantiate(
15511554
&dummy_ch,
1552-
15u64,
1555+
Config::<Test>::subsistence_threshold_uncached(),
15531556
ctx.gas_meter,
15541557
vec![]
15551558
).unwrap();
@@ -1679,7 +1682,7 @@ mod tests {
16791682
set_balance(&ALICE, 100);
16801683

16811684
let result = ctx.instantiate(
1682-
1,
1685+
cfg.subsistence_threshold(),
16831686
&mut GasMeter::<Test>::new(GAS_LIMIT),
16841687
&rent_allowance_ch,
16851688
vec![],

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -743,15 +743,15 @@ impl<T: Trait> Config<T> {
743743
/// than the subsistence threshold in order to guarantee that a tombstone is created.
744744
///
745745
/// The only way to completely kill a contract without a tombstone is calling `ext_terminate`.
746-
fn subsistence_threshold(&self) -> BalanceOf<T> {
746+
pub fn subsistence_threshold(&self) -> BalanceOf<T> {
747747
self.existential_deposit.saturating_add(self.tombstone_deposit)
748748
}
749749

750750
/// The same as `subsistence_threshold` but without the need for a preloaded instance.
751751
///
752752
/// This is for cases where this value is needed in rent calculation rather than
753753
/// during contract execution.
754-
fn subsistence_threshold_uncached() -> BalanceOf<T> {
754+
pub fn subsistence_threshold_uncached() -> BalanceOf<T> {
755755
T::Currency::minimum_balance().saturating_add(T::TombstoneDeposit::get())
756756
}
757757
}

src/tests.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -385,13 +385,14 @@ fn instantiate_and_call_and_deposit_event() {
385385
.build()
386386
.execute_with(|| {
387387
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
388+
let subsistence = super::Config::<Test>::subsistence_threshold_uncached();
388389

389390
assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm));
390391

391392
// Check at the end to get hash on error easily
392393
let creation = Contracts::instantiate(
393394
Origin::signed(ALICE),
394-
100,
395+
subsistence,
395396
GAS_LIMIT,
396397
code_hash.into(),
397398
vec![],
@@ -421,14 +422,14 @@ fn instantiate_and_call_and_deposit_event() {
421422
EventRecord {
422423
phase: Phase::Initialization,
423424
event: MetaEvent::balances(
424-
pallet_balances::RawEvent::Endowed(BOB, 100)
425+
pallet_balances::RawEvent::Endowed(BOB, subsistence)
425426
),
426427
topics: vec![],
427428
},
428429
EventRecord {
429430
phase: Phase::Initialization,
430431
event: MetaEvent::balances(
431-
pallet_balances::RawEvent::Transfer(ALICE, BOB, 100)
432+
pallet_balances::RawEvent::Transfer(ALICE, BOB, subsistence)
432433
),
433434
topics: vec![],
434435
},

src/wasm/runtime.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -617,10 +617,12 @@ define_env!(Env, <E: Ext>,
617617
// This function creates an account and executes the constructor defined in the code specified
618618
// by the code hash. The address of this new account is copied to `address_ptr` and its length
619619
// to `address_len_ptr`. The constructors output buffer is copied to `output_ptr` and its
620-
// length to `output_len_ptr`.
620+
// length to `output_len_ptr`. The copy of the output buffer and address can be skipped by
621+
// supplying the sentinel value of `u32::max_value()` to `output_ptr` or `address_ptr`.
621622
//
622-
// The copy of the output buffer and address can be skipped by supplying the sentinel value
623-
// of `u32::max_value()` to `output_ptr` or `address_ptr`.
623+
// After running the constructor it is verfied that the contract account holds at
624+
// least the subsistence threshold. If that is not the case the instantion fails and
625+
// the contract is not created.
624626
//
625627
// # Parameters
626628
//

0 commit comments

Comments
 (0)