mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-25 11:45:47 +00:00
pallet-contracts: Rent projection RPC (#4754)
* Initial approach * Introduce the pallet-contracts-common crate * Add rent::compute_rent_projection * Wire everything together * Fix build error. * Rename EvictionDate → EvictionAt. * Clean. * Renaming and cleaning. * Add documentation for rent_projection RPC. * Add documentation for rent_projection runtime API. * Refactor rent_budget. Merge it with subsistence_treshold. * Bump impl_version * Constrain RPC impl with Block::Header::Number. * Rename pallet-contracts-common into -primitives * Add a comment for `compute_rent_projection` on the usage * Small tidying
This commit is contained in:
Generated
+13
@@ -3196,6 +3196,7 @@ dependencies = [
|
|||||||
"pallet-balances 2.0.0",
|
"pallet-balances 2.0.0",
|
||||||
"pallet-collective 2.0.0",
|
"pallet-collective 2.0.0",
|
||||||
"pallet-contracts 2.0.0",
|
"pallet-contracts 2.0.0",
|
||||||
|
"pallet-contracts-primitives 2.0.0",
|
||||||
"pallet-contracts-rpc-runtime-api 0.8.0",
|
"pallet-contracts-rpc-runtime-api 0.8.0",
|
||||||
"pallet-democracy 2.0.0",
|
"pallet-democracy 2.0.0",
|
||||||
"pallet-elections-phragmen 2.0.0",
|
"pallet-elections-phragmen 2.0.0",
|
||||||
@@ -3631,6 +3632,7 @@ dependencies = [
|
|||||||
"frame-system 2.0.0",
|
"frame-system 2.0.0",
|
||||||
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pallet-balances 2.0.0",
|
"pallet-balances 2.0.0",
|
||||||
|
"pallet-contracts-primitives 2.0.0",
|
||||||
"pallet-randomness-collective-flip 2.0.0",
|
"pallet-randomness-collective-flip 2.0.0",
|
||||||
"pallet-timestamp 2.0.0",
|
"pallet-timestamp 2.0.0",
|
||||||
"parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -3646,6 +3648,15 @@ dependencies = [
|
|||||||
"wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wasmi-validation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pallet-contracts-primitives"
|
||||||
|
version = "2.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"sp-runtime 2.0.0",
|
||||||
|
"sp-std 2.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallet-contracts-rpc"
|
name = "pallet-contracts-rpc"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -3653,6 +3664,7 @@ dependencies = [
|
|||||||
"jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-core-client 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core-client 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-derive 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-derive 14.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pallet-contracts-primitives 2.0.0",
|
||||||
"pallet-contracts-rpc-runtime-api 0.8.0",
|
"pallet-contracts-rpc-runtime-api 0.8.0",
|
||||||
"parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -3668,6 +3680,7 @@ dependencies = [
|
|||||||
name = "pallet-contracts-rpc-runtime-api"
|
name = "pallet-contracts-rpc-runtime-api"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"pallet-contracts-primitives 2.0.0",
|
||||||
"parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sp-api 2.0.0",
|
"sp-api 2.0.0",
|
||||||
"sp-runtime 2.0.0",
|
"sp-runtime 2.0.0",
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use node_primitives::{Block, AccountId, Index, Balance};
|
use node_primitives::{Block, BlockNumber, AccountId, Index, Balance};
|
||||||
use node_runtime::UncheckedExtrinsic;
|
use node_runtime::UncheckedExtrinsic;
|
||||||
use sp_api::ProvideRuntimeApi;
|
use sp_api::ProvideRuntimeApi;
|
||||||
use sp_transaction_pool::TransactionPool;
|
use sp_transaction_pool::TransactionPool;
|
||||||
@@ -66,7 +66,7 @@ pub fn create<C, P, M, F>(
|
|||||||
C: sc_client::blockchain::HeaderBackend<Block>,
|
C: sc_client::blockchain::HeaderBackend<Block>,
|
||||||
C: Send + Sync + 'static,
|
C: Send + Sync + 'static,
|
||||||
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>,
|
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>,
|
||||||
C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance>,
|
C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance, BlockNumber>,
|
||||||
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance, UncheckedExtrinsic>,
|
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance, UncheckedExtrinsic>,
|
||||||
F: sc_client::light::fetcher::Fetcher<Block> + 'static,
|
F: sc_client::light::fetcher::Fetcher<Block> + 'static,
|
||||||
P: TransactionPool + 'static,
|
P: TransactionPool + 'static,
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ pallet-babe = { version = "2.0.0", default-features = false, path = "../../../fr
|
|||||||
pallet-balances = { version = "2.0.0", default-features = false, path = "../../../frame/balances" }
|
pallet-balances = { version = "2.0.0", default-features = false, path = "../../../frame/balances" }
|
||||||
pallet-collective = { version = "2.0.0", default-features = false, path = "../../../frame/collective" }
|
pallet-collective = { version = "2.0.0", default-features = false, path = "../../../frame/collective" }
|
||||||
pallet-contracts = { version = "2.0.0", default-features = false, path = "../../../frame/contracts" }
|
pallet-contracts = { version = "2.0.0", default-features = false, path = "../../../frame/contracts" }
|
||||||
|
pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "../../../frame/contracts/common/" }
|
||||||
pallet-contracts-rpc-runtime-api = { version = "0.8.0", default-features = false, path = "../../../frame/contracts/rpc/runtime-api/" }
|
pallet-contracts-rpc-runtime-api = { version = "0.8.0", default-features = false, path = "../../../frame/contracts/rpc/runtime-api/" }
|
||||||
pallet-democracy = { version = "2.0.0", default-features = false, path = "../../../frame/democracy" }
|
pallet-democracy = { version = "2.0.0", default-features = false, path = "../../../frame/democracy" }
|
||||||
pallet-elections-phragmen = { version = "2.0.0", default-features = false, path = "../../../frame/elections-phragmen" }
|
pallet-elections-phragmen = { version = "2.0.0", default-features = false, path = "../../../frame/elections-phragmen" }
|
||||||
@@ -83,8 +84,9 @@ std = [
|
|||||||
"sp-block-builder/std",
|
"sp-block-builder/std",
|
||||||
"codec/std",
|
"codec/std",
|
||||||
"pallet-collective/std",
|
"pallet-collective/std",
|
||||||
"pallet-contracts-rpc-runtime-api/std",
|
|
||||||
"pallet-contracts/std",
|
"pallet-contracts/std",
|
||||||
|
"pallet-contracts-primitives/std",
|
||||||
|
"pallet-contracts-rpc-runtime-api/std",
|
||||||
"pallet-democracy/std",
|
"pallet-democracy/std",
|
||||||
"pallet-elections-phragmen/std",
|
"pallet-elections-phragmen/std",
|
||||||
"frame-executive/std",
|
"frame-executive/std",
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
|||||||
// implementation changes and behavior does not, then leave spec_version as
|
// implementation changes and behavior does not, then leave spec_version as
|
||||||
// is and increment impl_version.
|
// is and increment impl_version.
|
||||||
spec_version: 210,
|
spec_version: 210,
|
||||||
impl_version: 0,
|
impl_version: 1,
|
||||||
apis: RUNTIME_API_VERSIONS,
|
apis: RUNTIME_API_VERSIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -746,7 +746,9 @@ impl_runtime_apis! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance> for Runtime {
|
impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber>
|
||||||
|
for Runtime
|
||||||
|
{
|
||||||
fn call(
|
fn call(
|
||||||
origin: AccountId,
|
origin: AccountId,
|
||||||
dest: AccountId,
|
dest: AccountId,
|
||||||
@@ -754,13 +756,8 @@ impl_runtime_apis! {
|
|||||||
gas_limit: u64,
|
gas_limit: u64,
|
||||||
input_data: Vec<u8>,
|
input_data: Vec<u8>,
|
||||||
) -> ContractExecResult {
|
) -> ContractExecResult {
|
||||||
let exec_result = Contracts::bare_call(
|
let exec_result =
|
||||||
origin,
|
Contracts::bare_call(origin, dest.into(), value, gas_limit, input_data);
|
||||||
dest.into(),
|
|
||||||
value,
|
|
||||||
gas_limit,
|
|
||||||
input_data,
|
|
||||||
);
|
|
||||||
match exec_result {
|
match exec_result {
|
||||||
Ok(v) => ContractExecResult::Success {
|
Ok(v) => ContractExecResult::Success {
|
||||||
status: v.status,
|
status: v.status,
|
||||||
@@ -773,16 +770,14 @@ impl_runtime_apis! {
|
|||||||
fn get_storage(
|
fn get_storage(
|
||||||
address: AccountId,
|
address: AccountId,
|
||||||
key: [u8; 32],
|
key: [u8; 32],
|
||||||
) -> pallet_contracts_rpc_runtime_api::GetStorageResult {
|
) -> pallet_contracts_primitives::GetStorageResult {
|
||||||
Contracts::get_storage(address, key).map_err(|rpc_err| {
|
Contracts::get_storage(address, key)
|
||||||
use pallet_contracts::GetStorageError;
|
}
|
||||||
use pallet_contracts_rpc_runtime_api::{GetStorageError as RpcGetStorageError};
|
|
||||||
/// Map the contract error into the RPC layer error.
|
fn rent_projection(
|
||||||
match rpc_err {
|
address: AccountId,
|
||||||
GetStorageError::ContractDoesntExist => RpcGetStorageError::ContractDoesntExist,
|
) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {
|
||||||
GetStorageError::IsTombstone => RpcGetStorageError::IsTombstone,
|
Contracts::rent_projection(address)
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ sp-std = { version = "2.0.0", default-features = false, path = "../../primitives
|
|||||||
sp-sandbox = { version = "0.8.0", default-features = false, path = "../../primitives/sandbox" }
|
sp-sandbox = { version = "0.8.0", default-features = false, path = "../../primitives/sandbox" }
|
||||||
frame-support = { version = "2.0.0", default-features = false, path = "../support" }
|
frame-support = { version = "2.0.0", default-features = false, path = "../support" }
|
||||||
frame-system = { version = "2.0.0", default-features = false, path = "../system" }
|
frame-system = { version = "2.0.0", default-features = false, path = "../system" }
|
||||||
|
pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "common" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wabt = "0.9.2"
|
wabt = "0.9.2"
|
||||||
@@ -42,4 +43,5 @@ std = [
|
|||||||
"parity-wasm/std",
|
"parity-wasm/std",
|
||||||
"pwasm-utils/std",
|
"pwasm-utils/std",
|
||||||
"wasmi-validation/std",
|
"wasmi-validation/std",
|
||||||
|
"pallet-contracts-primitives/std",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "pallet-contracts-primitives"
|
||||||
|
version = "2.0.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# This crate should not rely on any of the frame primitives.
|
||||||
|
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] }
|
||||||
|
sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" }
|
||||||
|
sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = [
|
||||||
|
"codec/std",
|
||||||
|
"sp-runtime/std",
|
||||||
|
"sp-std/std",
|
||||||
|
]
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// Substrate is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Substrate is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! A crate that hosts a common definitions that are relevent for the pallet-contracts.
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
use sp_std::prelude::*;
|
||||||
|
|
||||||
|
/// A result type of a get storage call.
|
||||||
|
pub type GetStorageResult = Result<Option<Vec<u8>>, ContractAccessError>;
|
||||||
|
|
||||||
|
/// The possible errors that can happen querying the storage of a contract.
|
||||||
|
#[derive(Eq, PartialEq, codec::Encode, codec::Decode, sp_runtime::RuntimeDebug)]
|
||||||
|
pub enum ContractAccessError {
|
||||||
|
/// The given address doesn't point to a contract.
|
||||||
|
DoesntExist,
|
||||||
|
/// The specified contract is a tombstone and thus cannot have any storage.
|
||||||
|
IsTombstone,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A result type of a `rent_projection` call.
|
||||||
|
pub type RentProjectionResult<BlockNumber> =
|
||||||
|
Result<RentProjection<BlockNumber>, ContractAccessError>;
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, codec::Encode, codec::Decode, sp_runtime::RuntimeDebug)]
|
||||||
|
pub enum RentProjection<BlockNumber> {
|
||||||
|
/// Eviction is projected to happen at the specified block number.
|
||||||
|
EvictionAt(BlockNumber),
|
||||||
|
/// No eviction is scheduled.
|
||||||
|
///
|
||||||
|
/// E.g. because the contract accumulated enough funds to offset the rent storage costs.
|
||||||
|
NoEviction,
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ sp-rpc = { version = "2.0.0", path = "../../../primitives/rpc" }
|
|||||||
serde = { version = "1.0.101", features = ["derive"] }
|
serde = { version = "1.0.101", features = ["derive"] }
|
||||||
sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" }
|
sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" }
|
||||||
sp-api = { version = "2.0.0", path = "../../../primitives/api" }
|
sp-api = { version = "2.0.0", path = "../../../primitives/api" }
|
||||||
|
pallet-contracts-primitives = { version = "2.0.0", path = "../common" }
|
||||||
pallet-contracts-rpc-runtime-api = { version = "0.8.0", path = "./runtime-api" }
|
pallet-contracts-rpc-runtime-api = { version = "0.8.0", path = "./runtime-api" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ sp-api = { version = "2.0.0", default-features = false, path = "../../../../prim
|
|||||||
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] }
|
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] }
|
||||||
sp-std = { version = "2.0.0", default-features = false, path = "../../../../primitives/std" }
|
sp-std = { version = "2.0.0", default-features = false, path = "../../../../primitives/std" }
|
||||||
sp-runtime = { version = "2.0.0", default-features = false, path = "../../../../primitives/runtime" }
|
sp-runtime = { version = "2.0.0", default-features = false, path = "../../../../primitives/runtime" }
|
||||||
|
pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "../../common" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
@@ -18,4 +19,5 @@ std = [
|
|||||||
"codec/std",
|
"codec/std",
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
"sp-runtime/std",
|
"sp-runtime/std",
|
||||||
|
"pallet-contracts-primitives/std",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -22,9 +22,10 @@
|
|||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use sp_std::vec::Vec;
|
use codec::{Codec, Decode, Encode};
|
||||||
use codec::{Encode, Decode, Codec};
|
use pallet_contracts_primitives::{GetStorageResult, RentProjectionResult};
|
||||||
use sp_runtime::RuntimeDebug;
|
use sp_runtime::RuntimeDebug;
|
||||||
|
use sp_std::vec::Vec;
|
||||||
|
|
||||||
/// A result of execution of a contract.
|
/// A result of execution of a contract.
|
||||||
#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
|
#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
|
||||||
@@ -44,25 +45,12 @@ pub enum ContractExecResult {
|
|||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A result type of the get storage call.
|
|
||||||
///
|
|
||||||
/// See [`ContractsApi::get_storage`] for more info.
|
|
||||||
pub type GetStorageResult = Result<Option<Vec<u8>>, GetStorageError>;
|
|
||||||
|
|
||||||
/// The possible errors that can happen querying the storage of a contract.
|
|
||||||
#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
|
|
||||||
pub enum GetStorageError {
|
|
||||||
/// The given address doesn't point on a contract.
|
|
||||||
ContractDoesntExist,
|
|
||||||
/// The specified contract is a tombstone and thus cannot have any storage.
|
|
||||||
IsTombstone,
|
|
||||||
}
|
|
||||||
|
|
||||||
sp_api::decl_runtime_apis! {
|
sp_api::decl_runtime_apis! {
|
||||||
/// The API to interact with contracts without using executive.
|
/// The API to interact with contracts without using executive.
|
||||||
pub trait ContractsApi<AccountId, Balance> where
|
pub trait ContractsApi<AccountId, Balance, BlockNumber> where
|
||||||
AccountId: Codec,
|
AccountId: Codec,
|
||||||
Balance: Codec,
|
Balance: Codec,
|
||||||
|
BlockNumber: Codec,
|
||||||
{
|
{
|
||||||
/// Perform a call from a specified account to a given contract.
|
/// Perform a call from a specified account to a given contract.
|
||||||
///
|
///
|
||||||
@@ -85,5 +73,13 @@ sp_api::decl_runtime_apis! {
|
|||||||
address: AccountId,
|
address: AccountId,
|
||||||
key: [u8; 32],
|
key: [u8; 32],
|
||||||
) -> GetStorageResult;
|
) -> GetStorageResult;
|
||||||
|
|
||||||
|
/// Returns the projected time a given contract will be able to sustain paying its rent.
|
||||||
|
///
|
||||||
|
/// The returned projection is relevent for the current block, i.e. it is as if the contract
|
||||||
|
/// was accessed at the current block.
|
||||||
|
///
|
||||||
|
/// Returns `Err` if the contract is in a tombstone state or doesn't exist.
|
||||||
|
fn rent_projection(address: AccountId) -> RentProjectionResult<BlockNumber>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,19 +18,23 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use sp_blockchain::HeaderBackend;
|
|
||||||
use codec::Codec;
|
use codec::Codec;
|
||||||
use jsonrpc_core::{Error, ErrorCode, Result};
|
use jsonrpc_core::{Error, ErrorCode, Result};
|
||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
use sp_core::{H256, Bytes};
|
use pallet_contracts_primitives::RentProjection;
|
||||||
use sp_rpc::number;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
|
||||||
use sp_api::ProvideRuntimeApi;
|
use sp_api::ProvideRuntimeApi;
|
||||||
|
use sp_blockchain::HeaderBackend;
|
||||||
|
use sp_core::{Bytes, H256};
|
||||||
|
use sp_rpc::number;
|
||||||
|
use sp_runtime::{
|
||||||
|
generic::BlockId,
|
||||||
|
traits::{Block as BlockT, Header as HeaderT},
|
||||||
|
};
|
||||||
|
|
||||||
pub use self::gen_client::Client as ContractsClient;
|
pub use self::gen_client::Client as ContractsClient;
|
||||||
pub use pallet_contracts_rpc_runtime_api::{
|
pub use pallet_contracts_rpc_runtime_api::{
|
||||||
self as runtime_api, ContractExecResult, ContractsApi as ContractsRuntimeApi, GetStorageResult,
|
self as runtime_api, ContractExecResult, ContractsApi as ContractsRuntimeApi,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RUNTIME_ERROR: i64 = 1;
|
const RUNTIME_ERROR: i64 = 1;
|
||||||
@@ -46,13 +50,13 @@ const CONTRACT_IS_A_TOMBSTONE: i64 = 3;
|
|||||||
/// https://docs.google.com/spreadsheets/d/1h0RqncdqiWI4KgxO0z9JIpZEJESXjX_ZCK6LFX6veDo/view
|
/// https://docs.google.com/spreadsheets/d/1h0RqncdqiWI4KgxO0z9JIpZEJESXjX_ZCK6LFX6veDo/view
|
||||||
const GAS_PER_SECOND: u64 = 1_000_000_000;
|
const GAS_PER_SECOND: u64 = 1_000_000_000;
|
||||||
|
|
||||||
/// A private newtype for converting `GetStorageError` into an RPC error.
|
/// A private newtype for converting `ContractAccessError` into an RPC error.
|
||||||
struct GetStorageError(runtime_api::GetStorageError);
|
struct ContractAccessError(pallet_contracts_primitives::ContractAccessError);
|
||||||
impl From<GetStorageError> for Error {
|
impl From<ContractAccessError> for Error {
|
||||||
fn from(e: GetStorageError) -> Error {
|
fn from(e: ContractAccessError) -> Error {
|
||||||
use runtime_api::GetStorageError::*;
|
use pallet_contracts_primitives::ContractAccessError::*;
|
||||||
match e.0 {
|
match e.0 {
|
||||||
ContractDoesntExist => Error {
|
DoesntExist => Error {
|
||||||
code: ErrorCode::ServerError(CONTRACT_DOESNT_EXIST),
|
code: ErrorCode::ServerError(CONTRACT_DOESNT_EXIST),
|
||||||
message: "The specified contract doesn't exist.".into(),
|
message: "The specified contract doesn't exist.".into(),
|
||||||
data: None,
|
data: None,
|
||||||
@@ -61,7 +65,7 @@ impl From<GetStorageError> for Error {
|
|||||||
code: ErrorCode::ServerError(CONTRACT_IS_A_TOMBSTONE),
|
code: ErrorCode::ServerError(CONTRACT_IS_A_TOMBSTONE),
|
||||||
message: "The contract is a tombstone and doesn't have any storage.".into(),
|
message: "The contract is a tombstone and doesn't have any storage.".into(),
|
||||||
data: None,
|
data: None,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,19 +101,18 @@ pub enum RpcContractExecResult {
|
|||||||
impl From<ContractExecResult> for RpcContractExecResult {
|
impl From<ContractExecResult> for RpcContractExecResult {
|
||||||
fn from(r: ContractExecResult) -> Self {
|
fn from(r: ContractExecResult) -> Self {
|
||||||
match r {
|
match r {
|
||||||
ContractExecResult::Success { status, data } => {
|
ContractExecResult::Success { status, data } => RpcContractExecResult::Success {
|
||||||
RpcContractExecResult::Success { status, data: data.into() }
|
status,
|
||||||
},
|
data: data.into(),
|
||||||
ContractExecResult::Error => {
|
|
||||||
RpcContractExecResult::Error(())
|
|
||||||
},
|
},
|
||||||
|
ContractExecResult::Error => RpcContractExecResult::Error(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contracts RPC methods.
|
/// Contracts RPC methods.
|
||||||
#[rpc]
|
#[rpc]
|
||||||
pub trait ContractsApi<BlockHash, AccountId, Balance> {
|
pub trait ContractsApi<BlockHash, BlockNumber, AccountId, Balance> {
|
||||||
/// Executes a call to a contract.
|
/// Executes a call to a contract.
|
||||||
///
|
///
|
||||||
/// This call is performed locally without submitting any transactions. Thus executing this
|
/// This call is performed locally without submitting any transactions. Thus executing this
|
||||||
@@ -132,6 +135,19 @@ pub trait ContractsApi<BlockHash, AccountId, Balance> {
|
|||||||
key: H256,
|
key: H256,
|
||||||
at: Option<BlockHash>,
|
at: Option<BlockHash>,
|
||||||
) -> Result<Option<Bytes>>;
|
) -> Result<Option<Bytes>>;
|
||||||
|
|
||||||
|
/// Returns the projected time a given contract will be able to sustain paying its rent.
|
||||||
|
///
|
||||||
|
/// The returned projection is relevent for the given block, i.e. it is as if the contract was
|
||||||
|
/// accessed at the beginning of that block.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the contract is exempted from rent.
|
||||||
|
#[rpc(name = "contracts_rentProjection")]
|
||||||
|
fn rent_projection(
|
||||||
|
&self,
|
||||||
|
address: AccountId,
|
||||||
|
at: Option<BlockHash>,
|
||||||
|
) -> Result<Option<BlockNumber>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An implementation of contract specific RPC methods.
|
/// An implementation of contract specific RPC methods.
|
||||||
@@ -149,13 +165,22 @@ impl<C, B> Contracts<C, B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<C, Block, AccountId, Balance>
|
||||||
impl<C, Block, AccountId, Balance> ContractsApi<<Block as BlockT>::Hash, AccountId, Balance>
|
ContractsApi<
|
||||||
for Contracts<C, Block>
|
<Block as BlockT>::Hash,
|
||||||
|
<<Block as BlockT>::Header as HeaderT>::Number,
|
||||||
|
AccountId,
|
||||||
|
Balance,
|
||||||
|
> for Contracts<C, Block>
|
||||||
where
|
where
|
||||||
Block: BlockT,
|
Block: BlockT,
|
||||||
C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
|
C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
|
||||||
C::Api: ContractsRuntimeApi<Block, AccountId, Balance>,
|
C::Api: ContractsRuntimeApi<
|
||||||
|
Block,
|
||||||
|
AccountId,
|
||||||
|
Balance,
|
||||||
|
<<Block as BlockT>::Header as HeaderT>::Number,
|
||||||
|
>,
|
||||||
AccountId: Codec,
|
AccountId: Codec,
|
||||||
Balance: Codec,
|
Balance: Codec,
|
||||||
{
|
{
|
||||||
@@ -188,8 +213,7 @@ where
|
|||||||
code: ErrorCode::InvalidParams,
|
code: ErrorCode::InvalidParams,
|
||||||
message: format!(
|
message: format!(
|
||||||
"Requested gas limit is greater than maximum allowed: {} > {}",
|
"Requested gas limit is greater than maximum allowed: {} > {}",
|
||||||
gas_limit,
|
gas_limit, max_gas_limit
|
||||||
max_gas_limit
|
|
||||||
),
|
),
|
||||||
data: None,
|
data: None,
|
||||||
});
|
});
|
||||||
@@ -197,11 +221,7 @@ where
|
|||||||
|
|
||||||
let exec_result = api
|
let exec_result = api
|
||||||
.call(&at, origin, dest, value, gas_limit, input_data.to_vec())
|
.call(&at, origin, dest, value, gas_limit, input_data.to_vec())
|
||||||
.map_err(|e| Error {
|
.map_err(|e| runtime_error_into_rpc_err(e))?;
|
||||||
code: ErrorCode::ServerError(RUNTIME_ERROR),
|
|
||||||
message: "Runtime trapped while executing a contract.".into(),
|
|
||||||
data: Some(format!("{:?}", e).into()),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(exec_result.into())
|
Ok(exec_result.into())
|
||||||
}
|
}
|
||||||
@@ -217,19 +237,43 @@ where
|
|||||||
// If the block hash is not supplied assume the best block.
|
// If the block hash is not supplied assume the best block.
|
||||||
self.client.info().best_hash));
|
self.client.info().best_hash));
|
||||||
|
|
||||||
let get_storage_result = api
|
let result = api
|
||||||
.get_storage(&at, address, key.into())
|
.get_storage(&at, address, key.into())
|
||||||
.map_err(|e|
|
.map_err(|e| runtime_error_into_rpc_err(e))?
|
||||||
// Handle general API calling errors.
|
.map_err(ContractAccessError)?
|
||||||
Error {
|
|
||||||
code: ErrorCode::ServerError(RUNTIME_ERROR),
|
|
||||||
message: "Runtime trapped while querying storage.".into(),
|
|
||||||
data: Some(format!("{:?}", e).into()),
|
|
||||||
})?
|
|
||||||
.map_err(GetStorageError)?
|
|
||||||
.map(Bytes);
|
.map(Bytes);
|
||||||
|
|
||||||
Ok(get_storage_result)
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rent_projection(
|
||||||
|
&self,
|
||||||
|
address: AccountId,
|
||||||
|
at: Option<<Block as BlockT>::Hash>,
|
||||||
|
) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
|
||||||
|
let api = self.client.runtime_api();
|
||||||
|
let at = BlockId::hash(at.unwrap_or_else(||
|
||||||
|
// If the block hash is not supplied assume the best block.
|
||||||
|
self.client.info().best_hash));
|
||||||
|
|
||||||
|
let result = api
|
||||||
|
.rent_projection(&at, address)
|
||||||
|
.map_err(|e| runtime_error_into_rpc_err(e))?
|
||||||
|
.map_err(ContractAccessError)?;
|
||||||
|
|
||||||
|
Ok(match result {
|
||||||
|
RentProjection::NoEviction => None,
|
||||||
|
RentProjection::EvictionAt(block_num) => Some(block_num),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a runtime trap into an RPC error.
|
||||||
|
fn runtime_error_into_rpc_err(err: impl std::fmt::Debug) -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(RUNTIME_ERROR),
|
||||||
|
message: "Runtime trapped".into(),
|
||||||
|
data: Some(format!("{:?}", err).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +284,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_serialize_deserialize_properly() {
|
fn should_serialize_deserialize_properly() {
|
||||||
fn test(expected: &str) {
|
fn test(expected: &str) {
|
||||||
let res: RpcContractExecResult = serde_json::from_str(expected).unwrap();
|
let res: RpcContractExecResult = serde_json::from_str(expected).unwrap();
|
||||||
let actual = serde_json::to_string(&res).unwrap();
|
let actual = serde_json::to_string(&res).unwrap();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ use frame_support::{
|
|||||||
use frame_support::traits::{OnFreeBalanceZero, OnUnbalanced, Currency, Get, Time, Randomness};
|
use frame_support::traits::{OnFreeBalanceZero, OnUnbalanced, Currency, Get, Time, Randomness};
|
||||||
use frame_system::{self as system, ensure_signed, RawOrigin, ensure_root};
|
use frame_system::{self as system, ensure_signed, RawOrigin, ensure_root};
|
||||||
use sp_core::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX;
|
use sp_core::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX;
|
||||||
|
use pallet_contracts_primitives::{RentProjection, ContractAccessError};
|
||||||
|
|
||||||
pub type CodeHash<T> = <T as frame_system::Trait>::Hash;
|
pub type CodeHash<T> = <T as frame_system::Trait>::Hash;
|
||||||
pub type TrieId = Vec<u8>;
|
pub type TrieId = Vec<u8>;
|
||||||
@@ -680,14 +681,6 @@ decl_module! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The possible errors that can happen querying the storage of a contract.
|
|
||||||
pub enum GetStorageError {
|
|
||||||
/// The given address doesn't point on a contract.
|
|
||||||
ContractDoesntExist,
|
|
||||||
/// The specified contract is a tombstone and thus cannot have any storage.
|
|
||||||
IsTombstone,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Public APIs provided by the contracts module.
|
/// Public APIs provided by the contracts module.
|
||||||
impl<T: Trait> Module<T> {
|
impl<T: Trait> Module<T> {
|
||||||
/// Perform a call to a specified contract.
|
/// Perform a call to a specified contract.
|
||||||
@@ -710,11 +703,11 @@ impl<T: Trait> Module<T> {
|
|||||||
pub fn get_storage(
|
pub fn get_storage(
|
||||||
address: T::AccountId,
|
address: T::AccountId,
|
||||||
key: [u8; 32],
|
key: [u8; 32],
|
||||||
) -> sp_std::result::Result<Option<Vec<u8>>, GetStorageError> {
|
) -> sp_std::result::Result<Option<Vec<u8>>, ContractAccessError> {
|
||||||
let contract_info = <ContractInfoOf<T>>::get(&address)
|
let contract_info = <ContractInfoOf<T>>::get(&address)
|
||||||
.ok_or(GetStorageError::ContractDoesntExist)?
|
.ok_or(ContractAccessError::DoesntExist)?
|
||||||
.get_alive()
|
.get_alive()
|
||||||
.ok_or(GetStorageError::IsTombstone)?;
|
.ok_or(ContractAccessError::IsTombstone)?;
|
||||||
|
|
||||||
let maybe_value = AccountDb::<T>::get_storage(
|
let maybe_value = AccountDb::<T>::get_storage(
|
||||||
&DirectAccountDb,
|
&DirectAccountDb,
|
||||||
@@ -724,6 +717,12 @@ impl<T: Trait> Module<T> {
|
|||||||
);
|
);
|
||||||
Ok(maybe_value)
|
Ok(maybe_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rent_projection(
|
||||||
|
address: T::AccountId,
|
||||||
|
) -> sp_std::result::Result<RentProjection<T::BlockNumber>, ContractAccessError> {
|
||||||
|
rent::compute_rent_projection::<T>(&address)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Trait> Module<T> {
|
impl<T: Trait> Module<T> {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use crate::{
|
|||||||
use frame_support::storage::child;
|
use frame_support::storage::child;
|
||||||
use frame_support::traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReason};
|
use frame_support::traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReason};
|
||||||
use frame_support::StorageMap;
|
use frame_support::StorageMap;
|
||||||
|
use pallet_contracts_primitives::{ContractAccessError, RentProjection, RentProjectionResult};
|
||||||
use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, SaturatedConversion, Saturating, Zero};
|
use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, SaturatedConversion, Saturating, Zero};
|
||||||
|
|
||||||
/// The amount to charge.
|
/// The amount to charge.
|
||||||
@@ -74,9 +75,57 @@ enum Verdict<T: Trait> {
|
|||||||
amount: Option<OutstandingAmount<T>>,
|
amount: Option<OutstandingAmount<T>>,
|
||||||
},
|
},
|
||||||
/// Everything is OK, we just only take some charge.
|
/// Everything is OK, we just only take some charge.
|
||||||
Charge {
|
Charge { amount: OutstandingAmount<T> },
|
||||||
amount: OutstandingAmount<T>,
|
}
|
||||||
},
|
|
||||||
|
/// Returns a fee charged per block from the contract.
|
||||||
|
///
|
||||||
|
/// This function accounts for the storage rent deposit. I.e. if the contract possesses enough funds
|
||||||
|
/// then the fee can drop to zero.
|
||||||
|
fn compute_fee_per_block<T: Trait>(
|
||||||
|
balance: &BalanceOf<T>,
|
||||||
|
contract: &AliveContractInfo<T>,
|
||||||
|
) -> BalanceOf<T> {
|
||||||
|
let free_storage = balance
|
||||||
|
.checked_div(&T::RentDepositOffset::get())
|
||||||
|
.unwrap_or_else(Zero::zero);
|
||||||
|
|
||||||
|
let effective_storage_size =
|
||||||
|
<BalanceOf<T>>::from(contract.storage_size).saturating_sub(free_storage);
|
||||||
|
|
||||||
|
effective_storage_size
|
||||||
|
.checked_mul(&T::RentByteFee::get())
|
||||||
|
.unwrap_or(<BalanceOf<T>>::max_value())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subsistence threshold is the extension of the minimum balance (aka existential deposit) by the
|
||||||
|
/// tombstone deposit, required for leaving a tombstone.
|
||||||
|
///
|
||||||
|
/// Rent mechanism cannot make the balance lower than subsistence threshold.
|
||||||
|
fn subsistence_threshold<T: Trait>() -> BalanceOf<T> {
|
||||||
|
T::Currency::minimum_balance() + T::TombstoneDeposit::get()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns amount of funds available to consume by rent mechanism.
|
||||||
|
///
|
||||||
|
/// Rent mechanism cannot consume more than `rent_allowance` set by the contract and it cannot make
|
||||||
|
/// the balance lower than [`subsistence_threshold`].
|
||||||
|
///
|
||||||
|
/// In case the balance is below the subsistence threshold, this function returns `None`.
|
||||||
|
fn rent_budget<T: Trait>(
|
||||||
|
balance: &BalanceOf<T>,
|
||||||
|
contract: &AliveContractInfo<T>,
|
||||||
|
) -> Option<BalanceOf<T>> {
|
||||||
|
let subsistence_threshold = subsistence_threshold::<T>();
|
||||||
|
if *balance < subsistence_threshold {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rent_allowed_to_charge = *balance - subsistence_threshold;
|
||||||
|
Some(<BalanceOf<T>>::min(
|
||||||
|
contract.rent_allowance,
|
||||||
|
rent_allowed_to_charge,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consider the case for rent payment of the given account and returns a `Verdict`.
|
/// Consider the case for rent payment of the given account and returns a `Verdict`.
|
||||||
@@ -103,37 +152,27 @@ fn consider_case<T: Trait>(
|
|||||||
let balance = T::Currency::free_balance(account);
|
let balance = T::Currency::free_balance(account);
|
||||||
|
|
||||||
// An amount of funds to charge per block for storage taken up by the contract.
|
// An amount of funds to charge per block for storage taken up by the contract.
|
||||||
let fee_per_block = {
|
let fee_per_block = compute_fee_per_block::<T>(&balance, contract);
|
||||||
let free_storage = balance
|
|
||||||
.checked_div(&T::RentDepositOffset::get())
|
|
||||||
.unwrap_or_else(Zero::zero);
|
|
||||||
|
|
||||||
let effective_storage_size =
|
|
||||||
<BalanceOf<T>>::from(contract.storage_size).saturating_sub(free_storage);
|
|
||||||
|
|
||||||
effective_storage_size
|
|
||||||
.checked_mul(&T::RentByteFee::get())
|
|
||||||
.unwrap_or(<BalanceOf<T>>::max_value())
|
|
||||||
};
|
|
||||||
|
|
||||||
if fee_per_block.is_zero() {
|
if fee_per_block.is_zero() {
|
||||||
// The rent deposit offset reduced the fee to 0. This means that the contract
|
// The rent deposit offset reduced the fee to 0. This means that the contract
|
||||||
// gets the rent for free.
|
// gets the rent for free.
|
||||||
return Verdict::Exempt;
|
return Verdict::Exempt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The minimal amount of funds required for a contract not to be evicted.
|
let rent_budget = match rent_budget::<T>(&balance, contract) {
|
||||||
let subsistence_threshold = T::Currency::minimum_balance() + T::TombstoneDeposit::get();
|
Some(rent_budget) => rent_budget,
|
||||||
|
None => {
|
||||||
if balance < subsistence_threshold {
|
// The contract's balance is already below subsistence threshold. That indicates that
|
||||||
// The contract cannot afford to leave a tombstone, so remove the contract info altogether.
|
// the contract cannot afford to leave a tombstone.
|
||||||
return Verdict::Kill;
|
//
|
||||||
}
|
// So cleanly wipe the contract.
|
||||||
|
return Verdict::Kill;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let dues = fee_per_block
|
let dues = fee_per_block
|
||||||
.checked_mul(&blocks_passed.saturated_into::<u32>().into())
|
.checked_mul(&blocks_passed.saturated_into::<u32>().into())
|
||||||
.unwrap_or(<BalanceOf<T>>::max_value());
|
.unwrap_or(<BalanceOf<T>>::max_value());
|
||||||
let rent_budget = contract.rent_allowance.min(balance - subsistence_threshold);
|
|
||||||
let insufficient_rent = rent_budget < dues;
|
let insufficient_rent = rent_budget < dues;
|
||||||
|
|
||||||
// If the rent payment cannot be withdrawn due to locks on the account balance, then evict the
|
// If the rent payment cannot be withdrawn due to locks on the account balance, then evict the
|
||||||
@@ -284,3 +323,69 @@ pub fn snitch_contract_should_be_evicted<T: Trait>(
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the projected time a given contract will be able to sustain paying its rent. The
|
||||||
|
/// returned projection is relevent for the current block, i.e. it is as if the contract was
|
||||||
|
/// accessed at the beginning of the current block. Returns `None` in case if the contract was
|
||||||
|
/// evicted before or as a result of the rent collection.
|
||||||
|
///
|
||||||
|
/// The returned value is only an estimation. It doesn't take into account any top ups, changing the
|
||||||
|
/// rent allowance, or any problems coming from withdrawing the dues.
|
||||||
|
///
|
||||||
|
/// NOTE that this is not a side-effect free function! It will actually collect rent and then
|
||||||
|
/// compute the projection. This function is only used for implementation of an RPC method through
|
||||||
|
/// `RuntimeApi` meaning that the changes will be discarded anyway.
|
||||||
|
pub fn compute_rent_projection<T: Trait>(
|
||||||
|
account: &T::AccountId,
|
||||||
|
) -> RentProjectionResult<T::BlockNumber> {
|
||||||
|
let contract_info = <ContractInfoOf<T>>::get(account);
|
||||||
|
let alive_contract_info = match contract_info {
|
||||||
|
None | Some(ContractInfo::Tombstone(_)) => return Err(ContractAccessError::IsTombstone),
|
||||||
|
Some(ContractInfo::Alive(contract)) => contract,
|
||||||
|
};
|
||||||
|
let current_block_number = <frame_system::Module<T>>::block_number();
|
||||||
|
let verdict = consider_case::<T>(
|
||||||
|
account,
|
||||||
|
current_block_number,
|
||||||
|
Zero::zero(),
|
||||||
|
&alive_contract_info,
|
||||||
|
);
|
||||||
|
let new_contract_info =
|
||||||
|
enact_verdict(account, alive_contract_info, current_block_number, verdict);
|
||||||
|
|
||||||
|
// Check what happened after enaction of the verdict.
|
||||||
|
let alive_contract_info = match new_contract_info {
|
||||||
|
None | Some(ContractInfo::Tombstone(_)) => return Err(ContractAccessError::IsTombstone),
|
||||||
|
Some(ContractInfo::Alive(contract)) => contract,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compute how much would the fee per block be with the *updated* balance.
|
||||||
|
let balance = T::Currency::free_balance(account);
|
||||||
|
let fee_per_block = compute_fee_per_block::<T>(&balance, &alive_contract_info);
|
||||||
|
if fee_per_block.is_zero() {
|
||||||
|
return Ok(RentProjection::NoEviction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then compute how much the contract will sustain under these circumstances.
|
||||||
|
let rent_budget = rent_budget::<T>(&balance, &alive_contract_info).expect(
|
||||||
|
"the contract exists and in the alive state;
|
||||||
|
the updated balance must be greater than subsistence deposit;
|
||||||
|
this function doesn't return `None`;
|
||||||
|
qed
|
||||||
|
",
|
||||||
|
);
|
||||||
|
let blocks_left = match rent_budget.checked_div(&fee_per_block) {
|
||||||
|
Some(blocks_left) => blocks_left,
|
||||||
|
None => {
|
||||||
|
// `fee_per_block` is not zero here, so `checked_div` can return `None` if
|
||||||
|
// there is an overflow. This cannot happen with integers though. Return
|
||||||
|
// `NoEviction` here just in case.
|
||||||
|
return Ok(RentProjection::NoEviction);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let blocks_left = blocks_left.saturated_into::<u32>().into();
|
||||||
|
Ok(RentProjection::EvictionAt(
|
||||||
|
current_block_number + blocks_left,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user