Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

contracts: Add RPC that allows instantiating of a contract #8451

Merged
9 commits merged into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
532 changes: 471 additions & 61 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bin/node/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ pub fn create_full<C, P, SC, B>(
C: ProvideRuntimeApi<Block> + HeaderBackend<Block> + AuxStore +
HeaderMetadata<Block, Error=BlockChainError> + Sync + Send + 'static,
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>,
C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance, BlockNumber>,
C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance, BlockNumber, Hash>,
C::Api: pallet_mmr_rpc::MmrRuntimeApi<Block, <Block as sp_runtime::traits::Block>::Hash>,
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
C::Api: BabeApi<Block>,
Expand Down
16 changes: 15 additions & 1 deletion bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,9 @@ impl_runtime_apis! {
}
}

impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber>
impl pallet_contracts_rpc_runtime_api::ContractsApi<
Block, AccountId, Balance, BlockNumber, Hash,
>
for Runtime
{
fn call(
Expand All @@ -1343,6 +1345,18 @@ impl_runtime_apis! {
Contracts::bare_call(origin, dest, value, gas_limit, input_data)
}

fn instantiate(
origin: AccountId,
endowment: Balance,
gas_limit: u64,
code: pallet_contracts_primitives::Code<Hash>,
athei marked this conversation as resolved.
Show resolved Hide resolved
data: Vec<u8>,
salt: Vec<u8>,
) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, BlockNumber>
{
Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true)
}

fn get_storage(
address: AccountId,
key: [u8; 32],
Expand Down
2 changes: 2 additions & 0 deletions frame/contracts/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ In other words: Upgrading this pallet will not break pre-existing contracts.

### Added

- Add new `instantiate` RPC that allows clients to dry-run contract instantiation.

- Make storage and fields of `Schedule` private to the crate.
[1](https://github.com/paritytech/substrate/pull/8359)

Expand Down
4 changes: 4 additions & 0 deletions frame/contracts/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ targets = ["x86_64-unknown-linux-gnu"]
# This crate should not rely on any of the frame primitives.
bitflags = "1.0"
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
sp-core = { version = "3.0.0", path = "../../../primitives/core", default-features = false }
sp-std = { version = "3.0.0", default-features = false, path = "../../../primitives/std" }
sp-runtime = { version = "3.0.0", default-features = false, path = "../../../primitives/runtime" }
serde = { version = "1", features = ["derive"], optional = true }

[features]
default = ["std"]
std = [
"codec/std",
"sp-core/std",
"sp-runtime/std",
"sp-std/std",
"serde",
]
96 changes: 59 additions & 37 deletions frame/contracts/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,41 @@

use bitflags::bitflags;
use codec::{Decode, Encode};
use sp_core::Bytes;
use sp_runtime::{DispatchError, RuntimeDebug};
use sp_std::prelude::*;

/// Result type of a `bare_call` call.
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};

/// Result type of a `bare_call` or `bare_instantiate` call.
///
/// The result of a contract execution along with a gas consumed.
/// It contains the execution result together with some auxiliary information.
#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
pub struct ContractExecResult {
pub exec_result: ExecResult,
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct ContractResult<T> {
/// How much gas was consumed during execution.
pub gas_consumed: u64,
/// An optional debug message. This message is only non-empty when explicitly requested
/// by the code that calls into the contract.
///
/// # Note
///
/// The debug message is never generated during on-chain execution. It is reserved for
/// RPC calls.
pub debug_message: String,
/// The execution result of the wasm code.
pub result: T,
}

/// Result type of a `bare_call` call.
pub type ContractExecResult = ContractResult<Result<ExecReturnValue, DispatchError>>;

/// Result type of a `bare_instantiate` call.
pub type ContractInstantiateResult<AccountId, BlockNumber> =
ContractResult<Result<InstantiateReturnValue<AccountId, BlockNumber>, DispatchError>>;

/// Result type of a `get_storage` call.
pub type GetStorageResult = Result<Option<Vec<u8>>, ContractAccessError>;

Expand All @@ -50,6 +73,8 @@ pub enum ContractAccessError {
}

#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub enum RentProjection<BlockNumber> {
/// Eviction is projected to happen at the specified block number.
EvictionAt(BlockNumber),
Expand All @@ -62,6 +87,8 @@ pub enum RentProjection<BlockNumber> {
bitflags! {
/// Flags used by a contract to customize exit behaviour.
#[derive(Encode, Decode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase", transparent))]
pub struct ReturnFlags: u32 {
/// If this bit is set all changes made by the contract execution are rolled back.
const REVERT = 0x0000_0001;
Expand All @@ -70,11 +97,14 @@ bitflags! {

/// Output of a contract call or instantiation which ran to completion.
#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)]
#[cfg_attr(not(feature = "std"), derive(Encode, Decode))]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct ExecReturnValue {
/// Flags passed along by `seal_return`. Empty when `seal_return` was never called.
pub flags: ReturnFlags,
/// Buffer passed along by `seal_return`. Empty when `seal_return` was never called.
pub data: Vec<u8>,
pub data: Bytes,
}

impl ExecReturnValue {
Expand All @@ -84,40 +114,32 @@ impl ExecReturnValue {
}
}

/// Origin of the error.
///
/// Call or instantiate both called into other contracts and pass through errors happening
/// in those to the caller. This enum is for the caller to distinguish whether the error
/// happened during the execution of the callee or in the current execution context.
/// The result of a successful contract instantiation.
#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)]
pub enum ErrorOrigin {
/// Caller error origin.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct InstantiateReturnValue<AccountId, BlockNumber> {
/// The output of the called constructor.
pub result: ExecReturnValue,
/// The account id of the new contract.
pub account_id: AccountId,
/// Information about when and if the new project will be evicted.
///
/// The error happened in the current exeuction context rather than in the one
/// of the contract that is called into.
Caller,
/// The error happened during execution of the called contract.
Callee,
}

/// Error returned by contract exection.
#[derive(PartialEq, Eq, Encode, Decode, RuntimeDebug)]
pub struct ExecError {
/// The reason why the execution failed.
pub error: DispatchError,
/// Origin of the error.
pub origin: ErrorOrigin,
/// # Note
///
/// `None` if `bare_instantiate` was called with
/// `compute_projection` set to false. From the perspective of an RPC this means that
/// the runtime API did not request this value and this feature is therefore unsupported.
pub rent_projection: Option<RentProjection<BlockNumber>>,
}

impl<T: Into<DispatchError>> From<T> for ExecError {
fn from(error: T) -> Self {
Self {
error: error.into(),
origin: ErrorOrigin::Caller,
}
}
/// Reference to an existing code hash or a new wasm module.
#[derive(Eq, PartialEq, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub enum Code<Hash> {
/// A wasm module as raw bytes.
Upload(Bytes),
/// The code hash of an on-chain wasm blob.
Existing(Hash),
}

/// The result that is returned from contract execution. It either contains the output
/// buffer or an error describing the reason for failure.
pub type ExecResult = Result<ExecReturnValue, ExecError>;
21 changes: 18 additions & 3 deletions frame/contracts/rpc/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,21 @@

use codec::Codec;
use sp_std::vec::Vec;
use pallet_contracts_primitives::{ContractExecResult, GetStorageResult, RentProjectionResult};
use pallet_contracts_primitives::{
ContractExecResult, GetStorageResult, RentProjectionResult, Code, ContractInstantiateResult,
};

sp_api::decl_runtime_apis! {
/// The API to interact with contracts without using executive.
pub trait ContractsApi<AccountId, Balance, BlockNumber> where
pub trait ContractsApi<AccountId, Balance, BlockNumber, Hash> where
AccountId: Codec,
Balance: Codec,
BlockNumber: Codec,
Hash: Codec,
{
/// Perform a call from a specified account to a given contract.
///
/// See the contracts' `call` dispatchable function for more details.
/// See [`pallet_contracts::Pallet::call`].
fn call(
origin: AccountId,
dest: AccountId,
Expand All @@ -45,6 +48,18 @@ sp_api::decl_runtime_apis! {
input_data: Vec<u8>,
) -> ContractExecResult;

/// Instantiate a new contract.
///
/// See [`pallet_contracts::Pallet::instantiate`].
fn instantiate(
origin: AccountId,
endowment: Balance,
gas_limit: u64,
code: Code<Hash>,
data: Vec<u8>,
salt: Vec<u8>,
) -> ContractInstantiateResult<AccountId, BlockNumber>;

/// Query a given storage key in a given contract.
///
/// Returns `Ok(Some(Vec<u8>))` if the storage value exists under the given key in the
Expand Down
Loading