contracts: Add storage deposits (#10082)

* Frame no longer needs to be mutable (refactoring artifact)

* Remove Contract/Tombstone deposit

* Add StorageMeter

* cargo fmt

* Fix weight annotation

* cargo run --quiet --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_contracts --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/contracts/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Simplify keep check for contract accounts

- Make sure that the "base deposit" for each contract >= ed
- Remove now obsolete checks when sneding away free balance

* Remove unused imports and functions

* Rename storage_limit to storage_deposit_limit

* cargo fmt

* Fix typo

Co-authored-by: Michael Müller <michi@parity.io>

* Finish up rename of storage_limit

* Fix rpc tests

* Make use of `StorageDepositLimitTooHigh`

* Add tests and fix bugs discovered by tests

* Add storage migration

* Don't use u128 in RPC

* Fix weight of migration

* Rename `endowment` to `value`

* Fix bug where contract couldn't get funded by a storage deposit

- Make sure that contract gets funded from deposits before value is transferred
- Don't reserve value at origin because otherwise funding isn't possible
	- Just transfer free balance and reserve it after the transfer
- When refunding make sure that this refund can't dust the contract
	- Can only happen after a runtime upgrade where costs where upped
- Add more tests

* Apply suggestions from code review

Co-authored-by: Andrew Jones <ascjones@gmail.com>

* Remove unused `fn storage_meter`

* Fix copy pasta doc error

* Import `MaxEncodeLen` from codec

* Beautify RPC trait bounds

* Add re-instrument behaviour to dispatchable doc

* Make sure a account won't be destroyed a refund after a slash

* Apply suggestions from code review

Co-authored-by: Andrew Jones <ascjones@gmail.com>

* Update `Storage::write` docs

* Improve doc

* Remove superflous conditional

* Typos

* Remove superflous clone (refactoring artifact)

* Apply suggestions from code review

Co-authored-by: Andrew Jones <ascjones@gmail.com>

Co-authored-by: Parity Bot <admin@parity.io>
Co-authored-by: Michael Müller <michi@parity.io>
Co-authored-by: Andrew Jones <ascjones@gmail.com>
This commit is contained in:
Alexander Theißen
2021-12-07 13:19:30 +01:00
committed by GitHub
parent 56fb1cfbb6
commit 6863476603
27 changed files with 4395 additions and 1914 deletions
+18 -51
View File
@@ -31,7 +31,7 @@ use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};
use pwasm_utils::parity_wasm::elements::ValueType;
use sp_core::{crypto::UncheckedFrom, Bytes};
use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256};
use sp_runtime::traits::Bounded;
use sp_runtime::traits::{Bounded, Zero};
use sp_sandbox::SandboxMemory;
use sp_std::prelude::*;
@@ -55,15 +55,12 @@ pub enum ReturnCode {
CalleeReverted = 2,
/// The passed key does not exist in storage.
KeyNotFound = 3,
/// Transfer failed because it would have brought the sender's total balance below the
/// subsistence threshold.
BelowSubsistenceThreshold = 4,
/// Transfer failed for other reasons. Most probably reserved or locked balance of the
/// sender prevents the transfer.
/// Deprecated and no longer returned: There is only the minimum balance.
_BelowSubsistenceThreshold = 4,
/// See [`Error::TransferFailed`].
TransferFailed = 5,
/// The newly created contract is below the subsistence threshold after executing
/// its constructor.
NewContractNotFunded = 6,
/// Deprecated and no longer returned: Endowment is no longer required.
_EndowmentTooLow = 6,
/// No code could be found at the supplied code hash.
CodeNotFound = 7,
/// The contract that was called is no contract (a plain account).
@@ -151,8 +148,6 @@ pub enum RuntimeCosts {
ValueTransferred,
/// Weight of calling `seal_minimum_balance`.
MinimumBalance,
/// Weight of calling `seal_contract_deposit`.
ContractDeposit,
/// Weight of calling `seal_block_number`.
BlockNumber,
/// Weight of calling `seal_now`.
@@ -231,7 +226,6 @@ impl RuntimeCosts {
Balance => s.balance,
ValueTransferred => s.value_transferred,
MinimumBalance => s.minimum_balance,
ContractDeposit => s.contract_deposit,
BlockNumber => s.block_number,
Now => s.now,
WeightToFee => s.weight_to_fee,
@@ -611,16 +605,12 @@ where
fn err_into_return_code(from: DispatchError) -> Result<ReturnCode, DispatchError> {
use ReturnCode::*;
let below_sub = Error::<E::T>::BelowSubsistenceThreshold.into();
let transfer_failed = Error::<E::T>::TransferFailed.into();
let not_funded = Error::<E::T>::NewContractNotFunded.into();
let no_code = Error::<E::T>::CodeNotFound.into();
let not_found = Error::<E::T>::ContractNotFound.into();
match from {
x if x == below_sub => Ok(BelowSubsistenceThreshold),
x if x == transfer_failed => Ok(TransferFailed),
x if x == not_funded => Ok(NewContractNotFunded),
x if x == no_code => Ok(CodeNotFound),
x if x == not_found => Ok(NotCallable),
err => Err(err),
@@ -837,7 +827,6 @@ define_env!(Env, <E: Ext>,
//
// # Errors
//
// `ReturnCode::BelowSubsistenceThreshold`
// `ReturnCode::TransferFailed`
[seal0] seal_transfer(
ctx,
@@ -925,7 +914,6 @@ define_env!(Env, <E: Ext>,
//
// `ReturnCode::CalleeReverted`: Output buffer is returned.
// `ReturnCode::CalleeTrapped`
// `ReturnCode::BelowSubsistenceThreshold`
// `ReturnCode::TransferFailed`
// `ReturnCode::NotCallable`
[seal1] seal_call(
@@ -1002,9 +990,8 @@ define_env!(Env, <E: Ext>,
// length to `output_len_ptr`. The copy of the output buffer and address can be skipped by
// supplying the sentinel value of `u32::MAX` to `output_ptr` or `address_ptr`.
//
// After running the constructor it is verified that the contract account holds at
// least the subsistence threshold. If that is not the case the instantiation fails and
// the contract is not created.
// `value` must be at least the minimum balance. Otherwise the instantiation fails and the
// contract is not created.
//
// # Parameters
//
@@ -1033,9 +1020,7 @@ define_env!(Env, <E: Ext>,
//
// `ReturnCode::CalleeReverted`: Output buffer is returned.
// `ReturnCode::CalleeTrapped`
// `ReturnCode::BelowSubsistenceThreshold`
// `ReturnCode::TransferFailed`
// `ReturnCode::NewContractNotFunded`
// `ReturnCode::CodeNotFound`
[seal1] seal_instantiate(
ctx,
@@ -1082,7 +1067,7 @@ define_env!(Env, <E: Ext>,
ctx.terminate(beneficiary_ptr)
},
// Remove the calling account and transfer remaining balance.
// Remove the calling account and transfer remaining **free** balance.
//
// This function never returns. Either the termination was successful and the
// execution of the destroyed contract is halted. Or it failed during the termination
@@ -1215,7 +1200,7 @@ define_env!(Env, <E: Ext>,
)?)
},
// Stores the balance of the current account into the supplied buffer.
// Stores the **free* balance of the current account into the supplied buffer.
//
// The value is stored to linear memory at the address pointed to by `out_ptr`.
// `out_len_ptr` must point to a u32 value that describes the available space at
@@ -1230,7 +1215,7 @@ define_env!(Env, <E: Ext>,
)?)
},
// Stores the value transferred along with this call or as endowment into the supplied buffer.
// Stores the value transferred along with this call/instantiate into the supplied buffer.
//
// The value is stored to linear memory at the address pointed to by `out_ptr`.
// `out_len_ptr` must point to a u32 value that describes the available space at
@@ -1323,38 +1308,20 @@ define_env!(Env, <E: Ext>,
)?)
},
// Stores the contract deposit into the supplied buffer.
//
// # Deprecation
//
// This is equivalent to calling `seal_contract_deposit` and only exists for backwards
// compatibility. See that function for documentation.
[seal0] seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => {
ctx.charge_gas(RuntimeCosts::ContractDeposit)?;
Ok(ctx.write_sandbox_output(
out_ptr, out_len_ptr, &ctx.ext.contract_deposit().encode(), false, already_charged
)?)
},
// Stores the contract deposit into the supplied buffer.
// Stores the tombstone deposit into the supplied buffer.
//
// The value is stored to linear memory at the address pointed to by `out_ptr`.
// `out_len_ptr` must point to a u32 value that describes the available space at
// `out_ptr`. This call overwrites it with the size of the value. If the available
// space at `out_ptr` is less than the size of the value a trap is triggered.
//
// The data is encoded as T::Balance.
// # Deprecation
//
// # Note
//
// The contract deposit is on top of the existential deposit. The sum
// is commonly referred as subsistence threshold in code. No contract initiated
// balance transfer can go below this threshold.
[seal0] seal_contract_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => {
ctx.charge_gas(RuntimeCosts::ContractDeposit)?;
Ok(ctx.write_sandbox_output(
out_ptr, out_len_ptr, &ctx.ext.contract_deposit().encode(), false, already_charged
)?)
// There is no longer a tombstone deposit. This function always returns 0.
[seal0] seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => {
ctx.charge_gas(RuntimeCosts::Balance)?;
let deposit = <BalanceOf<E::T>>::zero().encode();
Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, &deposit, false, already_charged)?)
},
// Was used to restore the given destination contract sacrificing the caller.