Contracts: use compiled rust tests (#2347)

see #2189

This PR does the following:
- Bring the user api functions into a new pallet-contracts-uapi (They
are currently defined in ink!
[here])(https://github.com/paritytech/ink/blob/master/crates/env/src/engine/on_chain/ext.rs)
- Add older api versions and unstable to the user api trait.
- Remove pallet-contracts-primitives and bring the types it defined in
uapi / pallet-contracts
- Add the infrastructure to build fixtures from Rust files and test it
works by replacing `dummy.wat` and `call.wat`
- Move all the doc from wasm/runtime.rs to pallet-contracts-uapi.

This will be done in a follow up:
- convert the rest of the test from .wat to rust
- bring risc-v uapi up to date with wasm
- finalize the uapi host fns, making sure everything is codegen from the
source host fns in pallet-contracts

---------

Co-authored-by: Alexander Theißen <alex.theissen@me.com>
This commit is contained in:
PG Herveou
2023-11-29 22:12:19 +01:00
committed by GitHub
parent f69069bd42
commit 2135fa872b
38 changed files with 2520 additions and 977 deletions
Generated
+66 -26
View File
@@ -2834,7 +2834,6 @@ dependencies = [
"pallet-balances",
"pallet-collator-selection",
"pallet-contracts",
"pallet-contracts-primitives",
"pallet-insecure-randomness-collective-flip",
"pallet-message-queue",
"pallet-multisig",
@@ -6828,7 +6827,6 @@ dependencies = [
"pallet-child-bounties",
"pallet-collective",
"pallet-contracts",
"pallet-contracts-primitives",
"pallet-conviction-voting",
"pallet-core-fellowship",
"pallet-democracy",
@@ -8465,7 +8463,7 @@ dependencies = [
"itertools 0.10.5",
"tar",
"tempfile",
"toml_edit",
"toml_edit 0.19.14",
]
[[package]]
@@ -9380,8 +9378,8 @@ dependencies = [
"pallet-assets",
"pallet-balances",
"pallet-contracts-fixtures",
"pallet-contracts-primitives",
"pallet-contracts-proc-macro",
"pallet-contracts-uapi",
"pallet-insecure-randomness-collective-flip",
"pallet-message-queue",
"pallet-proxy",
@@ -9412,11 +9410,21 @@ dependencies = [
name = "pallet-contracts-fixtures"
version = "1.0.0"
dependencies = [
"anyhow",
"cfg-if",
"frame-system",
"parity-wasm",
"sp-runtime",
"tempfile",
"toml 0.8.8",
"twox-hash",
"wat",
]
[[package]]
name = "pallet-contracts-fixtures-common"
version = "1.0.0"
[[package]]
name = "pallet-contracts-mock-network"
version = "1.0.0"
@@ -9428,8 +9436,8 @@ dependencies = [
"pallet-balances",
"pallet-contracts",
"pallet-contracts-fixtures",
"pallet-contracts-primitives",
"pallet-contracts-proc-macro",
"pallet-contracts-uapi",
"pallet-insecure-randomness-collective-flip",
"pallet-message-queue",
"pallet-proxy",
@@ -9455,18 +9463,6 @@ dependencies = [
"xcm-simulator",
]
[[package]]
name = "pallet-contracts-primitives"
version = "24.0.0"
dependencies = [
"bitflags 1.3.2",
"parity-scale-codec",
"scale-info",
"sp-runtime",
"sp-std 8.0.0",
"sp-weights",
]
[[package]]
name = "pallet-contracts-proc-macro"
version = "4.0.0-dev"
@@ -9476,6 +9472,16 @@ dependencies = [
"syn 2.0.38",
]
[[package]]
name = "pallet-contracts-uapi"
version = "4.0.0-dev"
dependencies = [
"bitflags 1.3.2",
"parity-scale-codec",
"paste",
"scale-info",
]
[[package]]
name = "pallet-conviction-voting"
version = "4.0.0-dev"
@@ -13415,7 +13421,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit",
"toml_edit 0.19.14",
]
[[package]]
@@ -13878,6 +13884,15 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_users"
version = "0.4.3"
@@ -16400,9 +16415,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "0.6.3"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80"
dependencies = [
"serde",
]
@@ -18758,13 +18773,13 @@ checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
[[package]]
name = "tempfile"
version = "3.8.0"
version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
dependencies = [
"cfg-if",
"fastrand 2.0.0",
"redox_syscall 0.3.5",
"redox_syscall 0.4.1",
"rustix 0.38.21",
"windows-sys 0.48.0",
]
@@ -19227,14 +19242,26 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
"toml_edit 0.19.14",
]
[[package]]
name = "toml"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.21.0",
]
[[package]]
name = "toml_datetime"
version = "0.6.3"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
@@ -19252,6 +19279,19 @@ dependencies = [
"winnow",
]
[[package]]
name = "toml_edit"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
dependencies = [
"indexmap 2.0.0",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "tower"
version = "0.4.13"
+2 -1
View File
@@ -283,8 +283,9 @@ members = [
"substrate/frame/collective",
"substrate/frame/contracts",
"substrate/frame/contracts/fixtures",
"substrate/frame/contracts/fixtures/contracts/common",
"substrate/frame/contracts/uapi",
"substrate/frame/contracts/mock-network",
"substrate/frame/contracts/primitives",
"substrate/frame/contracts/proc-macro",
"substrate/frame/conviction-voting",
"substrate/frame/core-fellowship",
@@ -52,7 +52,6 @@ pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/
pallet-utility = { path = "../../../../../substrate/frame/utility", default-features = false}
pallet-sudo = { path = "../../../../../substrate/frame/sudo", default-features = false}
pallet-contracts = { path = "../../../../../substrate/frame/contracts", default-features = false}
pallet-contracts-primitives = { path = "../../../../../substrate/frame/contracts/primitives", default-features = false}
# Polkadot
pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false }
@@ -102,7 +101,6 @@ std = [
"pallet-authorship/std",
"pallet-balances/std",
"pallet-collator-selection/std",
"pallet-contracts-primitives/std",
"pallet-contracts/std",
"pallet-insecure-randomness-collective-flip/std",
"pallet-message-queue/std",
@@ -588,7 +588,7 @@ impl_runtime_apis! {
gas_limit: Option<Weight>,
storage_deposit_limit: Option<Balance>,
input_data: Vec<u8>,
) -> pallet_contracts_primitives::ContractExecResult<Balance, EventRecord> {
) -> pallet_contracts::ContractExecResult<Balance, EventRecord> {
let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block);
Contracts::bare_call(
origin,
@@ -608,10 +608,10 @@ impl_runtime_apis! {
value: Balance,
gas_limit: Option<Weight>,
storage_deposit_limit: Option<Balance>,
code: pallet_contracts_primitives::Code<Hash>,
code: pallet_contracts::Code<Hash>,
data: Vec<u8>,
salt: Vec<u8>,
) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, Balance, EventRecord> {
) -> pallet_contracts::ContractInstantiateResult<AccountId, Balance, EventRecord> {
let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block);
Contracts::bare_instantiate(
origin,
@@ -631,7 +631,7 @@ impl_runtime_apis! {
code: Vec<u8>,
storage_deposit_limit: Option<Balance>,
determinism: pallet_contracts::Determinism,
) -> pallet_contracts_primitives::CodeUploadResult<Hash, Balance> {
) -> pallet_contracts::CodeUploadResult<Hash, Balance> {
Contracts::bare_upload_code(
origin,
code,
@@ -643,7 +643,7 @@ impl_runtime_apis! {
fn get_storage(
address: AccountId,
key: Vec<u8>,
) -> pallet_contracts_primitives::GetStorageResult {
) -> pallet_contracts::GetStorageResult {
Contracts::get_storage(address, key)
}
}
-2
View File
@@ -74,7 +74,6 @@ pallet-broker = { path = "../../../frame/broker", default-features = false}
pallet-child-bounties = { path = "../../../frame/child-bounties", default-features = false}
pallet-collective = { path = "../../../frame/collective", default-features = false}
pallet-contracts = { path = "../../../frame/contracts", default-features = false}
pallet-contracts-primitives = { path = "../../../frame/contracts/primitives", default-features = false}
pallet-conviction-voting = { path = "../../../frame/conviction-voting", default-features = false}
pallet-core-fellowship = { path = "../../../frame/core-fellowship", default-features = false}
pallet-democracy = { path = "../../../frame/democracy", default-features = false}
@@ -171,7 +170,6 @@ std = [
"pallet-broker/std",
"pallet-child-bounties/std",
"pallet-collective/std",
"pallet-contracts-primitives/std",
"pallet-contracts/std",
"pallet-conviction-voting/std",
"pallet-core-fellowship/std",
+5 -5
View File
@@ -2497,7 +2497,7 @@ impl_runtime_apis! {
gas_limit: Option<Weight>,
storage_deposit_limit: Option<Balance>,
input_data: Vec<u8>,
) -> pallet_contracts_primitives::ContractExecResult<Balance, EventRecord> {
) -> pallet_contracts::ContractExecResult<Balance, EventRecord> {
let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block);
Contracts::bare_call(
origin,
@@ -2517,10 +2517,10 @@ impl_runtime_apis! {
value: Balance,
gas_limit: Option<Weight>,
storage_deposit_limit: Option<Balance>,
code: pallet_contracts_primitives::Code<Hash>,
code: pallet_contracts::Code<Hash>,
data: Vec<u8>,
salt: Vec<u8>,
) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, Balance, EventRecord>
) -> pallet_contracts::ContractInstantiateResult<AccountId, Balance, EventRecord>
{
let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block);
Contracts::bare_instantiate(
@@ -2541,7 +2541,7 @@ impl_runtime_apis! {
code: Vec<u8>,
storage_deposit_limit: Option<Balance>,
determinism: pallet_contracts::Determinism,
) -> pallet_contracts_primitives::CodeUploadResult<Hash, Balance>
) -> pallet_contracts::CodeUploadResult<Hash, Balance>
{
Contracts::bare_upload_code(
origin,
@@ -2554,7 +2554,7 @@ impl_runtime_apis! {
fn get_storage(
address: AccountId,
key: Vec<u8>,
) -> pallet_contracts_primitives::GetStorageResult {
) -> pallet_contracts::GetStorageResult {
Contracts::get_storage(
address,
key
+1 -3
View File
@@ -40,7 +40,7 @@ frame-benchmarking = { path = "../benchmarking", default-features = false, optio
frame-support = { path = "../support", default-features = false}
frame-system = { path = "../system", default-features = false}
pallet-balances = { path = "../balances", default-features = false , optional = true}
pallet-contracts-primitives = { path = "primitives", default-features = false}
pallet-contracts-uapi = { path = "uapi" }
pallet-contracts-proc-macro = { path = "proc-macro" }
sp-api = { path = "../../primitives/api", default-features = false}
sp-core = { path = "../../primitives/core", default-features = false}
@@ -83,8 +83,6 @@ std = [
"frame-system/std",
"log/std",
"pallet-balances?/std",
"pallet-contracts-fixtures/std",
"pallet-contracts-primitives/std",
"pallet-contracts-proc-macro/full",
"pallet-insecure-randomness-collective-flip/std",
"pallet-proxy/std",
-1
View File
@@ -68,6 +68,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
version - 1,
)?;
println!("cargo:rerun-if-changed=src/migration");
Ok(())
}
+11 -5
View File
@@ -9,10 +9,16 @@ description = "Fixtures for testing contracts pallet."
[dependencies]
wat = "1"
frame-system = { path = "../../system", default-features = false}
sp-runtime = { path = "../../../primitives/runtime", default-features = false}
frame-system = { path = "../../system" }
sp-runtime = { path = "../../../primitives/runtime" }
anyhow = "1.0.0"
[build-dependencies]
parity-wasm = "0.45.0"
tempfile = "3.8.1"
toml = "0.8.8"
twox-hash = "1.6.3"
anyhow = "1.0.0"
cfg-if = { version = "1.0", default-features = false }
[features]
default = [ "std" ]
std = [ "frame-system/std", "sp-runtime/std" ]
+277
View File
@@ -0,0 +1,277 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Compile contracts to wasm and RISC-V binaries.
use anyhow::Result;
use parity_wasm::elements::{deserialize_file, serialize_to_file, Internal};
use std::{
env, fs,
hash::Hasher,
path::{Path, PathBuf},
process::Command,
};
use twox_hash::XxHash32;
/// Read the file at `path` and return its hash as a hex string.
fn file_hash(path: &Path) -> String {
let data = fs::read(path).expect("file exists; qed");
let mut hasher = XxHash32::default();
hasher.write(&data);
hasher.write(include_bytes!("build.rs"));
let hash = hasher.finish();
format!("{:x}", hash)
}
/// A contract entry.
struct Entry {
/// The path to the contract source file.
path: PathBuf,
/// The hash of the contract source file.
hash: String,
}
impl Entry {
/// Create a new contract entry from the given path.
fn new(path: PathBuf) -> Self {
let hash = file_hash(&path);
Self { path, hash }
}
/// Return the path to the contract source file.
fn path(&self) -> &str {
self.path.to_str().expect("path is valid unicode; qed")
}
/// Return the name of the contract.
fn name(&self) -> &str {
self.path
.file_stem()
.expect("file exits; qed")
.to_str()
.expect("name is valid unicode; qed")
}
/// Return the name of the output wasm file.
fn out_wasm_filename(&self) -> String {
format!("{}.wasm", self.name())
}
}
/// Collect all contract entries from the given source directory.
/// Contracts that have already been compiled are filtered out.
fn collect_entries(contracts_dir: &Path, out_dir: &Path) -> Vec<Entry> {
fs::read_dir(&contracts_dir)
.expect("src dir exists; qed")
.filter_map(|file| {
let path = file.expect("file exists; qed").path();
if path.extension().map_or(true, |ext| ext != "rs") {
return None;
}
let entry = Entry::new(path);
if out_dir.join(&entry.hash).exists() {
None
} else {
Some(entry)
}
})
.collect::<Vec<_>>()
}
/// Create a `Cargo.toml` to compile the given contract entries.
fn create_cargo_toml<'a>(
fixtures_dir: &Path,
entries: impl Iterator<Item = &'a Entry>,
output_dir: &Path,
) -> Result<()> {
let uapi_path = fixtures_dir.join("../uapi").canonicalize()?;
let common_path = fixtures_dir.join("./contracts/common").canonicalize()?;
let mut cargo_toml: toml::Value = toml::from_str(&format!(
"
[package]
name = 'contracts'
version = '0.1.0'
edition = '2021'
# Binary targets are injected below.
[[bin]]
[dependencies]
uapi = {{ package = 'pallet-contracts-uapi', default-features = false, path = {uapi_path:?}}}
common = {{ package = 'pallet-contracts-fixtures-common', path = {common_path:?}}}
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
"
))?;
let binaries = entries
.map(|entry| {
let name = entry.name();
let path = entry.path();
toml::Value::Table(toml::toml! {
name = name
path = path
})
})
.collect::<Vec<_>>();
cargo_toml["bin"] = toml::Value::Array(binaries);
let cargo_toml = toml::to_string_pretty(&cargo_toml)?;
fs::write(output_dir.join("Cargo.toml"), cargo_toml).map_err(Into::into)
}
/// Invoke `cargo fmt` to check that fixtures files are formatted.
fn invoke_cargo_fmt<'a>(
config_path: &Path,
files: impl Iterator<Item = &'a Path>,
contract_dir: &Path,
) -> Result<()> {
// If rustfmt is not installed, skip the check.
if !Command::new("rustup")
.args(&["run", "nightly", "rustfmt", "--version"])
.output()
.expect("failed to execute process")
.status
.success()
{
return Ok(())
}
let fmt_res = Command::new("rustup")
.args(&["run", "nightly", "rustfmt", "--check", "--config-path"])
.arg(config_path)
.args(files)
.output()
.expect("failed to execute process");
if fmt_res.status.success() {
return Ok(())
}
let stdout = String::from_utf8_lossy(&fmt_res.stdout);
let stderr = String::from_utf8_lossy(&fmt_res.stderr);
eprintln!("{}\n{}", stdout, stderr);
eprintln!(
"Fixtures files are not formatted.\nPlease run `rustup run nightly rustfmt --config-path {} {}/*.rs`",
config_path.display(),
contract_dir.display()
);
anyhow::bail!("Fixtures files are not formatted")
}
/// Invoke `cargo build` to compile the contracts.
fn invoke_build(current_dir: &Path) -> Result<()> {
let encoded_rustflags = [
"-Clink-arg=-zstack-size=65536",
"-Clink-arg=--import-memory",
"-Clinker-plugin-lto",
"-Ctarget-cpu=mvp",
"-Dwarnings",
]
.join("\x1f");
let build_res = Command::new(env::var("CARGO")?)
.current_dir(current_dir)
.env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags)
.args(&["build", "--release", "--target=wasm32-unknown-unknown"])
.output()
.expect("failed to execute process");
if build_res.status.success() {
return Ok(())
}
let stderr = String::from_utf8_lossy(&build_res.stderr);
eprintln!("{}", stderr);
anyhow::bail!("Failed to build contracts");
}
/// Post-process the compiled wasm contracts.
fn post_process_wasm(input_path: &Path, output_path: &Path) -> Result<()> {
let mut module = deserialize_file(input_path)?;
if let Some(section) = module.export_section_mut() {
section.entries_mut().retain(|entry| {
matches!(entry.internal(), Internal::Function(_)) &&
(entry.field() == "call" || entry.field() == "deploy")
});
}
serialize_to_file(output_path, module).map_err(Into::into)
}
/// Write the compiled contracts to the given output directory.
fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec<Entry>) -> Result<()> {
for entry in entries {
let wasm_output = entry.out_wasm_filename();
post_process_wasm(
&build_dir.join("target/wasm32-unknown-unknown/release").join(&wasm_output),
&out_dir.join(&wasm_output),
)?;
fs::write(out_dir.join(&entry.hash), "")?;
}
Ok(())
}
/// Returns the root path of the wasm workspace.
fn find_workspace_root(current_dir: &Path) -> Option<PathBuf> {
let mut current_dir = current_dir.to_path_buf();
while current_dir.parent().is_some() {
if current_dir.join("Cargo.toml").exists() {
let cargo_toml_contents =
std::fs::read_to_string(current_dir.join("Cargo.toml")).ok()?;
if cargo_toml_contents.contains("[workspace]") {
return Some(current_dir);
}
}
current_dir.pop();
}
None
}
fn main() -> Result<()> {
let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into();
let contracts_dir = fixtures_dir.join("contracts");
let out_dir: PathBuf = env::var("OUT_DIR")?.into();
let workspace_root = find_workspace_root(&fixtures_dir).expect("workspace root exists; qed");
let entries = collect_entries(&contracts_dir, &out_dir);
if entries.is_empty() {
return Ok(());
}
let tmp_dir = tempfile::tempdir()?;
let tmp_dir_path = tmp_dir.path();
create_cargo_toml(&fixtures_dir, entries.iter(), tmp_dir.path())?;
invoke_cargo_fmt(
&workspace_root.join(".rustfmt.toml"),
entries.iter().map(|entry| &entry.path as _),
&contracts_dir,
)?;
invoke_build(tmp_dir_path)?;
write_output(tmp_dir_path, &out_dir, entries)?;
Ok(())
}
@@ -0,0 +1,48 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! This calls another contract as passed as its account id.
#![no_std]
#![no_main]
extern crate common;
use uapi::{CallFlags, HostFn, HostFnImpl as api};
#[no_mangle]
pub extern "C" fn deploy() {}
#[no_mangle]
pub extern "C" fn call() {
let mut buffer = [0u8; 40];
let callee_input = 0..4;
let callee_addr = 4..36;
let value = 36..40;
// Read the input data.
api::input(&mut &mut buffer[..]);
// Call the callee
api::call_v1(
CallFlags::empty(),
&buffer[callee_addr],
0u64, // How much gas to devote for the execution. 0 = all.
&buffer[value],
&buffer[callee_input],
None,
)
.unwrap();
}
@@ -0,0 +1,9 @@
[package]
name = "pallet-contracts-fixtures-common"
publish = false
version = "1.0.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
description = "Common utilities for pallet-contracts-fixtures."
@@ -0,0 +1,31 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_std]
#![cfg(any(target_arch = "wasm32", target_arch = "riscv32"))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
#[cfg(target_arch = "wasm32")]
core::arch::wasm32::unreachable();
#[cfg(target_arch = "riscv32")]
// Safety: The unimp instruction is guaranteed to trap
unsafe {
core::arch::asm!("unimp");
core::hint::unreachable_unchecked();
}
}
@@ -0,0 +1,26 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_std]
#![no_main]
extern crate common;
#[no_mangle]
pub extern "C" fn deploy() {}
#[no_mangle]
pub extern "C" fn call() {}
@@ -1,39 +0,0 @@
;; This calls another contract as passed as its account id.
(module
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
(import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32)))
(import "env" "memory" (memory 1 1))
(func $assert (param i32)
(block $ok
(br_if $ok
(local.get 0)
)
(unreachable)
)
)
(func (export "deploy"))
(func (export "call")
;; Store length of input buffer.
(i32.store (i32.const 0) (i32.const 512))
;; Copy input at address 4.
(call $seal_input (i32.const 4) (i32.const 0))
;; Call passed contract.
(call $assert (i32.eqz
(call $seal_call
(i32.const 0) ;; No flags
(i32.const 8) ;; Pointer to "callee" address.
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
(i32.const 512) ;; Pointer to the buffer with value to transfer
(i32.const 4) ;; Pointer to input data buffer address
(i32.const 4) ;; Length of input data buffer
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
(i32.const 0) ;; Length is ignored in this case
)
))
)
)
@@ -1,6 +0,0 @@
;; A valid contract which does nothing at all
(module
(import "env" "memory" (memory 1 1))
(func (export "deploy"))
(func (export "call"))
)
+36 -4
View File
@@ -16,9 +16,9 @@
// limitations under the License.
use sp_runtime::traits::Hash;
use std::{env::var, path::PathBuf};
use std::{env::var, fs, path::PathBuf};
fn fixtures_root_dir() -> PathBuf {
fn wat_root_dir() -> PathBuf {
match (var("CARGO_MANIFEST_DIR"), var("CARGO_PKG_NAME")) {
// When `CARGO_MANIFEST_DIR` is not set, Rust resolves relative paths from the root folder
(Err(_), _) => "substrate/frame/contracts/fixtures/data".into(),
@@ -33,12 +33,44 @@ fn fixtures_root_dir() -> PathBuf {
/// with it's hash.
///
/// The fixture files are located under the `fixtures/` directory.
pub fn compile_module<T>(fixture_name: &str) -> wat::Result<(Vec<u8>, <T::Hashing as Hash>::Output)>
fn legacy_compile_module<T>(
fixture_name: &str,
) -> anyhow::Result<(Vec<u8>, <T::Hashing as Hash>::Output)>
where
T: frame_system::Config,
{
let fixture_path = fixtures_root_dir().join(format!("{fixture_name}.wat"));
let fixture_path = wat_root_dir().join(format!("{fixture_name}.wat"));
let wasm_binary = wat::parse_file(fixture_path)?;
let code_hash = T::Hashing::hash(&wasm_binary);
Ok((wasm_binary, code_hash))
}
/// Load a given wasm module and returns a wasm binary contents along with it's hash.
/// Use the legacy compile_module as fallback, if the rust fixture does not exist yet.
pub fn compile_module<T>(
fixture_name: &str,
) -> anyhow::Result<(Vec<u8>, <T::Hashing as Hash>::Output)>
where
T: frame_system::Config,
{
let out_dir: std::path::PathBuf = env!("OUT_DIR").into();
let fixture_path = out_dir.join(format!("{fixture_name}.wasm"));
match fs::read(fixture_path) {
Ok(wasm_binary) => {
let code_hash = T::Hashing::hash(&wasm_binary);
Ok((wasm_binary, code_hash))
},
Err(_) => legacy_compile_module::<T>(fixture_name),
}
}
#[cfg(test)]
mod test {
#[test]
fn out_dir_should_have_compiled_mocks() {
let out_dir: std::path::PathBuf = env!("OUT_DIR").into();
let dummy_wasm = out_dir.join("dummy.wasm");
println!("dummy_wasm: {:?}", dummy_wasm);
assert!(dummy_wasm.exists());
}
}
@@ -16,7 +16,7 @@ frame-system = { path = "../../system", default-features = false}
pallet-assets = { path = "../../assets" }
pallet-balances = { path = "../../balances" }
pallet-contracts = { path = ".." }
pallet-contracts-primitives = { path = "../primitives", default-features = false}
pallet-contracts-uapi = { path = "../uapi", default-features = false}
pallet-contracts-proc-macro = { path = "../proc-macro" }
pallet-insecure-randomness-collective-flip = { path = "../../insecure-randomness-collective-flip" }
pallet-message-queue = { path = "../../message-queue" }
@@ -52,7 +52,6 @@ std = [
"frame-support/std",
"frame-system/std",
"pallet-balances/std",
"pallet-contracts-primitives/std",
"pallet-contracts-proc-macro/full",
"pallet-contracts/std",
"pallet-insecure-randomness-collective-flip/std",
@@ -29,9 +29,8 @@ use frame_support::{
traits::{fungibles::Mutate, Currency},
};
use pallet_balances::{BalanceLock, Reasons};
use pallet_contracts::{CollectEvents, DebugInfo, Determinism};
use pallet_contracts::{Code, CollectEvents, DebugInfo, Determinism};
use pallet_contracts_fixtures::compile_module;
use pallet_contracts_primitives::Code;
use xcm::{v3::prelude::*, VersionedMultiLocation, VersionedXcm};
use xcm_simulator::TestExt;
@@ -1,33 +0,0 @@
[package]
name = "pallet-contracts-primitives"
version = "24.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage = "https://substrate.io"
repository.workspace = true
description = "A crate that hosts a common definitions that are relevant for the pallet-contracts."
readme = "README.md"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
bitflags = "1.0"
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] }
# Substrate Dependencies (This crate should not rely on frame)
sp-std = { path = "../../../primitives/std", default-features = false}
sp-runtime = { path = "../../../primitives/runtime", default-features = false}
sp-weights = { path = "../../../primitives/weights", default-features = false}
[features]
default = [ "std" ]
std = [
"codec/std",
"scale-info/std",
"sp-runtime/std",
"sp-std/std",
"sp-weights/std",
]
@@ -1,3 +0,0 @@
A crate that hosts a common definitions that are relevant for the pallet-contracts.
License: Apache-2.0
@@ -271,7 +271,7 @@ impl HostFn {
// process return type
let msg = r#"Should return one of the following:
- Result<(), TrapReason>,
- Result<ReturnCode, TrapReason>,
- Result<ReturnErrorCode, TrapReason>,
- Result<u64, TrapReason>,
- Result<u32, TrapReason>"#;
let ret_ty = match item.clone().sig.output {
@@ -336,7 +336,7 @@ impl HostFn {
"()" => Ok(HostFnReturn::Unit),
"u32" => Ok(HostFnReturn::U32),
"u64" => Ok(HostFnReturn::U64),
"ReturnCode" => Ok(HostFnReturn::ReturnCode),
"ReturnErrorCode" => Ok(HostFnReturn::ReturnCode),
_ => Err(err(arg1.span(), &msg)),
}?;
@@ -550,7 +550,7 @@ fn expand_env(def: &EnvDef, docs: bool) -> TokenStream2 {
/// consumed by humans through rustdoc.
#[cfg(doc)]
pub mod api_doc {
use super::{TrapReason, ReturnCode};
use super::{TrapReason, ReturnErrorCode};
#docs
}
}
@@ -767,7 +767,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2)
/// #[define_env]
/// pub mod some_env {
/// #[version(2)]
/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<ReturnCode, TrapReason> {
/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<ReturnErrorCode, TrapReason> {
/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
/// }
///
@@ -793,7 +793,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2)
/// pub mod some_env {
/// #[version(1)]
/// #[prefixed_alias]
/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<ReturnCode, TrapReason> {
/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<ReturnErrorCode, TrapReason> {
/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ())
/// }
///
@@ -811,7 +811,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2)
///
/// Only following return types are allowed for the host functions defined with the macro:
/// - `Result<(), TrapReason>`,
/// - `Result<ReturnCode, TrapReason>`,
/// - `Result<ReturnErrorCode, TrapReason>`,
/// - `Result<u32, TrapReason>`.
///
/// The macro expands to `pub struct Env` declaration, with the following traits implementations:
@@ -33,7 +33,6 @@ use crate::{
migration::{
codegen::LATEST_MIGRATION_VERSION, v09, v10, v11, v12, v13, v14, v15, MigrationStep,
},
wasm::CallFlags,
Pallet as Contracts, *,
};
use codec::{Encode, MaxEncodedLen};
@@ -46,6 +45,7 @@ use frame_support::{
};
use frame_system::RawOrigin;
use pallet_balances;
use pallet_contracts_uapi::CallFlags;
use sp_runtime::traits::{Bounded, Hash};
use sp_std::prelude::*;
use wasm_instrument::parity_wasm::elements::{BlockType, Instruction, ValueType};
@@ -81,7 +81,7 @@ use sp_std::{marker::PhantomData, vec::Vec};
pub use crate::{exec::Ext, gas::ChargedAmount, storage::meter::Diff, Config};
pub use frame_system::Config as SysConfig;
pub use pallet_contracts_primitives::ReturnFlags;
pub use pallet_contracts_uapi::ReturnFlags;
/// Result that returns a [`DispatchError`] on error.
pub type Result<T> = sp_std::result::Result<T, DispatchError>;
+4 -2
View File
@@ -15,9 +15,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
pub use crate::exec::{ExecResult, ExportedFunction};
pub use crate::{
exec::{ExecResult, ExportedFunction},
primitives::ExecReturnValue,
};
use crate::{Config, LOG_TARGET};
pub use pallet_contracts_primitives::ExecReturnValue;
/// Umbrella trait for all interfaces that serves for debugging.
pub trait Debugger<T: Config>: Tracing<T> + CallInterceptor<T> {}
+2 -2
View File
@@ -18,6 +18,7 @@
use crate::{
debug::{CallInterceptor, CallSpan, Tracing},
gas::GasMeter,
primitives::{ExecReturnValue, StorageDeposit},
storage::{self, meter::Diff, WriteOutcome},
BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf,
DebugBufferVec, Determinism, Error, Event, Nonce, Origin, Pallet as Contracts, Schedule,
@@ -37,7 +38,6 @@ use frame_support::{
Blake2_128Concat, BoundedVec, StorageHasher,
};
use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
use pallet_contracts_primitives::{ExecReturnValue, StorageDeposit};
use smallvec::{Array, SmallVec};
use sp_core::{
ecdsa::Public as ECDSAPublic,
@@ -1618,7 +1618,7 @@ mod tests {
use codec::{Decode, Encode};
use frame_support::{assert_err, assert_ok, parameter_types};
use frame_system::{EventRecord, Phase};
use pallet_contracts_primitives::ReturnFlags;
use pallet_contracts_uapi::ReturnFlags;
use pretty_assertions::assert_eq;
use sp_runtime::{traits::Hash, DispatchError};
use std::{cell::RefCell, collections::hash_map::HashMap, rc::Rc};
+3 -5
View File
@@ -91,6 +91,9 @@ mod address;
mod benchmarking;
mod exec;
mod gas;
mod primitives;
pub use primitives::*;
mod schedule;
mod storage;
mod wasm;
@@ -128,11 +131,6 @@ use frame_system::{
pallet_prelude::{BlockNumberFor, OriginFor},
EventRecord, Pallet as System,
};
use pallet_contracts_primitives::{
Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult,
ContractInstantiateResult, ContractResult, ExecReturnValue, GetStorageResult,
InstantiateReturnValue, StorageDeposit,
};
use scale_info::TypeInfo;
use smallvec::Array;
use sp_runtime::{
@@ -17,17 +17,15 @@
//! A crate that hosts a common definitions that are relevant for the pallet-contracts.
#![cfg_attr(not(feature = "std"), no_std)]
use bitflags::bitflags;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::weights::Weight;
use pallet_contracts_uapi::ReturnFlags;
use scale_info::TypeInfo;
use sp_runtime::{
traits::{Saturating, Zero},
DispatchError, RuntimeDebug,
};
use sp_std::prelude::*;
use sp_weights::Weight;
/// Result type of a `bare_call` or `bare_instantiate` call as well as `ContractsApi::call` and
/// `ContractsApi::instantiate`.
@@ -109,15 +107,6 @@ pub enum ContractAccessError {
MigrationInProgress,
}
bitflags! {
/// Flags used by a contract to customize exit behaviour.
#[derive(Encode, Decode, TypeInfo)]
pub struct ReturnFlags: u32 {
/// If this bit is set all changes made by the contract execution are rolled back.
const REVERT = 0x0000_0001;
}
}
/// Output of a contract call or instantiation which ran to completion.
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct ExecReturnValue {
+2 -2
View File
@@ -30,9 +30,10 @@ use crate::{
},
exec::{Frame, Key},
migration::codegen::LATEST_MIGRATION_VERSION,
primitives::CodeUploadReturnValue,
storage::DeletionQueueManager,
tests::test_utils::{get_contract, get_contract_checked},
wasm::{Determinism, ReturnCode as RuntimeReturnCode},
wasm::{Determinism, ReturnErrorCode as RuntimeReturnCode},
weights::WeightInfo,
BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf,
DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason,
@@ -55,7 +56,6 @@ use frame_support::{
};
use frame_system::{EventRecord, Phase};
use pallet_contracts_fixtures::compile_module;
use pallet_contracts_primitives::CodeUploadReturnValue;
use pretty_assertions::{assert_eq, assert_ne};
use sp_core::ByteArray;
use sp_io::hashing::blake2_256;
@@ -18,10 +18,10 @@
use super::*;
use crate::{
debug::{CallInterceptor, CallSpan, ExecResult, ExportedFunction, Tracing},
primitives::ExecReturnValue,
AccountIdOf,
};
use frame_support::traits::Currency;
use pallet_contracts_primitives::ExecReturnValue;
use pretty_assertions::assert_eq;
use std::cell::RefCell;
+12 -11
View File
@@ -24,13 +24,13 @@ mod runtime;
#[cfg(doc)]
pub use crate::wasm::runtime::api_doc;
#[cfg(test)]
pub use tests::MockExt;
pub use crate::wasm::runtime::{
AllowDeprecatedInterface, AllowUnstableInterface, CallFlags, Environment, ReturnCode, Runtime,
AllowDeprecatedInterface, AllowUnstableInterface, Environment, ReturnErrorCode, Runtime,
RuntimeCosts,
};
pub use pallet_contracts_uapi::ReturnFlags;
#[cfg(test)]
pub use tests::MockExt;
use crate::{
exec::{ExecResult, Executable, ExportedFunction, Ext},
@@ -436,6 +436,7 @@ mod tests {
use crate::{
exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Ext, Key, SeedOf},
gas::GasMeter,
primitives::ExecReturnValue,
storage::WriteOutcome,
tests::{RuntimeCall, Test, ALICE, BOB},
BalanceOf, CodeHash, Error, Origin, Pallet as Contracts,
@@ -445,7 +446,7 @@ mod tests {
assert_err, assert_ok, dispatch::DispatchResultWithPostInfo, weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};
use pallet_contracts_uapi::ReturnFlags;
use pretty_assertions::assert_eq;
use sp_core::H256;
use sp_runtime::DispatchError;
@@ -2739,7 +2740,7 @@ mod tests {
let result = execute(CODE, input, &mut ext).unwrap();
assert_eq!(
u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
ReturnCode::KeyNotFound as u32
ReturnErrorCode::KeyNotFound as u32
);
// value exists
@@ -2747,7 +2748,7 @@ mod tests {
let result = execute(CODE, input, &mut ext).unwrap();
assert_eq!(
u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
ReturnCode::Success as u32
ReturnErrorCode::Success as u32
);
assert_eq!(ext.storage.get(&[1u8; 64].to_vec()).unwrap(), &[42u8]);
assert_eq!(&result.data[4..], &[42u8]);
@@ -2757,7 +2758,7 @@ mod tests {
let result = execute(CODE, input, &mut ext).unwrap();
assert_eq!(
u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
ReturnCode::Success as u32
ReturnErrorCode::Success as u32
);
assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), Some(&vec![]));
assert_eq!(&result.data[4..], &([] as [u8; 0]));
@@ -2920,7 +2921,7 @@ mod tests {
let result = execute(CODE, input, &mut ext).unwrap();
assert_eq!(
u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
ReturnCode::KeyNotFound as u32
ReturnErrorCode::KeyNotFound as u32
);
// value did exist -> value returned
@@ -2928,7 +2929,7 @@ mod tests {
let result = execute(CODE, input, &mut ext).unwrap();
assert_eq!(
u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
ReturnCode::Success as u32
ReturnErrorCode::Success as u32
);
assert_eq!(ext.storage.get(&[1u8; 64].to_vec()), None);
assert_eq!(&result.data[4..], &[42u8]);
@@ -2938,7 +2939,7 @@ mod tests {
let result = execute(CODE, input, &mut ext).unwrap();
assert_eq!(
u32::from_le_bytes(result.data[0..4].try_into().unwrap()),
ReturnCode::Success as u32
ReturnErrorCode::Success as u32
);
assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), None);
assert_eq!(&result.data[4..], &[0u8; 0]);
File diff suppressed because it is too large Load Diff
+23
View File
@@ -0,0 +1,23 @@
[package]
name = "pallet-contracts-uapi"
version = "4.0.0-dev"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage = "https://substrate.io"
repository.workspace = true
description = "Exposes all the host functions that a contract can import."
[dependencies]
paste = { version = "1.0", default-features = false }
bitflags = "1.0"
scale-info = { version = "2.10.0", default-features = false, features = ["derive"], optional = true }
scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [
"derive",
"max-encoded-len",
], optional = true }
[features]
default = [ "scale" ]
scale = [ "dep:scale", "scale-info" ]
@@ -0,0 +1,73 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use bitflags::bitflags;
bitflags! {
/// Flags used by a contract to customize exit behaviour.
#[cfg_attr(feature = "scale", derive(scale::Encode, scale::Decode, scale_info::TypeInfo))]
pub struct ReturnFlags: u32 {
/// If this bit is set all changes made by the contract execution are rolled back.
const REVERT = 0x0000_0001;
}
}
bitflags! {
/// Flags used to change the behaviour of `seal_call` and `seal_delegate_call`.
pub struct CallFlags: u32 {
/// Forward the input of current function to the callee.
///
/// Supplied input pointers are ignored when set.
///
/// # Note
///
/// A forwarding call will consume the current contracts input. Any attempt to
/// access the input after this call returns will lead to [`Error::InputForwarded`].
/// It does not matter if this is due to calling `seal_input` or trying another
/// forwarding call. Consider using [`Self::CLONE_INPUT`] in order to preserve
/// the input.
const FORWARD_INPUT = 0b0000_0001;
/// Identical to [`Self::FORWARD_INPUT`] but without consuming the input.
///
/// This adds some additional weight costs to the call.
///
/// # Note
///
/// This implies [`Self::FORWARD_INPUT`] and takes precedence when both are set.
const CLONE_INPUT = 0b0000_0010;
/// Do not return from the call but rather return the result of the callee to the
/// callers caller.
///
/// # Note
///
/// This makes the current contract completely transparent to its caller by replacing
/// this contracts potential output by the callee ones. Any code after `seal_call`
/// can be safely considered unreachable.
const TAIL_CALL = 0b0000_0100;
/// Allow the callee to reenter into the current contract.
///
/// Without this flag any reentrancy into the current contract that originates from
/// the callee (or any of its callees) is denied. This includes the first callee:
/// You cannot call into yourself with this flag set.
///
/// # Note
///
/// For `seal_delegate_call` should be always unset, otherwise
/// [`Error::InvalidCallFlags`] is returned.
const ALLOW_REENTRY = 0b0000_1000;
}
}
+793
View File
@@ -0,0 +1,793 @@
// Copyright (C) Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{CallFlags, Result, ReturnFlags, SENTINEL};
use paste::paste;
#[cfg(target_arch = "wasm32")]
mod wasm32;
#[cfg(target_arch = "riscv32")]
mod riscv32;
macro_rules! hash_fn {
( $name:ident, $bytes:literal ) => {
paste! {
#[doc = "Computes the " $name " " $bytes "-bit hash on the given input buffer."]
#[doc = "\n# Notes\n"]
#[doc = "- The `input` and `output` buffer may overlap."]
#[doc = "- The output buffer is expected to hold at least " $bytes " bits."]
#[doc = "- It is the callers responsibility to provide an output buffer that is large enough to hold the expected amount of bytes returned by the hash function."]
#[doc = "\n# Parameters\n"]
#[doc = "- `input`: The input data buffer."]
#[doc = "- `output`: The output buffer to write the hash result to."]
fn [<hash_ $name>](input: &[u8], output: &mut [u8; $bytes]);
}
};
}
fn extract_from_slice(output: &mut &mut [u8], new_len: usize) {
debug_assert!(new_len <= output.len());
let tmp = core::mem::take(output);
*output = &mut tmp[..new_len];
}
fn ptr_len_or_sentinel(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) {
match data {
Some(ref mut data) => (data.as_mut_ptr(), data.len() as _),
None => (SENTINEL as _, 0),
}
}
fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 {
match data {
Some(ref data) => data.as_ptr(),
None => SENTINEL as _,
}
}
/// Implements [`HostFn`] for each supported target architecture.
pub enum HostFnImpl {}
/// Defines all the host apis implemented by both wasm and RISC-V vms.
pub trait HostFn {
/// Returns the number of times specified contract exists on the call stack. Delegated calls are
/// not counted as separate calls.
///
/// # Parameters
///
/// - `account`: The contract address. Should be decodable as an `T::AccountId`. Traps
/// otherwise.
///
/// # Return
///
/// Returns the number of times specified contract exists on the call stack.
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn account_reentrance_count(account: &[u8]) -> u32;
/// Stores the address of the current contract into the supplied buffer.
///
/// If the available space in `output` is less than the size of the value a trap is triggered.
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the address.
fn address(output: &mut &mut [u8]);
/// Adds a new delegate dependency to the contract.
///
/// Traps if the maximum number of delegate_dependencies is reached or if
/// the delegate dependency already exists.
///
/// # Parameters
///
/// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps
/// otherwise.
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn add_delegate_dependency(code_hash: &[u8]);
/// Stores the *free* balance of the current account into the supplied buffer.
///
/// If the available space in `output` is less than the size of the value a trap is triggered.
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the balance.
fn balance(output: &mut &mut [u8]);
/// Stores the current block number of the current contract into the supplied buffer.
///
/// If the available space in `output` is less than the size of the value a trap is triggered.
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the block number.
fn block_number(output: &mut &mut [u8]);
/// Make a call to another contract.
///
/// This is equivalent to calling the newer version of this function with
/// `flags` set to [`CallFlags::ALLOW_REENTRY`]. See the newer version for documentation.
#[deprecated(note = "Deprecated, use newer version instead")]
fn call(
callee: &[u8],
gas: u64,
value: &[u8],
input_data: &[u8],
output: Option<&mut [u8]>,
) -> Result;
/// Make a call to another contract.
///
/// Equivalent to the newer [`Self::call_v2`] version but works with
/// *ref_time* Weight only
fn call_v1(
flags: CallFlags,
callee: &[u8],
gas: u64,
value: &[u8],
input_data: &[u8],
output: Option<&mut [u8]>,
) -> Result;
/// Call (possibly transferring some amount of funds) into the specified account.
///
/// # Parameters
///
/// - `flags`: See [`CallFlags`] for a documentation of the supported flags.
/// - `callee`: The address of the callee. Should be decodable as an `T::AccountId`. Traps
/// otherwise.
/// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution.
/// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution.
/// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a
/// `Option<T::Balance>`. Traps otherwise. Passing `None` means setting no specific limit for
/// the call, which implies storage usage up to the limit of the parent call.
/// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`.
/// Traps otherwise.
/// - `input`: The input data buffer used to call the contract.
/// - `output`: A reference to the output data buffer to write the call output buffer. If `None`
/// is provided then the output buffer is not copied.
///
/// # Errors
///
/// An error means that the call wasn't successful output buffer is returned unless
/// stated otherwise.
///
/// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned.
/// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped]
/// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed]
/// - [NotCallable][`crate::ReturnErrorCode::NotCallable]
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn call_v2(
flags: CallFlags,
callee: &[u8],
ref_time_limit: u64,
proof_time_limit: u64,
deposit: Option<&[u8]>,
value: &[u8],
input_data: &[u8],
output: Option<&mut [u8]>,
) -> Result;
/// Call into the chain extension provided by the chain if any.
///
/// Handling of the input values is up to the specific chain extension and so is the
/// return value. The extension can decide to use the inputs as primitive inputs or as
/// in/out arguments by interpreting them as pointers. Any caller of this function
/// must therefore coordinate with the chain that it targets.
///
/// # Note
///
/// If no chain extension exists the contract will trap with the `NoChainExtension`
/// module error.
///
/// # Parameters
///
/// - `func_id`: The function id of the chain extension.
/// - `input`: The input data buffer.
/// - `output`: A reference to the output data buffer to write the output data.
///
/// # Return
///
/// The chain extension returned value, if executed successfully.
fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32;
/// Call some dispatchable of the runtime.
///
/// # Parameters
///
/// - `call`: The call data.
///
/// # Return
///
/// Returns `Error::Success` when the dispatchable was successfully executed and
/// returned `Ok`. When the dispatchable was executed but returned an error
/// `Error::CallRuntimeFailed` is returned. The full error is not
/// provided because it is not guaranteed to be stable.
///
/// # Comparison with `ChainExtension`
///
/// Just as a chain extension this API allows the runtime to extend the functionality
/// of contracts. While making use of this function is generally easier it cannot be
/// used in all cases. Consider writing a chain extension if you need to do perform
/// one of the following tasks:
///
/// - Return data.
/// - Provide functionality **exclusively** to contracts.
/// - Provide custom weights.
/// - Avoid the need to keep the `Call` data structure stable.
fn call_runtime(call: &[u8]) -> Result;
/// Stores the address of the caller into the supplied buffer.
///
/// If the available space in `output` is less than the size of the value a trap is triggered.
///
/// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the
/// extrinsic will be returned. Otherwise, if this call is initiated by another contract then
/// the address of the contract will be returned.
///
/// If there is no address associated with the caller (e.g. because the caller is root) then
/// it traps with `BadOrigin`.
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the caller address.
fn caller(output: &mut &mut [u8]);
/// Checks whether the caller of the current contract is the origin of the whole call stack.
///
/// Prefer this over [`is_contract()`][`Self::is_contract`] when checking whether your contract
/// is being called by a contract or a plain account. The reason is that it performs better
/// since it does not need to do any storage lookups.
///
/// # Return
///
/// A return value of `true` indicates that this contract is being called by a plain account
/// and `false` indicates that the caller is another contract.
fn caller_is_origin() -> bool;
/// Checks whether the caller of the current contract is root.
///
/// Note that only the origin of the call stack can be root. Hence this function returning
/// `true` implies that the contract is being called by the origin.
///
/// A return value of `true` indicates that this contract is being called by a root origin,
/// and `false` indicates that the caller is a signed origin.
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn caller_is_root() -> u32;
/// Clear the value at the given key in the contract storage.
///
/// Equivalent to the newer [`Self::clear_storage_v1`] version with
/// the exception of the return type. Still a valid thing to call when not interested in the
/// return value.
fn clear_storage(key: &[u8]);
/// Clear the value at the given key in the contract storage.
///
/// # Parameters
///
/// - `key`: The storage key.
///
/// # Return
///
/// Returns the size of the pre-existing value at the specified key if any.
fn clear_storage_v1(key: &[u8]) -> Option<u32>;
/// Retrieve the code hash for a specified contract address.
///
/// # Parameters
///
/// - `account_id`: The address of the contract.Should be decodable as an `T::AccountId`. Traps
/// otherwise.
/// - `output`: A reference to the output data buffer to write the code hash.
///
///
/// # Errors
///
/// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound]
fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result;
/// Checks whether there is a value stored under the given key.
///
/// This version is to be used with a fixed sized storage key. For runtimes supporting
/// transparent hashing, please use the newer version of this function.
fn contains_storage(key: &[u8]) -> Option<u32>;
/// Checks whether there is a value stored under the given key.
///
/// The key length must not exceed the maximum defined by the contracts module parameter.
///
/// # Parameters
/// - `key`: The storage key.
///
/// # Return
///
/// Returns the size of the pre-existing value at the specified key if any.
fn contains_storage_v1(key: &[u8]) -> Option<u32>;
/// Execute code in the context (storage, caller, value) of the current contract.
///
/// Reentrancy protection is always disabled since the callee is allowed
/// to modify the callers storage. This makes going through a reentrancy attack
/// unnecessary for the callee when it wants to exploit the caller.
///
/// # Parameters
///
/// - `flags`: See [`CallFlags`] for a documentation of the supported flags.
/// - `code_hash`: The hash of the code to be executed.
/// - `input`: The input data buffer used to call the contract.
/// - `output`: A reference to the output data buffer to write the call output buffer. If `None`
/// is provided then the output buffer is not copied.
///
/// # Errors
///
/// An error means that the call wasn't successful and no output buffer is returned unless
/// stated otherwise.
///
/// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned.
/// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped]
/// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound]
fn delegate_call(
flags: CallFlags,
code_hash: &[u8],
input_data: &[u8],
output: Option<&mut [u8]>,
) -> Result;
/// Deposit a contract event with the data buffer and optional list of topics. There is a limit
/// on the maximum number of topics specified by `event_topics`.
///
/// There should not be any duplicates in `topics`.
///
/// # Parameters
///
/// - `topics`: The topics list encoded as `Vec<T::Hash>`. It can't contain duplicates.
fn deposit_event(topics: &[u8], data: &[u8]);
/// Recovers the ECDSA public key from the given message hash and signature.
///
/// Writes the public key into the given output buffer.
/// Assumes the secp256k1 curve.
///
/// # Parameters
///
/// - `signature`: The signature bytes.
/// - `message_hash`: The message hash bytes.
/// - `output`: A reference to the output data buffer to write the public key.
///
/// # Errors
///
/// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed]
fn ecdsa_recover(
signature: &[u8; 65],
message_hash: &[u8; 32],
output: &mut [u8; 33],
) -> Result;
/// Calculates Ethereum address from the ECDSA compressed public key and stores
/// it into the supplied buffer.
///
/// # Parameters
///
/// - `pubkey`: The public key bytes.
/// - `output`: A reference to the output data buffer to write the address.
///
/// # Errors
///
/// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed]
fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result;
/// Stores the weight left into the supplied buffer.
///
/// Equivalent to the newer [`Self::gas_left_v1`] version but
/// works with *ref_time* Weight only.
fn gas_left(out: &mut &mut [u8]);
/// Stores the amount of weight left into the supplied buffer.
/// The data is encoded as Weight.
///
/// If the available space in `output` is less than the size of the value a trap is triggered.
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the weight left.
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn gas_left_v1(output: &mut &mut [u8]);
/// Retrieve the value under the given key from storage.
///
/// This version is to be used with a fixed sized storage key. For runtimes supporting
/// transparent hashing, please use the newer version of this function.
fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result;
/// Retrieve the value under the given key from storage.
///
/// The key length must not exceed the maximum defined by the contracts module parameter.
///
/// # Parameters
/// - `key`: The storage key.
/// - `output`: A reference to the output data buffer to write the storage entry.
///
/// # Errors
///
/// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound]
fn get_storage_v1(key: &[u8], output: &mut &mut [u8]) -> Result;
hash_fn!(sha2_256, 32);
hash_fn!(keccak_256, 32);
hash_fn!(blake2_256, 32);
hash_fn!(blake2_128, 16);
/// Stores the input passed by the caller into the supplied buffer.
///
/// # Note
///
/// This function traps if:
/// - the input is larger than the available space.
/// - the input was previously forwarded by a [`call()`][`Self::call()`].
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the input data.
fn input(output: &mut &mut [u8]);
/// Instantiate a contract with the specified code hash.
///
/// Equivalent to the newer [`Self::instantiate_v2`] version but works
/// with *ref_time* Weight only.
fn instantiate_v1(
code_hash: &[u8],
gas: u64,
value: &[u8],
input: &[u8],
address: Option<&mut [u8]>,
output: Option<&mut [u8]>,
salt: &[u8],
) -> Result;
/// Instantiate a contract with the specified code hash.
///
/// This function creates an account and executes the constructor defined in the code specified
/// by the code hash.
///
/// # Parameters
///
/// - `code_hash`: The hash of the code to be instantiated.
/// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution.
/// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution.
/// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a
/// `Option<T::Balance>`. Traps otherwise. Passing `None` means setting no specific limit for
/// the call, which implies storage usage up to the limit of the parent call.
/// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`.
/// Traps otherwise.
/// - `input`: The input data buffer.
/// - `address`: A reference to the address buffer to write the address of the contract. If
/// `None` is provided then the output buffer is not copied.
/// - `output`: A reference to the return value buffer to write the constructor output buffer.
/// If `None` is provided then the output buffer is not copied.
/// - `salt`: The salt bytes to use for this instantiation.
///
/// # Errors
///
/// Please consult the [ReturnErrorCode][`crate::ReturnErrorCode`] enum declaration for more
/// information on those errors. Here we only note things specific to this function.
///
/// An error means that the account wasn't created and no address or output buffer
/// is returned unless stated otherwise.
///
/// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned.
/// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped]
/// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed]
/// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound]
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn instantiate_v2(
code_hash: &[u8],
ref_time_limit: u64,
proof_size_limit: u64,
deposit: Option<&[u8]>,
value: &[u8],
input: &[u8],
address: Option<&mut [u8]>,
output: Option<&mut [u8]>,
salt: &[u8],
) -> Result;
/// Returns a nonce that is unique per contract instantiation.
///
/// The nonce is incremented for each successful contract instantiation. This is a
/// sensible default salt for contract instantiations.
fn instantiation_nonce() -> u64;
/// Checks whether a specified address belongs to a contract.
///
/// # Parameters
///
/// - `account_id`: The address to check. Should be decodable as an `T::AccountId`. Traps
/// otherwise.
///
/// # Return
///
/// Returns `true` if the address belongs to a contract.
fn is_contract(account_id: &[u8]) -> bool;
/// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
/// The data is encoded as `T::Balance`.
///
/// If the available space in `output` is less than the size of the value a trap is triggered.
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the minimum balance.
fn minimum_balance(output: &mut &mut [u8]);
/// Retrieve the code hash of the currently executing contract.
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the code hash.
fn own_code_hash(output: &mut [u8]);
/// Load the latest block timestamp into the supplied buffer
///
/// If the available space in `output` is less than the size of the value a trap is triggered.
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the timestamp.
fn now(output: &mut &mut [u8]);
/// Returns the number of times the currently executing contract exists on the call stack in
/// addition to the calling instance.
///
/// # Return
///
/// Returns `0` when there is no reentrancy.
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn reentrance_count() -> u32;
/// Removes the delegate dependency from the contract.
///
/// Traps if the delegate dependency does not exist.
///
/// # Parameters
///
/// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps
/// otherwise.
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn remove_delegate_dependency(code_hash: &[u8]);
/// Cease contract execution and save a data buffer as a result of the execution.
///
/// This function never returns as it stops execution of the caller.
/// This is the only way to return a data buffer to the caller. Returning from
/// execution without calling this function is equivalent to calling:
/// ```nocompile
/// return_value(ReturnFlags::empty(), &[])
/// ```
///
/// Using an unnamed non empty `ReturnFlags` triggers a trap.
///
/// # Parameters
///
/// - `flags`: Flag used to signal special return conditions to the supervisor. See
/// [`ReturnFlags`] for a documentation of the supported flags.
/// - `return_value`: The return value buffer.
fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !;
/// Replace the contract code at the specified address with new code.
///
/// # Note
///
/// There are a couple of important considerations which must be taken into account when
/// using this API:
///
/// 1. The storage at the code address will remain untouched. This means that contract
/// developers must ensure that the storage layout of the new code is compatible with that of
/// the old code.
///
/// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another
/// way, when using this API you lose the guarantee that an address always identifies a specific
/// code hash.
///
/// 3. If a contract calls into itself after changing its code the new call would use
/// the new code. However, if the original caller panics after returning from the sub call it
/// would revert the changes made by [`set_code_hash()`][`Self::set_code_hash`] and the next
/// caller would use the old code.
///
/// # Parameters
///
/// - `code_hash`: The hash of the new code. Should be decodable as an `T::Hash`. Traps
/// otherwise.
///
/// # Errors
///
/// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound]
fn set_code_hash(code_hash: &[u8]) -> Result;
/// Set the value at the given key in the contract storage.
///
/// Equivalent to [`Self::set_storage_v1`] version with the
/// exception of the return type. Still a valid thing to call for fixed sized storage key, when
/// not interested in the return value.
fn set_storage(key: &[u8], value: &[u8]);
/// Set the value at the given key in the contract storage.
///
/// This version is to be used with a fixed sized storage key. For runtimes supporting
/// transparent hashing, please use the newer version of this function.
fn set_storage_v1(key: &[u8], value: &[u8]) -> Option<u32>;
/// Set the value at the given key in the contract storage.
///
/// The key and value lengths must not exceed the maximums defined by the contracts module
/// parameters.
///
/// # Parameters
///
/// - `key`: The storage key.
/// - `encoded_value`: The storage value.
///
/// # Return
///
/// Returns the size of the pre-existing value at the specified key if any.
fn set_storage_v2(key: &[u8], value: &[u8]) -> Option<u32>;
/// Verify a sr25519 signature
///
/// # Parameters
///
/// - `signature`: The signature bytes.
/// - `message`: The message bytes.
///
/// # Errors
///
/// - [Sr25519VerifyFailed][`crate::ReturnErrorCode::Sr25519VerifyFailed]
fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result;
/// Retrieve and remove the value under the given key from storage.
///
/// # Parameters
/// - `key`: The storage key.
/// - `output`: A reference to the output data buffer to write the storage entry.
///
/// # Errors
///
/// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound]
fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result;
/// Transfer some amount of funds into the specified account.
///
/// # Parameters
///
/// - `account_id`: The address of the account to transfer funds to. Should be decodable as an
/// `T::AccountId`. Traps otherwise.
/// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps otherwise.
///
/// # Errors
///
/// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed]
fn transfer(account_id: &[u8], value: &[u8]) -> Result;
/// Remove the calling account and transfer remaining balance.
///
/// This is equivalent to calling the newer version of this function
#[deprecated(note = "Deprecated, use newer version instead")]
fn terminate(beneficiary: &[u8]) -> !;
/// 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
/// which is considered fatal and results in a trap + rollback.
///
/// # Parameters
///
/// - `beneficiary`: The address of the beneficiary account, Should be decodable as an
/// `T::AccountId`.
///
/// # Traps
///
/// - The contract is live i.e is already on the call stack.
/// - Failed to send the balance to the beneficiary.
/// - The deletion queue is full.
fn terminate_v1(beneficiary: &[u8]) -> !;
/// Stores the value transferred along with this call/instantiate into the supplied buffer.
/// The data is encoded as `T::Balance`.
///
/// If the available space in `output` is less than the size of the value a trap is triggered.
///
/// # Parameters
///
/// - `output`: A reference to the output data buffer to write the transferred value.
fn value_transferred(output: &mut &mut [u8]);
/// Stores the price for the specified amount of gas into the supplied buffer.
///
/// Equivalent to the newer [`Self::weight_to_fee_v1`] version but
/// works with *ref_time* Weight only. It is recommended to switch to the latest version, once
/// it's stabilized.
fn weight_to_fee(gas: u64, output: &mut &mut [u8]);
/// Stores the price for the specified amount of gas into the supplied buffer.
/// The data is encoded as `T::Balance`.
///
/// If the available space in `output` is less than the size of the value a trap is triggered.
///
/// # Parameters
///
/// - `ref_time_limit`: The *ref_time* Weight limit to query the price for.
/// - `proof_size_limit`: The *proof_size* Weight limit to query the price for.
/// - `output`: A reference to the output data buffer to write the price.
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn weight_to_fee_v1(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]);
/// Execute an XCM program locally, using the contract's address as the origin.
/// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that
/// the function is called directly instead of being dispatched.
///
/// # Parameters
///
/// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html),
/// traps otherwise.
/// - `output`: A reference to the output data buffer to write the [Outcome](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v3/enum.Outcome.html)
///
/// # Return
///
/// Returns `Error::Success` when the XCM execution attempt is successful. When the XCM
/// execution fails, `ReturnCode::XcmExecutionFailed` is returned
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn xcm_execute(msg: &[u8], output: &mut &mut [u8]) -> Result;
/// Send an XCM program from the contract to the specified destination.
/// This is equivalent to dispatching `pallet_xcm::send` through `call_runtime`, except that
/// the function is called directly instead of being dispatched.
///
/// # Parameters
///
/// - `dest`: The XCM destination, should be decodable as [VersionedMultiLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedMultiLocation.html),
/// traps otherwise.
/// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html),
/// traps otherwise.
/// - `output`: A reference to the output data buffer to write the [XcmHash](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v3/type.XcmHash.html)
///
/// # Return
///
/// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM
/// execution fails, `ReturnErrorCode::XcmSendFailed` is returned.
#[deprecated(
note = "Unstable function. Behaviour can change without further notice. Use only for testing."
)]
fn xcm_send(dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result;
}
@@ -0,0 +1,14 @@
// Copyright (C) Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// TODO: bring up to date with wasm32.rs
@@ -0,0 +1,811 @@
// Copyright (C) Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{
extract_from_slice, ptr_len_or_sentinel, ptr_or_sentinel, CallFlags, HostFn, HostFnImpl, Result,
};
use crate::{ReturnCode, ReturnFlags};
mod sys {
use super::ReturnCode;
#[link(wasm_import_module = "seal0")]
extern "C" {
pub fn account_reentrance_count(account_ptr: *const u8) -> u32;
pub fn add_delegate_dependency(code_hash_ptr: *const u8);
pub fn address(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn balance(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn block_number(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn call(
callee_ptr: *const u8,
callee_len: u32,
gas: u64,
value_ptr: *const u8,
value_len: u32,
input_data_ptr: *const u8,
input_data_len: u32,
output_ptr: *mut u8,
output_len_ptr: *mut u32,
) -> ReturnCode;
pub fn call_chain_extension(
func_id: u32,
input_ptr: *const u8,
input_len: u32,
output_ptr: *mut u8,
output_len_ptr: *mut u32,
) -> ReturnCode;
pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode;
pub fn caller(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn caller_is_origin() -> ReturnCode;
pub fn caller_is_root() -> ReturnCode;
pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode;
pub fn code_hash(
account_id_ptr: *const u8,
output_ptr: *mut u8,
output_len_ptr: *mut u32,
) -> ReturnCode;
pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode;
pub fn delegate_call(
flags: u32,
code_hash_ptr: *const u8,
input_data_ptr: *const u8,
input_data_len: u32,
output_ptr: *mut u8,
output_len_ptr: *mut u32,
) -> ReturnCode;
pub fn deposit_event(
topics_ptr: *const u8,
topics_len: u32,
data_ptr: *const u8,
data_len: u32,
);
pub fn ecdsa_recover(
signature_ptr: *const u8,
message_hash_ptr: *const u8,
output_ptr: *mut u8,
) -> ReturnCode;
pub fn ecdsa_to_eth_address(public_key_ptr: *const u8, output_ptr: *mut u8) -> ReturnCode;
pub fn gas_left(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn get_storage(
key_ptr: *const u8,
key_len: u32,
out_ptr: *mut u8,
out_len_ptr: *mut u32,
) -> ReturnCode;
pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8);
pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8);
pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8);
pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8);
pub fn input(buf_ptr: *mut u8, buf_len_ptr: *mut u32);
pub fn instantiation_nonce() -> u64;
pub fn is_contract(account_id_ptr: *const u8) -> ReturnCode;
pub fn minimum_balance(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn now(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn own_code_hash(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn reentrance_count() -> u32;
pub fn remove_delegate_dependency(code_hash_ptr: *const u8);
pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32) -> !;
pub fn set_code_hash(code_hash_ptr: *const u8) -> ReturnCode;
pub fn set_storage(
key_ptr: *const u8,
key_len: u32,
value_ptr: *const u8,
value_len: u32,
) -> ReturnCode;
pub fn sr25519_verify(
signature_ptr: *const u8,
public_key_ptr: *const u8,
message_len: u32,
message_ptr: *const u8,
) -> ReturnCode;
pub fn take_storage(
key_ptr: *const u8,
key_len: u32,
out_ptr: *mut u8,
out_len_ptr: *mut u32,
) -> ReturnCode;
pub fn terminate(beneficiary_ptr: *const u8) -> !;
pub fn transfer(
account_id_ptr: *const u8,
account_id_len: u32,
transferred_value_ptr: *const u8,
transferred_value_len: u32,
) -> ReturnCode;
pub fn value_transferred(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn weight_to_fee(gas: u64, output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn xcm_execute(msg_ptr: *const u8, msg_len: u32, output_ptr: *mut u8) -> ReturnCode;
pub fn xcm_send(
dest_ptr: *const u8,
msg_ptr: *const u8,
msg_len: u32,
output_ptr: *mut u8,
) -> ReturnCode;
}
pub mod v1 {
use crate::ReturnCode;
#[link(wasm_import_module = "seal1")]
extern "C" {
pub fn call(
flags: u32,
callee_ptr: *const u8,
gas: u64,
transferred_value_ptr: *const u8,
input_data_ptr: *const u8,
input_data_len: u32,
output_ptr: *mut u8,
output_len_ptr: *mut u32,
) -> ReturnCode;
pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode;
pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode;
pub fn gas_left(output_ptr: *mut u8, output_len_ptr: *mut u32);
pub fn get_storage(
key_ptr: *const u8,
key_len: u32,
out_ptr: *mut u8,
out_len_ptr: *mut u32,
) -> ReturnCode;
pub fn instantiate(
code_hash_ptr: *const u8,
gas: u64,
value_ptr: *const u8,
input_ptr: *const u8,
input_len: u32,
address_ptr: *mut u8,
address_len_ptr: *mut u32,
output_ptr: *mut u8,
output_len_ptr: *mut u32,
salt_ptr: *const u8,
salt_len: u32,
) -> ReturnCode;
pub fn set_storage(
key_ptr: *const u8,
key_len: u32,
value_ptr: *const u8,
value_len: u32,
) -> ReturnCode;
pub fn terminate(beneficiary_ptr: *const u8) -> !;
pub fn weight_to_fee(
ref_time_limit: u64,
proof_time_limit: u64,
output_ptr: *mut u8,
output_len_ptr: *mut u32,
);
}
}
pub mod v2 {
use crate::ReturnCode;
#[link(wasm_import_module = "seal2")]
extern "C" {
pub fn call(
flags: u32,
callee_ptr: *const u8,
ref_time_limit: u64,
proof_time_limit: u64,
deposit_ptr: *const u8,
transferred_value_ptr: *const u8,
input_data_ptr: *const u8,
input_data_len: u32,
output_ptr: *mut u8,
output_len_ptr: *mut u32,
) -> ReturnCode;
pub fn instantiate(
code_hash_ptr: *const u8,
ref_time_limit: u64,
proof_time_limit: u64,
deposit_ptr: *const u8,
value_ptr: *const u8,
input_ptr: *const u8,
input_len: u32,
address_ptr: *mut u8,
address_len_ptr: *mut u32,
output_ptr: *mut u8,
output_len_ptr: *mut u32,
salt_ptr: *const u8,
salt_len: u32,
) -> ReturnCode;
pub fn set_storage(
key_ptr: *const u8,
key_len: u32,
value_ptr: *const u8,
value_len: u32,
) -> ReturnCode;
}
}
}
/// A macro to implement all Host functions with a signature of `fn(&mut &mut [u8])`.
macro_rules! impl_wrapper_for {
(@impl_fn $( $mod:ident )::*, $suffix:literal, $name:ident) => {
paste::paste! {
fn [<$name $suffix>](output: &mut &mut [u8]) {
let mut output_len = output.len() as u32;
unsafe {
$( $mod )::*::$name(output.as_mut_ptr(), &mut output_len);
}
}
}
};
() => {};
(($mod:ident, $suffix:literal) => [$( $name:ident),*], $($tail:tt)*) => {
$(impl_wrapper_for!(@impl_fn sys::$mod, $suffix, $name);)*
impl_wrapper_for!($($tail)*);
};
(() => [$( $name:ident),*], $($tail:tt)*) => {
$(impl_wrapper_for!(@impl_fn sys, "", $name);)*
impl_wrapper_for!($($tail)*);
};
}
/// A macro to implement all the hash functions Apis.
macro_rules! impl_hash_fn {
( $name:ident, $bytes_result:literal ) => {
paste::item! {
fn [<hash_ $name>](input: &[u8], output: &mut [u8; $bytes_result]) {
unsafe {
sys::[<hash_ $name>](
input.as_ptr(),
input.len() as u32,
output.as_mut_ptr(),
)
}
}
}
};
}
/// A macro to implement the get_storage functions.
macro_rules! impl_get_storage {
($fn_name:ident, $sys_get_storage:path) => {
fn $fn_name(key: &[u8], output: &mut &mut [u8]) -> Result {
let mut output_len = output.len() as u32;
let ret_code = {
unsafe {
$sys_get_storage(
key.as_ptr(),
key.len() as u32,
output.as_mut_ptr(),
&mut output_len,
)
}
};
extract_from_slice(output, output_len as usize);
ret_code.into()
}
};
}
impl HostFn for HostFnImpl {
fn instantiate_v1(
code_hash: &[u8],
gas: u64,
value: &[u8],
input: &[u8],
mut address: Option<&mut [u8]>,
mut output: Option<&mut [u8]>,
salt: &[u8],
) -> Result {
let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address);
let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
let ret_code = unsafe {
sys::v1::instantiate(
code_hash.as_ptr(),
gas,
value.as_ptr(),
input.as_ptr(),
input.len() as u32,
address_ptr,
&mut address_len,
output_ptr,
&mut output_len,
salt.as_ptr(),
salt.len() as u32,
)
};
if let Some(ref mut address) = address {
extract_from_slice(address, address_len as usize);
}
if let Some(ref mut output) = output {
extract_from_slice(output, output_len as usize);
}
ret_code.into()
}
fn instantiate_v2(
code_hash: &[u8],
ref_time_limit: u64,
proof_size_limit: u64,
deposit: Option<&[u8]>,
value: &[u8],
input: &[u8],
mut address: Option<&mut [u8]>,
mut output: Option<&mut [u8]>,
salt: &[u8],
) -> Result {
let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address);
let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
let deposit_ptr = ptr_or_sentinel(&deposit);
let ret_code = {
unsafe {
sys::v2::instantiate(
code_hash.as_ptr(),
ref_time_limit,
proof_size_limit,
deposit_ptr,
value.as_ptr(),
input.as_ptr(),
input.len() as u32,
address_ptr,
&mut address_len,
output_ptr,
&mut output_len,
salt.as_ptr(),
salt.len() as u32,
)
}
};
if let Some(ref mut address) = address {
extract_from_slice(address, address_len as usize);
}
if let Some(ref mut output) = output {
extract_from_slice(output, output_len as usize);
}
ret_code.into()
}
fn call(
callee: &[u8],
gas: u64,
value: &[u8],
input_data: &[u8],
mut output: Option<&mut [u8]>,
) -> Result {
let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
let ret_code = {
unsafe {
sys::call(
callee.as_ptr(),
callee.len() as u32,
gas,
value.as_ptr(),
value.len() as u32,
input_data.as_ptr(),
input_data.len() as u32,
output_ptr,
&mut output_len,
)
}
};
if let Some(ref mut output) = output {
extract_from_slice(output, output_len as usize);
}
ret_code.into()
}
fn call_v1(
flags: CallFlags,
callee: &[u8],
gas: u64,
value: &[u8],
input_data: &[u8],
mut output: Option<&mut [u8]>,
) -> Result {
let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
let ret_code = {
unsafe {
sys::v1::call(
flags.bits(),
callee.as_ptr(),
gas,
value.as_ptr(),
input_data.as_ptr(),
input_data.len() as u32,
output_ptr,
&mut output_len,
)
}
};
if let Some(ref mut output) = output {
extract_from_slice(output, output_len as usize);
}
ret_code.into()
}
fn call_v2(
flags: CallFlags,
callee: &[u8],
ref_time_limit: u64,
proof_time_limit: u64,
deposit: Option<&[u8]>,
value: &[u8],
input_data: &[u8],
mut output: Option<&mut [u8]>,
) -> Result {
let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
let deposit_ptr = ptr_or_sentinel(&deposit);
let ret_code = {
unsafe {
sys::v2::call(
flags.bits(),
callee.as_ptr(),
ref_time_limit,
proof_time_limit,
deposit_ptr,
value.as_ptr(),
input_data.as_ptr(),
input_data.len() as u32,
output_ptr,
&mut output_len,
)
}
};
if let Some(ref mut output) = output {
extract_from_slice(output, output_len as usize);
}
ret_code.into()
}
fn caller_is_root() -> u32 {
unsafe { sys::caller_is_root() }.into_u32()
}
fn delegate_call(
flags: CallFlags,
code_hash: &[u8],
input: &[u8],
mut output: Option<&mut [u8]>,
) -> Result {
let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
let ret_code = {
unsafe {
sys::delegate_call(
flags.bits(),
code_hash.as_ptr(),
input.as_ptr(),
input.len() as u32,
output_ptr,
&mut output_len,
)
}
};
if let Some(ref mut output) = output {
extract_from_slice(output, output_len as usize);
}
ret_code.into()
}
fn transfer(account_id: &[u8], value: &[u8]) -> Result {
let ret_code = unsafe {
sys::transfer(
account_id.as_ptr(),
account_id.len() as u32,
value.as_ptr(),
value.len() as u32,
)
};
ret_code.into()
}
fn deposit_event(topics: &[u8], data: &[u8]) {
unsafe {
sys::deposit_event(
topics.as_ptr(),
topics.len() as u32,
data.as_ptr(),
data.len() as u32,
)
}
}
fn set_storage(key: &[u8], value: &[u8]) {
unsafe {
sys::set_storage(key.as_ptr(), key.len() as u32, value.as_ptr(), value.len() as u32)
};
}
fn set_storage_v1(key: &[u8], encoded_value: &[u8]) -> Option<u32> {
let ret_code = unsafe {
sys::v1::set_storage(
key.as_ptr(),
key.len() as u32,
encoded_value.as_ptr(),
encoded_value.len() as u32,
)
};
ret_code.into()
}
fn set_storage_v2(key: &[u8], encoded_value: &[u8]) -> Option<u32> {
let ret_code = unsafe {
sys::v2::set_storage(
key.as_ptr(),
key.len() as u32,
encoded_value.as_ptr(),
encoded_value.len() as u32,
)
};
ret_code.into()
}
fn clear_storage(key: &[u8]) {
unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) };
}
fn clear_storage_v1(key: &[u8]) -> Option<u32> {
let ret_code = unsafe { sys::v1::clear_storage(key.as_ptr(), key.len() as u32) };
ret_code.into()
}
impl_get_storage!(get_storage, sys::get_storage);
impl_get_storage!(get_storage_v1, sys::v1::get_storage);
fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result {
let mut output_len = output.len() as u32;
let ret_code = {
unsafe {
sys::take_storage(
key.as_ptr(),
key.len() as u32,
output.as_mut_ptr(),
&mut output_len,
)
}
};
extract_from_slice(output, output_len as usize);
ret_code.into()
}
fn contains_storage(key: &[u8]) -> Option<u32> {
let ret_code = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) };
ret_code.into()
}
fn contains_storage_v1(key: &[u8]) -> Option<u32> {
let ret_code = unsafe { sys::v1::contains_storage(key.as_ptr(), key.len() as u32) };
ret_code.into()
}
fn terminate(beneficiary: &[u8]) -> ! {
unsafe { sys::terminate(beneficiary.as_ptr()) }
}
fn terminate_v1(beneficiary: &[u8]) -> ! {
unsafe { sys::v1::terminate(beneficiary.as_ptr()) }
}
fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32 {
let mut output_len = output.len() as u32;
let ret_code = {
unsafe {
sys::call_chain_extension(
func_id,
input.as_ptr(),
input.len() as u32,
output.as_mut_ptr(),
&mut output_len,
)
}
};
extract_from_slice(output, output_len as usize);
ret_code.into_u32()
}
fn input(output: &mut &mut [u8]) {
let mut output_len = output.len() as u32;
{
unsafe { sys::input(output.as_mut_ptr(), &mut output_len) };
}
extract_from_slice(output, output_len as usize);
}
fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! {
unsafe { sys::seal_return(flags.bits(), return_value.as_ptr(), return_value.len() as u32) }
}
fn call_runtime(call: &[u8]) -> Result {
let ret_code = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) };
ret_code.into()
}
impl_wrapper_for! {
() => [caller, block_number, address, balance, gas_left, value_transferred, now, minimum_balance],
(v1, "_v1") => [gas_left],
}
fn weight_to_fee(gas: u64, output: &mut &mut [u8]) {
let mut output_len = output.len() as u32;
{
unsafe { sys::weight_to_fee(gas, output.as_mut_ptr(), &mut output_len) };
}
extract_from_slice(output, output_len as usize);
}
fn weight_to_fee_v1(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]) {
let mut output_len = output.len() as u32;
{
unsafe {
sys::v1::weight_to_fee(
ref_time_limit,
proof_size_limit,
output.as_mut_ptr(),
&mut output_len,
)
};
}
extract_from_slice(output, output_len as usize);
}
impl_hash_fn!(sha2_256, 32);
impl_hash_fn!(keccak_256, 32);
impl_hash_fn!(blake2_256, 32);
impl_hash_fn!(blake2_128, 16);
fn ecdsa_recover(
signature: &[u8; 65],
message_hash: &[u8; 32],
output: &mut [u8; 33],
) -> Result {
let ret_code = unsafe {
sys::ecdsa_recover(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr())
};
ret_code.into()
}
fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result {
let ret_code = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) };
ret_code.into()
}
fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result {
let ret_code = unsafe {
sys::sr25519_verify(
signature.as_ptr(),
pub_key.as_ptr(),
message.len() as u32,
message.as_ptr(),
)
};
ret_code.into()
}
fn is_contract(account_id: &[u8]) -> bool {
let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) };
ret_val.into_bool()
}
fn caller_is_origin() -> bool {
let ret_val = unsafe { sys::caller_is_origin() };
ret_val.into_bool()
}
fn set_code_hash(code_hash: &[u8]) -> Result {
let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) };
ret_val.into()
}
fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result {
let mut output_len = output.len() as u32;
let ret_val =
unsafe { sys::code_hash(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) };
ret_val.into()
}
fn own_code_hash(output: &mut [u8]) {
let mut output_len = output.len() as u32;
unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) }
}
fn account_reentrance_count(account: &[u8]) -> u32 {
unsafe { sys::account_reentrance_count(account.as_ptr()) }
}
fn add_delegate_dependency(code_hash: &[u8]) {
unsafe { sys::add_delegate_dependency(code_hash.as_ptr()) }
}
fn remove_delegate_dependency(code_hash: &[u8]) {
unsafe { sys::remove_delegate_dependency(code_hash.as_ptr()) }
}
fn instantiation_nonce() -> u64 {
unsafe { sys::instantiation_nonce() }
}
fn reentrance_count() -> u32 {
unsafe { sys::reentrance_count() }
}
fn xcm_execute(msg: &[u8], output: &mut &mut [u8]) -> Result {
let ret_code =
unsafe { sys::xcm_execute(msg.as_ptr(), msg.len() as _, output.as_mut_ptr()) };
ret_code.into()
}
fn xcm_send(dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result {
let ret_code = unsafe {
sys::xcm_send(dest.as_ptr(), msg.as_ptr(), msg.len() as _, output.as_mut_ptr())
};
ret_code.into()
}
}
+139
View File
@@ -0,0 +1,139 @@
// Copyright (C) Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! External C API to communicate with substrate contracts runtime module.
//!
//! Refer to substrate FRAME contract module for more documentation.
#![no_std]
mod flags;
pub use flags::*;
#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))]
mod host;
#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))]
pub use host::*;
macro_rules! define_error_codes {
(
$(
$( #[$attr:meta] )*
$name:ident = $discr:literal,
)*
) => {
/// Every error that can be returned to a contract when it calls any of the host functions.
#[derive(Debug)]
#[repr(u32)]
pub enum ReturnErrorCode {
/// API call successful.
Success = 0,
$(
$( #[$attr] )*
$name = $discr,
)*
/// Returns if an unknown error was received from the host module.
Unknown,
}
impl From<ReturnCode> for Result {
#[inline]
fn from(return_code: ReturnCode) -> Self {
match return_code.0 {
0 => Ok(()),
$(
$discr => Err(ReturnErrorCode::$name),
)*
_ => Err(ReturnErrorCode::Unknown),
}
}
}
};
}
impl From<ReturnErrorCode> for u32 {
fn from(code: ReturnErrorCode) -> u32 {
code as u32
}
}
define_error_codes! {
/// The called function trapped and has its state changes reverted.
/// In this case no output buffer is returned.
/// Can only be returned from `call` and `instantiate`.
CalleeTrapped = 1,
/// The called function ran to completion but decided to revert its state.
/// An output buffer is returned when one was supplied.
/// Can only be returned from `call` and `instantiate`.
CalleeReverted = 2,
/// The passed key does not exist in storage.
KeyNotFound = 3,
/// Deprecated and no longer returned: There is only the minimum balance.
_BelowSubsistenceThreshold = 4,
/// Transfer failed for other not further specified reason. Most probably
/// reserved or locked balance of the sender that was preventing the transfer.
TransferFailed = 5,
/// 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 account that was called is no contract.
NotCallable = 8,
/// The call to `debug_message` had no effect because debug message
/// recording was disabled.
LoggingDisabled = 9,
/// The call dispatched by `call_runtime` was executed but returned an error.
CallRuntimeFailed = 10,
/// ECDSA public key recovery failed. Most probably wrong recovery id or signature.
EcdsaRecoveryFailed = 11,
/// sr25519 signature verification failed.
Sr25519VerifyFailed = 12,
/// The `xcm_execute` call failed.
XcmExecutionFailed = 13,
/// The `xcm_send` call failed.
XcmSendFailed = 14,
}
/// The raw return code returned by the host side.
#[repr(transparent)]
pub struct ReturnCode(u32);
/// Used as a sentinel value when reading and writing contract memory.
///
/// We use this value to signal `None` to a contract when only a primitive is
/// allowed and we don't want to go through encoding a full Rust type.
/// Using `u32::Max` is a safe sentinel because contracts are never
/// allowed to use such a large amount of resources. So this value doesn't
/// make sense for a memory location or length.
const SENTINEL: u32 = u32::MAX;
impl From<ReturnCode> for Option<u32> {
fn from(code: ReturnCode) -> Self {
(code.0 < SENTINEL).then_some(code.0)
}
}
impl ReturnCode {
/// Returns the raw underlying `u32` representation.
pub fn into_u32(self) -> u32 {
self.0
}
/// Returns the underlying `u32` converted into `bool`.
pub fn into_bool(self) -> bool {
self.0.ne(&0)
}
}
type Result = core::result::Result<(), ReturnErrorCode>;