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
@@ -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::{
+252
View File
@@ -0,0 +1,252 @@
// 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.
//! A crate that hosts a common definitions that are relevant for the pallet-contracts.
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::*;
/// 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<R, Balance, EventRecord> {
/// 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 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<Balance>,
/// An optional debug message. This message is only filled when explicitly requested
/// by the code that calls into the contract. Otherwise it is empty.
///
/// The contained bytes are valid UTF-8. This is not declared as `String` because
/// this type is not allowed within the runtime.
///
/// Clients should not make any assumptions about the format of the buffer.
/// They should just display it as-is. It is **not** only a collection of log lines
/// provided by a contract but a formatted buffer with different sections.
///
/// # Note
///
/// The debug message is never generated during on-chain execution. It is reserved for
/// RPC calls.
pub debug_message: Vec<u8>,
/// The execution result of the wasm code.
pub result: R,
/// The events that were emitted during execution. It is an option as event collection is
/// optional.
pub events: Option<Vec<EventRecord>>,
}
/// Result type of a `bare_call` call as well as `ContractsApi::call`.
pub type ContractExecResult<Balance, EventRecord> =
ContractResult<Result<ExecReturnValue, DispatchError>, Balance, EventRecord>;
/// Result type of a `bare_instantiate` call as well as `ContractsApi::instantiate`.
pub type ContractInstantiateResult<AccountId, Balance, EventRecord> =
ContractResult<Result<InstantiateReturnValue<AccountId>, DispatchError>, Balance, EventRecord>;
/// Result type of a `bare_code_upload` call.
pub type CodeUploadResult<CodeHash, Balance> =
Result<CodeUploadReturnValue<CodeHash, Balance>, DispatchError>;
/// Result type of a `get_storage` call.
pub type GetStorageResult = Result<Option<Vec<u8>>, ContractAccessError>;
/// The possible errors that can happen querying the storage of a contract.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
pub enum ContractAccessError {
/// The given address doesn't point to a contract.
DoesntExist,
/// Storage key cannot be decoded from the provided input data.
KeyDecodingFailed,
/// Storage is migrating. Try again later.
MigrationInProgress,
}
/// Output of a contract call or instantiation which ran to completion.
#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct ExecReturnValue {
/// Flags passed along by `seal_return`. Empty when `seal_return` was never called.
pub flags: ReturnFlags,
/// Buffer passed along by `seal_return`. Empty when `seal_return` was never called.
pub data: Vec<u8>,
}
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<AccountId> {
/// The output of the called constructor.
pub result: ExecReturnValue,
/// The account id of the new contract.
pub account_id: AccountId,
}
/// The result of successfully uploading a contract.
#[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)]
pub struct CodeUploadReturnValue<CodeHash, Balance> {
/// The key under which the new code is stored.
pub code_hash: CodeHash,
/// The deposit that was reserved at the caller. Is zero when the code already existed.
pub deposit: Balance,
}
/// Reference to an existing code hash or a new wasm module.
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum Code<Hash> {
/// A wasm module as raw bytes.
Upload(Vec<u8>),
/// The code hash of an on-chain wasm blob.
Existing(Hash),
}
/// 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<Balance> {
/// 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<Balance: Zero> Default for StorageDeposit<Balance> {
fn default() -> Self {
Self::Charge(Zero::zero())
}
}
impl<Balance: Zero + Copy> StorageDeposit<Balance> {
/// 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<Balance> StorageDeposit<Balance>
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),
}
}
}
+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