diff --git a/CHANGELOG.md b/CHANGELOG.md index abdba2861da..b24fb09107f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Implements the API for the `pallet-revive` host function `to_account_id` - [#2578](https://github.com/use-ink/ink/pull/2578) - Add `#[ink::contract_ref]` attribute - [#2648](https://github.com/use-ink/ink/pull/2648) +- Add `ink_revive_types` (and remove `pallet-revive` dependency from `ink_e2e`) - [#2657](https://github.com/use-ink/ink/pull/2657) ### Changed - Marks the `pallet-revive` host function `account_id` stable - [#2578](https://github.com/use-ink/ink/pull/2578) diff --git a/Cargo.lock b/Cargo.lock index 60c18bf98bb..21bc59fed67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1224,9 +1224,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" dependencies = [ "cc", "glob", @@ -1336,9 +1336,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "2.1.1" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7318cfa722931cb5fe0838b98d3ce5621e75f6a6408abc21721d80de9223f2e4" +checksum = "137a2a2878ed823ef1bd73e5441e245602aae5360022113b8ad259ca4b5b8727" dependencies = [ "blst", "cc", @@ -1538,7 +1538,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -2346,7 +2346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys 0.60.2", ] [[package]] @@ -3485,10 +3485,10 @@ dependencies = [ "ink_e2e_macro", "ink_env", "ink_primitives 6.0.0-alpha.4", + "ink_revive_types", "ink_sandbox", "itertools 0.14.0", "jsonrpsee", - "pallet-revive", "parity-scale-codec", "regex", "scale-info", @@ -3713,6 +3713,20 @@ dependencies = [ "xxhash-rust", ] +[[package]] +name = "ink_revive_types" +version = "6.0.0-alpha.4" +dependencies = [ + "alloy-core", + "derive_more 2.0.1", + "ink_primitives 6.0.0-alpha.4", + "pallet-revive-uapi", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", +] + [[package]] name = "ink_sandbox" version = "6.0.0-alpha.4" @@ -3721,6 +3735,7 @@ dependencies = [ "frame-support", "frame-system", "ink_primitives 6.0.0-alpha.4", + "ink_revive_types", "pallet-balances", "pallet-revive", "pallet-timestamp", @@ -5836,7 +5851,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.0", + "windows-sys 0.60.2", ] [[package]] @@ -7847,7 +7862,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.61.0", + "windows-sys 0.60.2", ] [[package]] @@ -8747,7 +8762,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 95fac355d3a..0cafe5d2405 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/ink/ir", "crates/ink/macro", "crates/metadata", + "crates/revive-types", "crates/prelude", "crates/primitives", "crates/storage", @@ -104,6 +105,7 @@ xcm = { package = "staging-xcm", git = "https://github.com/use-ink/polkadot-sdk. polkavm-derive = { version = "0.26.0", default-features = false } # Solidity dependencies +alloy-core = { version = "1.1.0", default-features = false } alloy-sol-types = { version = "1.3.0", default-features = false } const_format = { version = "0.2.34", features = ["fmt"] } keccak-const = "0.2.0" @@ -121,6 +123,7 @@ ink_macro = { version = "=6.0.0-alpha.4", path = "crates/ink/macro", default-fea ink_metadata = { version = "=6.0.0-alpha.4", path = "crates/metadata", default-features = false } ink_prelude = { version = "=6.0.0-alpha.4", path = "crates/prelude", default-features = false } ink_primitives = { version = "=6.0.0-alpha.4", path = "crates/primitives", default-features = false } +ink_revive_types = { version = "=6.0.0-alpha.4", path = "crates/revive-types", default-features = false } ink_storage = { version = "=6.0.0-alpha.4", path = "crates/storage", default-features = false } ink_storage_traits = { version = "=6.0.0-alpha.4", path = "crates/storage/traits", default-features = false } diff --git a/crates/e2e/Cargo.toml b/crates/e2e/Cargo.toml index e83b112dcf7..c65f1cc9e0b 100644 --- a/crates/e2e/Cargo.toml +++ b/crates/e2e/Cargo.toml @@ -19,11 +19,11 @@ ink_e2e_macro = { workspace = true, default-features = true } ink = { workspace = true, default-features = true } ink_env = { workspace = true, default-features = true } ink_primitives = { workspace = true, default-features = true } +ink_revive_types = { workspace = true, default-features = true } ink_sandbox = { version = "=6.0.0-alpha.4", path = "./sandbox", optional = true } cargo_metadata = { workspace = true } contract-build = { workspace = true } -pallet-revive = { workspace = true } funty = { workspace = true } impl-serde = { workspace = true } jsonrpsee = { workspace = true, features = ["ws-client"] } @@ -61,7 +61,6 @@ default = [ "std" ] std = [ "impl-serde/std", "ink_e2e_macro/std", - "pallet-revive/std", "scale-info/std", "scale/std", "serde/std", @@ -72,6 +71,7 @@ std = [ "sp-runtime-interface/std", "sp-weights/std", "ink_e2e_macro/std", + "ink_revive_types/std", "ink_sandbox?/std", "frame-support/std", ] diff --git a/crates/e2e/sandbox/Cargo.toml b/crates/e2e/sandbox/Cargo.toml index 35ee1eee1e3..608d2f20382 100644 --- a/crates/e2e/sandbox/Cargo.toml +++ b/crates/e2e/sandbox/Cargo.toml @@ -23,6 +23,7 @@ sp-externalities = { workspace = true } sp-runtime = { workspace = true } sp-io = { workspace = true } ink_primitives = { workspace = true } +ink_revive_types = { workspace = true } paste = { workspace = true } scale-info = { workspace = true } @@ -37,6 +38,7 @@ std = [ "frame-system/std", "frame-metadata/std", "ink_primitives/std", + "ink_revive_types/std", "pallet-balances/std", "pallet-revive/std", "pallet-timestamp/std", diff --git a/crates/e2e/sandbox/src/lib.rs b/crates/e2e/sandbox/src/lib.rs index 3cae6bb33d9..4f88bb95f34 100644 --- a/crates/e2e/sandbox/src/lib.rs +++ b/crates/e2e/sandbox/src/lib.rs @@ -17,6 +17,10 @@ use frame_system::{ }, }; use ink_primitives::U256; +use ink_revive_types::evm::{ + CallLog, + CallTrace, +}; pub use macros::{ BlockBuilder, DefaultSandbox, @@ -170,3 +174,64 @@ where let evm_value: U256 = value.into(); native_to_eth_ratio.saturating_mul(evm_value) } + +/// Convert a `pallet_revive::CallTrace` (sandbox) into an `ink_revive_types::CallTrace` +/// (API). +pub fn to_revive_trace(t: pallet_revive::evm::CallTrace) -> CallTrace { + CallTrace { + from: t.from, + gas: t.gas, + gas_used: t.gas_used, + to: t.to, + input: t.input.0, + output: t.output.0, + error: t.error, + revert_reason: t.revert_reason, + calls: t.calls.into_iter().map(to_revive_trace).collect(), + logs: t + .logs + .into_iter() + .map(|log| { + CallLog { + address: log.address, + topics: log.topics, + data: log.data.0, + ..Default::default() + } + }) + .collect(), + value: t.value, + call_type: to_revive_call_type(t.call_type), + } +} + +/// Convert a `pallet_revive::CallType` into an `ink_revive_types::evm::CallType`. +fn to_revive_call_type( + ct: pallet_revive::evm::CallType, +) -> ink_revive_types::evm::CallType { + match ct { + pallet_revive::evm::CallType::Call => ink_revive_types::evm::CallType::Call, + pallet_revive::evm::CallType::StaticCall => { + ink_revive_types::evm::CallType::StaticCall + } + pallet_revive::evm::CallType::DelegateCall => { + ink_revive_types::evm::CallType::DelegateCall + } + pallet_revive::evm::CallType::Create => ink_revive_types::evm::CallType::Create, + pallet_revive::evm::CallType::Create2 => ink_revive_types::evm::CallType::Create2, + } +} + +/// Convert a `pallet_revive::StorageDeposit` into an `ink_revive_types::StorageDeposit`. +pub fn to_revive_storage_deposit( + sd: pallet_revive::StorageDeposit, +) -> ink_revive_types::StorageDeposit { + match sd { + pallet_revive::StorageDeposit::Charge(b) => { + ink_revive_types::StorageDeposit::Charge(b) + } + pallet_revive::StorageDeposit::Refund(b) => { + ink_revive_types::StorageDeposit::Refund(b) + } + } +} diff --git a/crates/e2e/src/backend.rs b/crates/e2e/src/backend.rs index 759059f2da8..958c7d8c304 100644 --- a/crates/e2e/src/backend.rs +++ b/crates/e2e/src/backend.rs @@ -21,8 +21,8 @@ use ink_primitives::{ H160, abi::AbiEncodeWith, }; +use ink_revive_types::evm::CallTrace; use jsonrpsee::core::async_trait; -use pallet_revive::evm::CallTrace; use sp_weights::Weight; use subxt::dynamic::Value; diff --git a/crates/e2e/src/contract_results.rs b/crates/e2e/src/contract_results.rs index 99e844b258a..5bb49c0fa27 100644 --- a/crates/e2e/src/contract_results.rs +++ b/crates/e2e/src/contract_results.rs @@ -36,7 +36,7 @@ use ink_primitives::{ H256, MessageResult, }; -use pallet_revive::{ +use ink_revive_types::{ CodeUploadResult, ExecReturnValue, InstantiateReturnValue, diff --git a/crates/e2e/src/error.rs b/crates/e2e/src/error.rs index 71f4fcd7962..8fb99044301 100644 --- a/crates/e2e/src/error.rs +++ b/crates/e2e/src/error.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use pallet_revive::evm::CallTrace; +use ink_revive_types::evm::CallTrace; use std::fmt; /// An error occurred while interacting with the E2E backend. diff --git a/crates/e2e/src/lib.rs b/crates/e2e/src/lib.rs index 4698998956f..096d9e96b5d 100644 --- a/crates/e2e/src/lib.rs +++ b/crates/e2e/src/lib.rs @@ -53,11 +53,11 @@ pub use contract_results::{ UploadResult, }; pub use ink_e2e_macro::test; +pub use ink_revive_types::evm::CallTrace; pub use node_proc::{ TestNodeProcess, TestNodeProcessBuilder, }; -pub use pallet_revive::evm::CallTrace; #[cfg(feature = "sandbox")] pub use sandbox_client::{ Client as SandboxClient, diff --git a/crates/e2e/src/sandbox_client.rs b/crates/e2e/src/sandbox_client.rs index 56714feab1e..5139bc390d5 100644 --- a/crates/e2e/src/sandbox_client.rs +++ b/crates/e2e/src/sandbox_client.rs @@ -59,8 +59,15 @@ use ink_env::{ use ink_primitives::{ DepositLimit, H160, + U256, abi::AbiEncodeWith, }; +use ink_revive_types::{ + CodeUploadReturnValue, + ExecReturnValue, + InstantiateReturnValue, + evm::CallTrace, +}; use ink_sandbox::{ AccountIdFor, RuntimeCall, @@ -71,21 +78,19 @@ use ink_sandbox::{ frame_system::pallet_prelude::OriginFor, pallet_balances, pallet_revive, -}; -use jsonrpsee::core::async_trait; -use pallet_revive::{ - AddressMapper, - CodeUploadReturnValue, - InstantiateReturnValue, - MomentOf, - evm::{ - CallTrace, - CallTracerConfig, - Trace, - TracerType, - U256, + pallet_revive::{ + AddressMapper, + MomentOf, + evm::{ + CallTracerConfig, + Trace, + TracerType, + }, }, + to_revive_storage_deposit, + to_revive_trace, }; +use jsonrpsee::core::async_trait; use scale::Decode; use sp_core::{ Pair as _, @@ -360,7 +365,7 @@ where }; let trace = match tracer.collect_trace() { - Some(Trace::Call(call_trace)) => Some(call_trace), + Some(Trace::Call(call_trace)) => Some(to_revive_trace(call_trace)), _ => None, }; @@ -444,10 +449,13 @@ where let result = ContractResult:: { gas_consumed: dry_run_result.gas_consumed, gas_required: dry_run_result.gas_required, - storage_deposit: dry_run_result.storage_deposit, + storage_deposit: to_revive_storage_deposit(dry_run_result.storage_deposit), result: dry_run_result.result.map(|res| { InstantiateReturnValue { - result: res.result, + result: ExecReturnValue { + flags: res.result.flags, + data: res.result.data, + }, addr: res.addr, } }), @@ -556,7 +564,7 @@ where .map_err(|err| SandboxErr::new(format!("bare_call: {err:?}"))) })?; let trace = match tracer.collect_trace() { - Some(Trace::Call(call_trace)) => Some(call_trace), + Some(Trace::Call(call_trace)) => Some(to_revive_trace(call_trace)), _ => None, }; @@ -630,8 +638,13 @@ where exec_result: ContractExecResultFor:: { gas_consumed: result.gas_consumed, gas_required: result.gas_required, - storage_deposit: result.storage_deposit, - result: result.result, + storage_deposit: to_revive_storage_deposit(result.storage_deposit), + result: result.result.map(|res| { + ExecReturnValue { + flags: res.flags, + data: res.data, + } + }), }, trace: None, // todo _marker: Default::default(), diff --git a/crates/e2e/src/subxt_client.rs b/crates/e2e/src/subxt_client.rs index 4fb2bf5a4f9..5bc722049d7 100644 --- a/crates/e2e/src/subxt_client.rs +++ b/crates/e2e/src/subxt_client.rs @@ -73,8 +73,8 @@ use ink_primitives::{ DepositLimit, abi::AbiEncodeWith, }; +use ink_revive_types::evm::CallTrace; use jsonrpsee::core::async_trait; -use pallet_revive::evm::CallTrace; use scale::{ Decode, Encode, diff --git a/crates/e2e/src/xts.rs b/crates/e2e/src/xts.rs index 4874ba85b5e..0e9e488e83b 100644 --- a/crates/e2e/src/xts.rs +++ b/crates/e2e/src/xts.rs @@ -29,7 +29,7 @@ use ink_primitives::{ Address, DepositLimit, }; -use pallet_revive::{ +use ink_revive_types::{ CodeUploadResult, evm::{ CallTrace, diff --git a/crates/revive-types/Cargo.toml b/crates/revive-types/Cargo.toml new file mode 100644 index 00000000000..b4adf252d7b --- /dev/null +++ b/crates/revive-types/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "ink_revive_types" +version.workspace = true +authors = ["Use Ink ", "Parity Technologies "] +edition.workspace = true +license.workspace = true +description = "Primitives copied from Parity's Polkadot SDK module `pallet-revive`. Copied to reduce the build times of ink! contracts." +repository.workspace = true +homepage.workspace = true +keywords.workspace = true +categories.workspace = true + +[lib] +path = "src/lib.rs" + +[dependencies] +scale = { workspace = true } +scale-info = { workspace = true } +serde = { workspace = true, features = ["derive"] } +ink_primitives = { workspace = true } +derive_more = { workspace = true, features = ["from", "try_into"] } +sp-runtime = { workspace = true } +pallet-revive-uapi = { workspace = true, features = ["scale"] } +alloy-core = { workspace = true, features = ["sol-types"] } + +[features] +default = ["std"] +std = [ + "alloy-core/std", + "scale/std", + "scale-info/std", + "ink_primitives/std", + "sp-runtime/std", + "serde/std", +] + diff --git a/crates/revive-types/src/evm.rs b/crates/revive-types/src/evm.rs new file mode 100644 index 00000000000..77c78ac5bb3 --- /dev/null +++ b/crates/revive-types/src/evm.rs @@ -0,0 +1,285 @@ +// 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 alloc::{ + collections::BTreeMap, + string::String, + vec::Vec, +}; +use derive_more::From; +use ink_primitives::{ + H160, + H256, + U256, +}; +use scale::{ + Decode, + Encode, +}; +use scale_info::TypeInfo; +use serde::{ + Deserialize, + Serialize, + ser::{ + SerializeMap, + Serializer, + }, +}; + +/// A smart contract execution call trace. +#[derive( + TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, +)] +#[serde(rename_all = "camelCase")] +pub struct CallTrace { + /// Address of the sender. + pub from: H160, + /// Amount of gas provided for the call. + pub gas: Gas, + /// Amount of gas used. + pub gas_used: Gas, + /// Address of the receiver. + pub to: H160, + /// Call input data. + pub input: Vec, + /// Return data. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub output: Vec, + /// The error message if the call failed. + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + /// The revert reason, if the call reverted. + #[serde(skip_serializing_if = "Option::is_none")] + pub revert_reason: Option, + /// List of sub-calls. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub calls: Vec>, + /// List of logs emitted during the call. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub logs: Vec, + /// Amount of value transferred. + #[serde(skip_serializing_if = "Option::is_none")] + pub value: Option, + /// Type of call. + #[serde(rename = "type")] + pub call_type: CallType, +} + +/// A log emitted during a call. +#[derive( + Debug, Default, Clone, Encode, Decode, TypeInfo, Serialize, Deserialize, Eq, PartialEq, +)] +pub struct CallLog { + /// The address of the contract that emitted the log. + pub address: H160, + /// The topics used to index the log. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub topics: Vec, + /// The log's data. + pub data: Vec, + /// Position of the log relative to subcalls within the same trace + /// See for details + #[serde(with = "super::hex_serde")] + pub position: u32, +} + +/// The type of call that was executed. +#[derive( + Default, TypeInfo, Encode, Decode, Serialize, Deserialize, Eq, PartialEq, Clone, Debug, +)] +#[serde(rename_all = "UPPERCASE")] +pub enum CallType { + /// A regular call. + #[default] + Call, + /// A read-only call. + StaticCall, + /// A delegate call. + DelegateCall, + /// A create call. + Create, + /// A create2 call. + Create2, +} + +/// The configuration for the call tracer. +#[derive(Clone, Debug, Decode, Serialize, Deserialize, Encode, PartialEq, TypeInfo)] +#[serde(default, rename_all = "camelCase")] +pub struct CallTracerConfig { + /// Whether to include logs in the trace. + pub with_logs: bool, + + /// Whether to only include the top-level calls in the trace. + pub only_top_call: bool, +} + +impl Default for CallTracerConfig { + fn default() -> Self { + Self { + with_logs: true, + only_top_call: false, + } + } +} + +/// A Trace +#[derive( + TypeInfo, From, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, +)] +#[serde(untagged)] +pub enum Trace { + /// A call trace. + Call(CallTrace), + /// A prestate trace. + Prestate(PrestateTrace), +} + +/// A prestate Trace +#[derive( + TypeInfo, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, +)] +#[serde(untagged)] +pub enum PrestateTrace { + /// The Prestate mode returns the accounts necessary to execute a given transaction + Prestate(BTreeMap), + + /// The diff mode returns the differences between the transaction's pre and post-state + /// The result only contains the accounts that were modified by the transaction + DiffMode { + /// The state before the call. + /// The accounts in the `pre` field will contain all of their basic fields, even + /// if those fields have not been modified. For `storage` however, only + /// non-empty slots that have been modified will be included + pre: BTreeMap, + /// The state after the call. + /// It only contains the specific fields that were actually modified during the + /// transaction + post: BTreeMap, + }, +} + +impl PrestateTrace { + /// Returns the pre and post trace info. + pub fn state_mut( + &mut self, + ) -> ( + &mut BTreeMap, + Option<&mut BTreeMap>, + ) { + match self { + PrestateTrace::Prestate(pre) => (pre, None), + PrestateTrace::DiffMode { pre, post } => (pre, Some(post)), + } + } +} + +/// The info of a prestate trace. +#[derive( + TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, +)] +pub struct PrestateTraceInfo { + /// The balance of the account. + #[serde(skip_serializing_if = "Option::is_none")] + pub balance: Option, + /// The nonce of the account. + #[serde(skip_serializing_if = "Option::is_none")] + pub nonce: Option, + /// The code of the contract account. + #[serde(skip_serializing_if = "Option::is_none")] + pub code: Option>, + /// The storage of the contract account. + #[serde( + skip_serializing_if = "is_empty", + serialize_with = "serialize_map_skip_none" + )] + pub storage: BTreeMap, Option>>, +} + +/// Returns true if the map has no `Some` element +pub fn is_empty(map: &BTreeMap>) -> bool { + !map.values().any(|v| v.is_some()) +} + +/// Serializes a map, skipping `None` values. +pub fn serialize_map_skip_none( + map: &BTreeMap>, + serializer: S, +) -> Result +where + S: Serializer, + K: serde::Serialize, + V: serde::Serialize, +{ + let len = map.values().filter(|v| v.is_some()).count(); + let mut ser_map = serializer.serialize_map(Some(len))?; + + for (key, opt_val) in map { + if let Some(val) = opt_val { + ser_map.serialize_entry(key, val)?; + } + } + + ser_map.end() +} + +/// The type of tracer to use. +/// Only "callTracer" is supported for now. +#[derive(TypeInfo, Debug, Clone, Encode, Decode, Serialize, Deserialize, PartialEq)] +#[serde(tag = "tracer", content = "tracerConfig", rename_all = "camelCase")] +pub enum TracerType { + /// A tracer that traces calls. + CallTracer(Option), + + /// A tracer that traces the prestate. + PrestateTracer(Option), +} + +impl From for TracerType { + fn from(config: CallTracerConfig) -> Self { + TracerType::CallTracer(Some(config)) + } +} + +impl Default for TracerType { + fn default() -> Self { + TracerType::CallTracer(Some(CallTracerConfig::default())) + } +} + +/// The configuration for the prestate tracer. +#[derive(Clone, Debug, Decode, Serialize, Deserialize, Encode, PartialEq, TypeInfo)] +#[serde(default, rename_all = "camelCase")] +pub struct PrestateTracerConfig { + /// Whether to include the diff mode in the trace. + pub diff_mode: bool, + + /// Whether to include storage in the trace. + pub disable_storage: bool, + + /// Whether to include code in the trace. + pub disable_code: bool, +} + +impl Default for PrestateTracerConfig { + fn default() -> Self { + Self { + diff_mode: false, + disable_storage: false, + disable_code: false, + } + } +} diff --git a/crates/revive-types/src/hex_serde.rs b/crates/revive-types/src/hex_serde.rs new file mode 100644 index 00000000000..15fcd849ded --- /dev/null +++ b/crates/revive-types/src/hex_serde.rs @@ -0,0 +1,95 @@ +// 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 alloc::{ + format, + string::String, + vec::Vec, +}; +use alloy_core::hex; +use serde::{ + Deserialize, + Deserializer, + Serializer, +}; + +pub trait HexCodec: Sized { + type Error; + fn to_hex(&self) -> String; + fn from_hex(s: String) -> Result; +} + +macro_rules! impl_hex_codec { + ($($t:ty),*) => { + $( + impl HexCodec for $t { + type Error = core::num::ParseIntError; + fn to_hex(&self) -> String { + format!("0x{:x}", self) + } + fn from_hex(s: String) -> Result { + <$t>::from_str_radix(s.trim_start_matches("0x"), 16) + } + } + )* + }; +} + +impl_hex_codec!(u8, u32); + +impl HexCodec for [u8; T] { + type Error = hex::FromHexError; + fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self)) + } + fn from_hex(s: String) -> Result { + let data = hex::decode(s.trim_start_matches("0x"))?; + data.try_into() + .map_err(|_| hex::FromHexError::InvalidStringLength) + } +} + +impl HexCodec for Vec { + type Error = hex::FromHexError; + fn to_hex(&self) -> String { + format!("0x{}", hex::encode(self)) + } + fn from_hex(s: String) -> Result { + hex::decode(s.trim_start_matches("0x")) + } +} + +pub fn serialize(value: &T, serializer: S) -> Result +where + S: Serializer, + T: HexCodec, +{ + let s = value.to_hex(); + serializer.serialize_str(&s) +} + +pub fn deserialize<'de, D, T>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: HexCodec, + ::Error: core::fmt::Debug, +{ + let s = String::deserialize(deserializer)?; + let value = + T::from_hex(s).map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?; + Ok(value) +} diff --git a/crates/revive-types/src/lib.rs b/crates/revive-types/src/lib.rs new file mode 100644 index 00000000000..dde5d22af58 --- /dev/null +++ b/crates/revive-types/src/lib.rs @@ -0,0 +1,29 @@ +// Copyright (C) Use Ink (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. + +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +pub mod evm; +mod hex_serde; +mod primitives; + +pub use primitives::{ + CodeUploadResult, + CodeUploadReturnValue, + ContractResult, + ExecReturnValue, + InstantiateReturnValue, + StorageDeposit, +}; diff --git a/crates/revive-types/src/primitives.rs b/crates/revive-types/src/primitives.rs new file mode 100644 index 00000000000..1b41461f70f --- /dev/null +++ b/crates/revive-types/src/primitives.rs @@ -0,0 +1,229 @@ +// 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. + +#![cfg_attr(not(feature = "std"), allow(unused_imports))] + +use alloc::vec::Vec; +use ink_primitives::{ + H160, + H256, + Weight, +}; +use pallet_revive_uapi::ReturnFlags; +use scale::{ + Decode, + Encode, + MaxEncodedLen, +}; +use scale_info::TypeInfo; +use sp_runtime::{ + DispatchError, + RuntimeDebug, + traits::{ + Saturating, + Zero, + }, +}; + +/// The amount of balance that was either charged or refunded in order to pay for storage. +#[derive( + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + MaxEncodedLen, + RuntimeDebug, + TypeInfo, +)] +pub enum StorageDeposit { + /// The transaction reduced storage consumption. + /// + /// This means that the specified amount of balance was transferred from the involved + /// deposit accounts to the origin. + Refund(Balance), + /// The transaction increased storage consumption. + /// + /// This means that the specified amount of balance was transferred from the origin + /// to the involved deposit accounts. + Charge(Balance), +} + +impl Default for StorageDeposit { + fn default() -> Self { + Self::Charge(Zero::zero()) + } +} + +impl StorageDeposit { + /// Returns how much balance is charged or `0` in case of a refund. + pub fn charge_or_zero(&self) -> Balance { + match self { + Self::Charge(amount) => *amount, + Self::Refund(_) => Zero::zero(), + } + } + + pub fn is_zero(&self) -> bool { + match self { + Self::Charge(amount) => amount.is_zero(), + Self::Refund(amount) => amount.is_zero(), + } + } +} + +impl StorageDeposit +where + Balance: Saturating + Ord + Copy, +{ + /// This is essentially a saturating signed add. + pub fn saturating_add(&self, rhs: &Self) -> Self { + use StorageDeposit::*; + match (self, rhs) { + (Charge(lhs), Charge(rhs)) => Charge(lhs.saturating_add(*rhs)), + (Refund(lhs), Refund(rhs)) => Refund(lhs.saturating_add(*rhs)), + (Charge(lhs), Refund(rhs)) => { + if lhs >= rhs { + Charge(lhs.saturating_sub(*rhs)) + } else { + Refund(rhs.saturating_sub(*lhs)) + } + } + (Refund(lhs), Charge(rhs)) => { + if lhs > rhs { + Refund(lhs.saturating_sub(*rhs)) + } else { + Charge(rhs.saturating_sub(*lhs)) + } + } + } + } + + /// This is essentially a saturating signed sub. + pub fn saturating_sub(&self, rhs: &Self) -> Self { + use StorageDeposit::*; + match (self, rhs) { + (Charge(lhs), Refund(rhs)) => Charge(lhs.saturating_add(*rhs)), + (Refund(lhs), Charge(rhs)) => Refund(lhs.saturating_add(*rhs)), + (Charge(lhs), Charge(rhs)) => { + if lhs >= rhs { + Charge(lhs.saturating_sub(*rhs)) + } else { + Refund(rhs.saturating_sub(*lhs)) + } + } + (Refund(lhs), Refund(rhs)) => { + if lhs > rhs { + Refund(lhs.saturating_sub(*rhs)) + } else { + Charge(rhs.saturating_sub(*lhs)) + } + } + } + } + + /// If the amount of deposit (this type) is constrained by a `limit` this calculates + /// how much balance (if any) is still available from this limit. + /// + /// # Note + /// + /// In case of a refund the return value can be larger than `limit`. + pub fn available(&self, limit: &Balance) -> Balance { + use StorageDeposit::*; + match self { + Charge(amount) => limit.saturating_sub(*amount), + Refund(amount) => limit.saturating_add(*amount), + } + } +} + +/// Result type of a `bare_call` or `bare_instantiate` call as well as +/// `ContractsApi::call` and `ContractsApi::instantiate`. +/// +/// It contains the execution result together with some auxiliary information. +/// +/// #Note +/// +/// It has been extended to include `events` at the end of the struct while not bumping +/// the `ContractsApi` version. Therefore when SCALE decoding a `ContractResult` its +/// trailing data should be ignored to avoid any potential compatibility issues. +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct ContractResult { + /// How much weight was consumed during execution. + pub gas_consumed: Weight, + /// How much weight is required as gas limit in order to execute this call. + /// + /// This value should be used to determine the weight limit for on-chain execution. + /// + /// # Note + /// + /// This can only be different from [`Self::gas_consumed`] when weight pre charging + /// is used. Currently, only `seal_call_runtime` makes use of pre charging. + /// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging + /// when a non-zero `gas_limit` argument is supplied. + pub gas_required: Weight, + /// How much balance was paid by the origin into the contract's deposit account in + /// order to pay for storage. + /// + /// The storage deposit is never actually charged from the origin in case of + /// [`Self::result`] is `Err`. This is because on error all storage changes are + /// rolled back including the payment of the deposit. + pub storage_deposit: StorageDeposit, + /// The execution result of the vm binary code. + pub result: Result, +} + +/// Output of a contract call or instantiation which ran to completion. +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)] +pub struct ExecReturnValue { + /// Flags passed along by `seal_return`. Empty when `seal_return` was never called. + pub flags: ReturnFlags, + /// Buffer passed along by `seal_return`. Empty when `seal_return` was never called. + pub data: Vec, +} + +impl ExecReturnValue { + /// The contract did revert all storage changes. + pub fn did_revert(&self) -> bool { + self.flags.contains(ReturnFlags::REVERT) + } +} + +/// The result of a successful contract instantiation. +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct InstantiateReturnValue { + /// The output of the called constructor. + pub result: ExecReturnValue, + /// The address of the new contract. + pub addr: H160, +} + +/// The result of successfully uploading a contract. +#[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)] +pub struct CodeUploadReturnValue { + /// The key under which the new code is stored. + pub code_hash: H256, + /// The deposit that was reserved at the caller. Is zero when the code already + /// existed. + pub deposit: Balance, +} + +/// Result type of a `bare_code_upload` call. +pub type CodeUploadResult = + Result, DispatchError>; diff --git a/integration-tests/solidity-abi/sol-cross-contract/Cargo.toml b/integration-tests/solidity-abi/sol-cross-contract/Cargo.toml index 2c71661acf4..dfc7b82d33e 100755 --- a/integration-tests/solidity-abi/sol-cross-contract/Cargo.toml +++ b/integration-tests/solidity-abi/sol-cross-contract/Cargo.toml @@ -12,7 +12,7 @@ sha3 = { version = "0.10", default-features = false } [dev-dependencies] ink_e2e = { path = "../../../crates/e2e", features = ["sandbox"] } ink_sandbox = { path = "../../../crates/e2e/sandbox" } -pallet-revive = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "a71ec19a94702ea71767ba5ac97603ea6c6305c1", default-features = false } +ink_revive_types = { path = "../../../crates/revive-types" } sha3 = { version = "0.10" } other-contract-sol = { path = "other-contract-sol" } diff --git a/integration-tests/solidity-abi/sol-cross-contract/e2e_tests.rs b/integration-tests/solidity-abi/sol-cross-contract/e2e_tests.rs index 741cbbe6b0a..dc2a13959bb 100644 --- a/integration-tests/solidity-abi/sol-cross-contract/e2e_tests.rs +++ b/integration-tests/solidity-abi/sol-cross-contract/e2e_tests.rs @@ -12,8 +12,8 @@ use ink::{ SolEncode, primitives::DepositLimit, }; +use ink_revive_types::ExecReturnValue; use ink_sandbox::frame_system::pallet_prelude::OriginFor; -use pallet_revive::ExecReturnValue; const STORAGE_DEPOSIT_LIMIT: DepositLimit = DepositLimit::UnsafeOnlyForDryRun; @@ -172,7 +172,11 @@ impl ContractSandbox { STORAGE_DEPOSIT_LIMIT, ); - result.result.expect("sandbox call contract failed") + let call_raw = result.result.expect("sandbox call contract failed"); + ExecReturnValue { + flags: call_raw.flags, + data: call_raw.data, + } } } diff --git a/integration-tests/solidity-abi/sol-encoding/Cargo.toml b/integration-tests/solidity-abi/sol-encoding/Cargo.toml index e6e3223c68a..8ac0cfcac38 100755 --- a/integration-tests/solidity-abi/sol-encoding/Cargo.toml +++ b/integration-tests/solidity-abi/sol-encoding/Cargo.toml @@ -11,7 +11,7 @@ ink = { path = "../../../crates/ink", default-features = false } [dev-dependencies] ink_e2e = { path = "../../../crates/e2e", features = ["sandbox"] } ink_sandbox = { path = "../../../crates/e2e/sandbox" } -pallet-revive = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "a71ec19a94702ea71767ba5ac97603ea6c6305c1", default-features = false } +ink_revive_types = { path = "../../../crates/revive-types" } sha3 = { version = "0.10" } [lib] diff --git a/integration-tests/solidity-abi/sol-encoding/e2e_tests.rs b/integration-tests/solidity-abi/sol-encoding/e2e_tests.rs index ba753d52518..1e81ae71345 100644 --- a/integration-tests/solidity-abi/sol-encoding/e2e_tests.rs +++ b/integration-tests/solidity-abi/sol-encoding/e2e_tests.rs @@ -6,13 +6,13 @@ use ink::{ primitives::DepositLimit, }; use ink_e2e::ContractsRegistry; +use ink_revive_types::ExecReturnValue; use ink_sandbox::{ DefaultSandbox, Sandbox, api::prelude::*, frame_system::pallet_prelude::OriginFor, }; -use pallet_revive::ExecReturnValue; const STORAGE_DEPOSIT_LIMIT: DepositLimit = DepositLimit::UnsafeOnlyForDryRun; @@ -114,7 +114,7 @@ impl ContractSandbox { data: Vec, origin: OriginFor<::Runtime>, ) -> ExecReturnValue { - ::call_contract( + let call_raw = ::call_contract( &mut self.sandbox, self.contract_addr, 0, @@ -124,7 +124,11 @@ impl ContractSandbox { STORAGE_DEPOSIT_LIMIT, ) .result - .expect("sandbox call contract failed") + .expect("sandbox call contract failed"); + ExecReturnValue { + flags: call_raw.flags, + data: call_raw.data, + } } }