From f21834d48af988a4f11bb0951c9e8a8d8640f35f Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 11 Mar 2024 11:25:08 +0100 Subject: [PATCH] Rename & update SandboxConfig trait (#105) --- CHANGELOG.md | 4 + Cargo.lock | 1 + Cargo.toml | 1 + drink-cli/src/app_state/mod.rs | 8 +- drink-cli/src/executor/mod.rs | 11 +- drink/Cargo.toml | 3 +- drink/src/errors.rs | 6 - drink/src/lib.rs | 17 +- drink/src/runtime.rs | 6 +- drink/src/runtime/minimal.rs | 245 +++++++++++------- drink/src/sandbox.rs | 90 ++++--- drink/src/sandbox/balance_api.rs | 61 +++-- drink/src/sandbox/contracts_api.rs | 227 ++++++++++------ drink/src/sandbox/system_api.rs | 130 +++++++--- drink/src/sandbox/timestamp_api.rs | 39 ++- drink/src/session.rs | 144 +++++----- drink/src/session/mocking_api.rs | 18 +- drink/src/session/record.rs | 11 +- drink/test-macro/src/lib.rs | 18 +- examples/chain-extension/Cargo.lock | 5 +- examples/chain-extension/Cargo.toml | 2 +- examples/chain-extension/README.md | 12 +- examples/chain-extension/src/lib.rs | 6 +- examples/contract-events/Cargo.lock | 5 +- examples/contract-events/README.md | 5 +- .../cross-contract-call-tracing/Cargo.lock | 5 +- examples/dry-running/Cargo.lock | 5 +- examples/dry-running/lib.rs | 9 +- examples/flipper/Cargo.lock | 7 +- examples/flipper/Cargo.toml | 5 - examples/flipper/lib.rs | 20 -- examples/mocking/Cargo.lock | 5 +- examples/multiple-contracts/Cargo.lock | 5 +- examples/quick-start-with-drink/Cargo.lock | 5 +- examples/quick-start-with-drink/lib.rs | 2 +- examples/runtime-interaction/Cargo.lock | 5 +- examples/runtime-interaction/README.md | 11 +- examples/runtime-interaction/lib.rs | 17 +- 38 files changed, 692 insertions(+), 484 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0efd2..68f291d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.13.0] + +- Rework Sandbox API to better support custom Runtime + ## [Unreleased] # [0.13.0] diff --git a/Cargo.lock b/Cargo.lock index f2bab5b..de072de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1304,6 +1304,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", diff --git a/Cargo.toml b/Cargo.toml index d6d7ee3..79a7bea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ crossterm = { version = "0.26.0" } darling = { version = "0.20.3" } parity-scale-codec = { version = "3.6.9" } parity-scale-codec-derive = { version = "3.6.9" } +paste = { version = "1.0.7" } proc-macro2 = { version = "1" } quote = { version = "1" } ratatui = { version = "0.21.0" } diff --git a/drink-cli/src/app_state/mod.rs b/drink-cli/src/app_state/mod.rs index a8517fb..8028308 100644 --- a/drink-cli/src/app_state/mod.rs +++ b/drink-cli/src/app_state/mod.rs @@ -1,7 +1,7 @@ use std::{env, path::PathBuf}; pub use contracts::{Contract, ContractIndex, ContractRegistry}; -use drink::{runtime::MinimalRuntime, session::Session, SandboxConfig, Weight, DEFAULT_GAS_LIMIT}; +use drink::{runtime::MinimalSandbox, session::Session, Sandbox, Weight, DEFAULT_GAS_LIMIT}; use sp_core::crypto::AccountId32; pub use user_input::UserInput; @@ -23,7 +23,7 @@ impl Default for ChainInfo { fn default() -> Self { Self { block_height: 0, - actor: MinimalRuntime::default_actor(), + actor: MinimalSandbox::default_actor(), gas_limit: DEFAULT_GAS_LIMIT, } } @@ -69,7 +69,7 @@ impl Default for UiState { } pub struct AppState { - pub session: Session, + pub session: Session, pub chain_info: ChainInfo, pub ui_state: UiState, pub contracts: ContractRegistry, @@ -78,7 +78,7 @@ pub struct AppState { impl AppState { pub fn new(cwd_override: Option) -> Self { AppState { - session: Session::new().expect("Failed to create drinking session"), + session: Session::default(), chain_info: Default::default(), ui_state: UiState::new(cwd_override), contracts: Default::default(), diff --git a/drink-cli/src/executor/mod.rs b/drink-cli/src/executor/mod.rs index 74dbd1c..d9924e7 100644 --- a/drink-cli/src/executor/mod.rs +++ b/drink-cli/src/executor/mod.rs @@ -5,7 +5,7 @@ use std::env; use anyhow::Result; use clap::Parser; -use drink::Weight; +use drink::{sandbox::prelude::*, Weight}; use sp_core::crypto::AccountId32; use crate::{app_state::AppState, cli::CliCommand}; @@ -67,12 +67,7 @@ pub fn execute(app_state: &mut AppState) -> Result<()> { } fn build_blocks(app_state: &mut AppState, count: u32) { - app_state.chain_info.block_height = app_state - .session - .sandbox() - .build_blocks(count) - .expect("Failed to build block - chain is broken"); - + app_state.chain_info.block_height = app_state.session.sandbox().build_blocks(count); app_state.print(&format!("{count} blocks built")); } @@ -80,7 +75,7 @@ fn add_tokens(app_state: &mut AppState, recipient: AccountId32, value: u128) -> app_state .session .sandbox() - .mint_into(recipient.clone(), value) + .mint_into(&recipient, value) .map_err(|err| anyhow::format_err!("Failed to add token: {err:?}"))?; app_state.print(&format!("{value} tokens added to {recipient}",)); Ok(()) diff --git a/drink/Cargo.toml b/drink/Cargo.toml index 9ffeb6b..1a03946 100644 --- a/drink/Cargo.toml +++ b/drink/Cargo.toml @@ -25,8 +25,9 @@ sp-externalities = { workspace = true } sp-io = { workspace = true } sp-runtime-interface = { workspace = true } -serde_json = { workspace = true, optional = true } +paste = { workspace = true } scale-info = { workspace = true } +serde_json = { workspace = true, optional = true } thiserror = { workspace = true } wat = { workspace = true } diff --git a/drink/src/errors.rs b/drink/src/errors.rs index eac1ba8..a05bc39 100644 --- a/drink/src/errors.rs +++ b/drink/src/errors.rs @@ -8,12 +8,6 @@ pub enum Error { /// Externalities could not be initialized. #[error("Failed to build storage: {0}")] StorageBuilding(String), - /// Block couldn't have been initialized. - #[error("Failed to initialize block: {0}")] - BlockInitialize(String), - /// Block couldn't have been finalized. - #[error("Failed to finalize block: {0}")] - BlockFinalize(String), /// Bundle loading and parsing has failed #[error("Loading the contract bundle has failed: {0}")] BundleLoadFailed(String), diff --git a/drink/src/lib.rs b/drink/src/lib.rs index 43519e2..683f9bb 100644 --- a/drink/src/lib.rs +++ b/drink/src/lib.rs @@ -23,17 +23,15 @@ use pallet_contracts::{ContractExecResult, ContractInstantiateResult}; #[cfg(feature = "session")] pub use session::mock::{mock_message, ContractMock, MessageMock, MockedCallResult, Selector}; /// Export pallets that are used in the minimal runtime. -pub use {frame_support, frame_system, pallet_balances, pallet_contracts, pallet_timestamp}; +pub use { + frame_support, frame_system, pallet_balances, pallet_contracts, pallet_timestamp, paste, + sp_externalities::Extension, sp_io::TestExternalities, +}; -pub use crate::runtime::minimal::{self, MinimalRuntime}; -use crate::runtime::AccountIdFor; +pub use crate::runtime::minimal::{self, MinimalSandbox}; /// Alias for `frame-system`'s `RuntimeCall` type. -pub type RuntimeCall = ::RuntimeCall; - -/// Alias for `pallet-balances`'s Balance type. -pub type BalanceOf = - <::Currency as Inspect>>::Balance; +pub type RuntimeCall = ::RuntimeCall; /// Main result type for the drink crate. pub type DrinkResult = std::result::Result; @@ -44,6 +42,9 @@ pub type EventRecordOf = EventRecord< ::Hash, >; +type BalanceOf = + <::Currency as Inspect>>::Balance; + /// Copied from pallet-contracts. pub type ContractInstantiateResultFor = ContractInstantiateResult, BalanceOf, EventRecordOf>; diff --git a/drink/src/runtime.rs b/drink/src/runtime.rs index 2ba1039..4c603d9 100644 --- a/drink/src/runtime.rs +++ b/drink/src/runtime.rs @@ -1,10 +1,10 @@ -//! Module containing the [`Runtime`](Runtime) trait and its example implementations. You can use -//! `drink` with any runtime that implements the `Runtime` trait. +//! Module containing a [`MinimalSandbox`] that implements the [`crate::Sandbox`] trait. +//! Also contains debugging utilities for the contracts pallet. pub mod minimal; pub mod pallet_contracts_debugging; pub use frame_metadata::RuntimeMetadataPrefixed; -pub use minimal::MinimalRuntime; +pub use minimal::MinimalSandbox; /// The type of an account identifier. pub type AccountIdFor = ::AccountId; diff --git a/drink/src/runtime/minimal.rs b/drink/src/runtime/minimal.rs index a6f43db..e6e0584 100644 --- a/drink/src/runtime/minimal.rs +++ b/drink/src/runtime/minimal.rs @@ -1,94 +1,88 @@ #![allow(missing_docs)] // `construct_macro` doesn't allow doc comments for the runtime type. -/// The macro will generate an implementation of `drink::SandboxConfig` for the given runtime type. -#[macro_export] -macro_rules! impl_sandbox_config { - ( - $( #[ $attr:meta ] )* - $vis:vis struct $name:ident { - runtime: $runtime:tt; - default_balance: $default_balance:expr; - default_actor: $default_actor:expr; - } - ) => { - $( #[ $attr ] )* - $vis struct $name; - impl_sandbox_config!($name, $runtime, $default_balance, $default_actor); - }; - ( - $name:ident, $runtime:tt, $default_balance:expr, $default_actor:expr - ) => { - impl $crate::SandboxConfig for $name { - type Runtime = $runtime; - - fn initialize_storage(storage: &mut $crate::frame_support::sp_runtime::Storage) -> Result<(), String> { - use $crate::frame_support::sp_runtime::BuildStorage; - $crate::pallet_balances::GenesisConfig::<$runtime> { - balances: vec![(Self::default_actor(), $default_balance)], - } - .assimilate_storage(storage) - } - - fn initialize_block( - height: $crate::frame_system::pallet_prelude::BlockNumberFor<$runtime>, - parent_hash: <$runtime as $crate::frame_system::Config>::Hash, - ) -> Result<(), String> { - use std::time::SystemTime; - use $crate::frame_support::traits::Hooks; - - $crate::frame_system::Pallet::<$runtime>::reset_events(); - $crate::frame_system::Pallet::<$runtime>::initialize(&height, &parent_hash, &Default::default()); - $crate::pallet_balances::Pallet::<$runtime>::on_initialize(height); - $crate::pallet_timestamp::Pallet::<$runtime>::set_timestamp( - SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(), - ); - $crate::pallet_timestamp::Pallet::<$runtime>::on_initialize(height); - $crate::pallet_contracts::Pallet::<$runtime>::on_initialize(height); - $crate::frame_system::Pallet::<$runtime>::note_finished_initialize(); - Ok(()) - } - - fn finalize_block( - height: $crate::frame_system::pallet_prelude::BlockNumberFor<$runtime>, - ) -> Result<<$runtime as $crate::frame_system::Config>::Hash, String> { - use $crate::frame_support::traits::Hooks; - - $crate::pallet_contracts::Pallet::<$runtime>::on_finalize(height); - $crate::pallet_timestamp::Pallet::<$runtime>::on_finalize(height); - $crate::pallet_balances::Pallet::<$runtime>::on_finalize(height); - Ok($crate::frame_system::Pallet::<$runtime>::finalize().hash()) - } - - fn default_actor() -> $crate::runtime::AccountIdFor<$runtime> { - $default_actor - } - - fn get_metadata() -> $crate::runtime::RuntimeMetadataPrefixed { - $runtime::metadata() - } - - fn convert_account_to_origin( - account: $crate::runtime::AccountIdFor<$runtime>, - ) -> <<$runtime as $crate::frame_system::Config>::RuntimeCall as $crate::frame_support::sp_runtime::traits::Dispatchable>::RuntimeOrigin { - Some(account).into() - } - } - }; +use std::time::SystemTime; + +use frame_support::{ + sp_runtime::{ + traits::{Header, One}, + BuildStorage, + }, + traits::Hooks, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_io::TestExternalities; + +/// A helper struct for initializing and finalizing blocks. +pub struct BlockBuilder(std::marker::PhantomData); + +impl< + T: pallet_balances::Config + pallet_timestamp::Config + pallet_contracts::Config, + > BlockBuilder +{ + /// Create a new externalities with the given balances. + pub fn new_ext(balances: Vec<(T::AccountId, T::Balance)>) -> TestExternalities { + let mut storage = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut storage) + .unwrap(); + + let mut ext = TestExternalities::new(storage); + + ext.execute_with(|| Self::initialize_block(BlockNumberFor::::one(), Default::default())); + ext + } + + /// Initialize a new block at particular height. + pub fn initialize_block( + height: frame_system::pallet_prelude::BlockNumberFor, + parent_hash: ::Hash, + ) { + frame_system::Pallet::::reset_events(); + frame_system::Pallet::::initialize(&height, &parent_hash, &Default::default()); + pallet_balances::Pallet::::on_initialize(height); + pallet_timestamp::Pallet::::set_timestamp( + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Time went backwards") + .as_secs(), + ); + pallet_timestamp::Pallet::::on_initialize(height); + pallet_contracts::Pallet::::on_initialize(height); + frame_system::Pallet::::note_finished_initialize(); + } + + /// Finalize a block at particular height. + pub fn finalize_block( + height: frame_system::pallet_prelude::BlockNumberFor, + ) -> ::Hash { + pallet_contracts::Pallet::::on_finalize(height); + pallet_timestamp::Pallet::::on_finalize(height); + pallet_balances::Pallet::::on_finalize(height); + frame_system::Pallet::::finalize().hash() + } } /// Macro creating a minimal runtime with the given name. Optionally can take a chain extension /// type as a second argument. /// -/// The new macro will automatically implement `drink::SandboxConfig`. +/// The new macro will automatically implement `drink::Sandbox`. #[macro_export] -macro_rules! create_minimal_runtime { +macro_rules! create_minimal_sandbox { ($name:ident) => { - create_minimal_runtime!($name, ()); + $crate::paste::paste! { + $crate::create_minimal_sandbox!($name, [<$name Runtime>], ()); + } }; ($name:ident, $chain_extension: ty) => { + $crate::paste::paste! { + $crate::create_minimal_sandbox!($name, [<$name Runtime>], $chain_extension); + } + }; + ($sandbox:ident, $runtime:ident, $chain_extension: ty) => { + // ------------ Put all the boilerplate into an auxiliary module ----------------------------------- mod construct_runtime { @@ -110,7 +104,7 @@ mod construct_runtime { // ------------ Define the runtime type as a collection of pallets ----------------------------- construct_runtime!( - pub enum $name { + pub enum $runtime { System: $crate::frame_system, Balances: $crate::pallet_balances, Timestamp: $crate::pallet_timestamp, @@ -120,15 +114,15 @@ mod construct_runtime { // ------------ Configure pallet system -------------------------------------------------------- #[derive_impl($crate::frame_system::config_preludes::SolochainDefaultConfig as $crate::frame_system::DefaultConfig)] - impl $crate::frame_system::Config for $name { - type Block = $crate::frame_system::mocking::MockBlockU32<$name>; + impl $crate::frame_system::Config for $runtime { + type Block = $crate::frame_system::mocking::MockBlockU32<$runtime>; type Version = (); type BlockHashCount = ConstU32<250>; - type AccountData = $crate::pallet_balances::AccountData<<$name as $crate::pallet_balances::Config>::Balance>; + type AccountData = $crate::pallet_balances::AccountData<<$runtime as $crate::pallet_balances::Config>::Balance>; } // ------------ Configure pallet balances ------------------------------------------------------ - impl $crate::pallet_balances::Config for $name { + impl $crate::pallet_balances::Config for $runtime { type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type Balance = u128; @@ -145,7 +139,7 @@ mod construct_runtime { } // ------------ Configure pallet timestamp ----------------------------------------------------- - impl $crate::pallet_timestamp::Config for $name { + impl $crate::pallet_timestamp::Config for $runtime { type Moment = u64; type OnTimestampSet = (); type MinimumPeriod = ConstU64<1>; @@ -161,15 +155,15 @@ mod construct_runtime { } type BalanceOf = >::Balance; - impl Convert for $name { + impl Convert for $runtime { fn convert(w: Weight) -> BalanceOf { w.ref_time().into() } } parameter_types! { - pub SandboxSchedule: $crate::pallet_contracts::Schedule<$name> = { - <$crate::pallet_contracts::Schedule<$name>>::default() + pub SandboxSchedule: $crate::pallet_contracts::Schedule<$runtime> = { + <$crate::pallet_contracts::Schedule<$runtime>>::default() }; pub DeletionWeightLimit: Weight = Weight::zero(); pub DefaultDepositLimit: BalanceOf = 10_000_000; @@ -177,7 +171,7 @@ mod construct_runtime { pub MaxDelegateDependencies: u32 = 32; } - impl $crate::pallet_contracts::Config for $name { + impl $crate::pallet_contracts::Config for $runtime { type Time = Timestamp; type Randomness = SandboxRandomness; type Currency = Balances; @@ -211,18 +205,81 @@ mod construct_runtime { /// Default initial balance for the default account. pub const INITIAL_BALANCE: u128 = 1_000_000_000_000_000; - $crate::impl_sandbox_config!($name, $name, INITIAL_BALANCE, AccountId32::new([1u8; 32])); -} + pub const DEFAULT_ACCOUNT: AccountId32 = AccountId32::new([1u8; 32]); + pub struct $sandbox { + ext: $crate::TestExternalities, + } + + impl ::std::default::Default for $sandbox { + fn default() -> Self { + let ext = $crate::minimal::BlockBuilder::<$runtime>::new_ext(vec![( + DEFAULT_ACCOUNT, + INITIAL_BALANCE, + )]); + Self { ext } + } + } + + impl $crate::Sandbox for $sandbox { + type Runtime = $runtime; + + fn execute_with(&mut self, execute: impl FnOnce() -> T) -> T { + self.ext.execute_with(execute) + } + + fn dry_run(&mut self, action: impl FnOnce(&mut Self) -> T) -> T { + // Make a backup of the backend. + let backend_backup = self.ext.as_backend(); + // Run the action, potentially modifying storage. Ensure, that there are no pending changes + // that would affect the reverted backend. + let result = action(self); + self.ext.commit_all().expect("Failed to commit changes"); + + // Restore the backend. + self.ext.backend = backend_backup; + result + } + + fn register_extension(&mut self, ext: E) { + self.ext.register_extension(ext); + } + + fn initialize_block( + height: $crate::frame_system::pallet_prelude::BlockNumberFor, + parent_hash: ::Hash, + ) { + $crate::minimal::BlockBuilder::::initialize_block(height, parent_hash) + } + fn finalize_block( + height: $crate::frame_system::pallet_prelude::BlockNumberFor, + ) -> ::Hash { + $crate::minimal::BlockBuilder::::finalize_block(height) + } + + fn default_actor() -> $crate::runtime::AccountIdFor { + DEFAULT_ACCOUNT + } + fn get_metadata() -> $crate::runtime::RuntimeMetadataPrefixed { + Self::Runtime::metadata() + } + + fn convert_account_to_origin( + account: $crate::runtime::AccountIdFor, + ) -> <::RuntimeCall as $crate::frame_support::sp_runtime::traits::Dispatchable>::RuntimeOrigin { + Some(account).into() + } + } +} // ------------ Export runtime type itself, pallets and useful types from the auxiliary module ----- pub use construct_runtime::{ - $name, Balances, Contracts, PalletInfo, RuntimeCall, RuntimeEvent, RuntimeHoldReason, + $sandbox, $runtime, Balances, Contracts, PalletInfo, RuntimeCall, RuntimeEvent, RuntimeHoldReason, RuntimeOrigin, System, Timestamp, }; }; } -create_minimal_runtime!(MinimalRuntime); +create_minimal_sandbox!(MinimalSandbox); diff --git a/drink/src/sandbox.rs b/drink/src/sandbox.rs index b9f8b5b..9ee863c 100644 --- a/drink/src/sandbox.rs +++ b/drink/src/sandbox.rs @@ -1,56 +1,64 @@ -//! A sandboxed runtime. +//! Module containing the sandbox trait and its prelude. + +use core::any::Any; -mod sandbox_config; -pub use sandbox_config::SandboxConfig; pub mod balance_api; pub mod contracts_api; -pub mod runtime_api; pub mod system_api; pub mod timestamp_api; -use std::any::Any; +/// The prelude of the sandbox module. +pub mod prelude { + pub use super::{ + balance_api::BalanceAPI, contracts_api::ContractAPI, system_api::SystemAPI, + timestamp_api::TimestampAPI, Sandbox, + }; +} +use frame_metadata::RuntimeMetadataPrefixed; +use frame_support::sp_runtime::traits::Dispatchable; +use frame_system::pallet_prelude::BlockNumberFor; use sp_externalities::Extension; -use sp_io::TestExternalities; -/// A sandboxed runtime. -pub struct Sandbox { - externalities: TestExternalities, - _phantom: std::marker::PhantomData, -} +/// The type of an account identifier. +pub type AccountIdFor = ::AccountId; -impl Sandbox { - /// Execute the given closure with the inner externallities. - /// - /// Returns the result of the given closure. - pub fn execute_with(&mut self, execute: impl FnOnce() -> T) -> T { - self.externalities.execute_with(execute) - } +/// Sandbox defines the API of a sandboxed runtime. +pub trait Sandbox { + /// The runtime associated with the sandbox. + type Runtime: frame_system::Config; + + /// Execute the given externalities. + fn execute_with(&mut self, execute: impl FnOnce() -> T) -> T; - /// Run an action without modifying the storage. - /// - /// # Arguments - /// - /// * `action` - The action to run. - pub fn dry_run(&mut self, action: impl FnOnce(&mut Self) -> T) -> T { - // Make a backup of the backend. - let backend_backup = self.externalities.as_backend(); - - // Run the action, potentially modifying storage. Ensure, that there are no pending changes - // that would affect the reverted backend. - let result = action(self); - self.externalities - .commit_all() - .expect("Failed to commit changes"); - - // Restore the backend. - self.externalities.backend = backend_backup; - - result + /// Dry run an action without modifying the storage. + fn dry_run(&mut self, action: impl FnOnce(&mut Self) -> T) -> T; + + /// Register an extension. + fn register_extension(&mut self, ext: E); + + /// Initialize a new block at particular height. + fn initialize_block( + _height: BlockNumberFor, + _parent_hash: ::Hash, + ) { } - /// Registers an extension. - pub fn register_extension(&mut self, ext: E) { - self.externalities.register_extension(ext); + /// Finalize a block at particular height. + fn finalize_block( + _height: BlockNumberFor, + ) -> ::Hash { + Default::default() } + + /// Default actor for the sandbox. + fn default_actor() -> AccountIdFor; + + /// Metadata of the runtime. + fn get_metadata() -> RuntimeMetadataPrefixed; + + /// Convert an account to an call origin. + fn convert_account_to_origin( + account: AccountIdFor, + ) -> <::RuntimeCall as Dispatchable>::RuntimeOrigin; } diff --git a/drink/src/sandbox/balance_api.rs b/drink/src/sandbox/balance_api.rs index 0fb2296..8f68e55 100644 --- a/drink/src/sandbox/balance_api.rs +++ b/drink/src/sandbox/balance_api.rs @@ -2,13 +2,15 @@ use frame_support::{sp_runtime::DispatchError, traits::fungible::Mutate}; use super::Sandbox; -use crate::{runtime::AccountIdFor, SandboxConfig}; +use crate::runtime::AccountIdFor; type BalanceOf = ::Balance; -impl Sandbox +/// Balance API for the sandbox. +pub trait BalanceAPI where - Config::Runtime: pallet_balances::Config, + T: Sandbox, + T::Runtime: pallet_balances::Config, { /// Mint tokens to an account. /// @@ -16,25 +18,54 @@ where /// /// * `address` - The address of the account to add tokens to. /// * `amount` - The number of tokens to add. - pub fn mint_into( + fn mint_into( &mut self, - address: AccountIdFor, - amount: BalanceOf, - ) -> Result, DispatchError> { - self.execute_with(|| { - pallet_balances::Pallet::::mint_into(&address, amount) - }) - } + address: &AccountIdFor, + amount: BalanceOf, + ) -> Result, DispatchError>; /// Return the free balance of an account. /// /// # Arguments /// /// * `address` - The address of the account to query. - pub fn free_balance( + fn free_balance(&mut self, address: &AccountIdFor) -> BalanceOf; +} + +impl BalanceAPI for T +where + T: Sandbox, + T::Runtime: pallet_balances::Config, +{ + fn mint_into( &mut self, - address: &AccountIdFor, - ) -> BalanceOf { - self.execute_with(|| pallet_balances::Pallet::::free_balance(address)) + address: &AccountIdFor, + amount: BalanceOf, + ) -> Result, DispatchError> { + self.execute_with(|| pallet_balances::Pallet::::mint_into(address, amount)) + } + + fn free_balance(&mut self, address: &AccountIdFor) -> BalanceOf { + self.execute_with(|| pallet_balances::Pallet::::free_balance(address)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::MinimalSandbox; + #[test] + fn mint_works() { + let mut sandbox = MinimalSandbox::default(); + let balance = sandbox.free_balance(&MinimalSandbox::default_actor()); + + sandbox + .mint_into(&MinimalSandbox::default_actor(), 100) + .unwrap(); + + assert_eq!( + sandbox.free_balance(&MinimalSandbox::default_actor()), + balance + 100 + ); } } diff --git a/drink/src/sandbox/contracts_api.rs b/drink/src/sandbox/contracts_api.rs index bdad1b8..8746a4c 100644 --- a/drink/src/sandbox/contracts_api.rs +++ b/drink/src/sandbox/contracts_api.rs @@ -1,7 +1,7 @@ //! Contracts API for the sandbox. use std::ops::Not; -use frame_support::weights::Weight; +use frame_support::{traits::fungible::Inspect, weights::Weight}; use frame_system::Config as SysConfig; use pallet_contracts::{ Code, CodeUploadResult, CollectEvents, ContractInstantiateResult, DebugInfo, Determinism, @@ -9,14 +9,18 @@ use pallet_contracts::{ use parity_scale_codec::Decode as _; use crate::{ - runtime::AccountIdFor, BalanceOf, ContractExecResultFor, ContractInstantiateResultFor, - EventRecordOf, Sandbox, + runtime::AccountIdFor, ContractExecResultFor, ContractInstantiateResultFor, EventRecordOf, + Sandbox, }; -impl Sandbox -where - Config::Runtime: pallet_contracts::Config, -{ +type BalanceOf = + <::Currency as Inspect>>::Balance; + +/// Contract API used to interact with the contracts pallet. +pub trait ContractAPI { + /// The runtime contract config. + type T: pallet_contracts::Config; + /// Interface for `bare_instantiate` contract call with a simultaneous upload. /// /// # Arguments @@ -29,30 +33,16 @@ where /// * `gas_limit` - The gas limit for the contract call. /// * `storage_deposit_limit` - The storage deposit limit for the contract call. #[allow(clippy::type_complexity, clippy::too_many_arguments)] - pub fn deploy_contract( + fn deploy_contract( &mut self, contract_bytes: Vec, - value: BalanceOf, + value: BalanceOf, data: Vec, salt: Vec, - origin: AccountIdFor, + origin: AccountIdFor, gas_limit: Weight, - storage_deposit_limit: Option>, - ) -> ContractInstantiateResultFor { - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( - origin, - value, - gas_limit, - storage_deposit_limit, - Code::Upload(contract_bytes), - data, - salt, - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - ) - }) - } + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult, BalanceOf, EventRecordOf>; /// Interface for `bare_instantiate` contract call for a previously uploaded contract. /// @@ -66,29 +56,107 @@ where /// * `gas_limit` - The gas limit for the contract call. /// * `storage_deposit_limit` - The storage deposit limit for the contract call. #[allow(clippy::type_complexity, clippy::too_many_arguments)] - pub fn instantiate_contract( + fn instantiate_contract( + &mut self, + code_hash: Vec, + value: BalanceOf, + data: Vec, + salt: Vec, + origin: AccountIdFor, + gas_limit: Weight, + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult, BalanceOf, EventRecordOf>; + + /// Interface for `bare_upload_code` contract call. + /// + /// # Arguments + /// + /// * `contract_bytes` - The contract code. + /// * `origin` - The sender of the contract call. + /// * `storage_deposit_limit` - The storage deposit limit for the contract call. + fn upload_contract( + &mut self, + contract_bytes: Vec, + origin: AccountIdFor, + storage_deposit_limit: Option>, + determinism: Determinism, + ) -> CodeUploadResult<::Hash, BalanceOf>; + + /// Interface for `bare_call` contract call. + /// + /// # Arguments + /// + /// * `address` - The address of the contract to be called. + /// * `value` - The number of tokens to be transferred to the contract. + /// * `data` - The input data to be passed to the contract (including message name). + /// * `origin` - The sender of the contract call. + /// * `gas_limit` - The gas limit for the contract call. + /// * `storage_deposit_limit` - The storage deposit limit for the contract call. + #[allow(clippy::type_complexity, clippy::too_many_arguments)] + fn call_contract( + &mut self, + address: AccountIdFor, + value: BalanceOf, + data: Vec, + origin: AccountIdFor, + gas_limit: Weight, + storage_deposit_limit: Option>, + determinism: Determinism, + ) -> ContractExecResultFor; +} + +impl ContractAPI for T +where + T: Sandbox, + T::Runtime: pallet_contracts::Config, +{ + type T = T::Runtime; + + fn deploy_contract( + &mut self, + contract_bytes: Vec, + value: BalanceOf, + data: Vec, + salt: Vec, + origin: AccountIdFor, + gas_limit: Weight, + storage_deposit_limit: Option>, + ) -> ContractInstantiateResultFor { + self.execute_with(|| { + pallet_contracts::Pallet::::bare_instantiate( + origin, + value, + gas_limit, + storage_deposit_limit, + Code::Upload(contract_bytes), + data, + salt, + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + ) + }) + } + + fn instantiate_contract( &mut self, code_hash: Vec, - value: BalanceOf, + value: BalanceOf, data: Vec, salt: Vec, - origin: AccountIdFor, + origin: AccountIdFor, gas_limit: Weight, - storage_deposit_limit: Option>, - ) -> ContractInstantiateResult< - AccountIdFor, - BalanceOf, - EventRecordOf, - > { + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult, BalanceOf, EventRecordOf> + { let mut code_hash = &code_hash[..]; - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( + self.execute_with(|| { + pallet_contracts::Pallet::::bare_instantiate( origin, value, gas_limit, storage_deposit_limit, Code::Existing( - ::Hash::decode(&mut code_hash) + ::Hash::decode(&mut code_hash) .expect("Invalid code hash"), ), data, @@ -99,23 +167,15 @@ where }) } - /// Interface for `bare_upload_code` contract call. - /// - /// # Arguments - /// - /// * `contract_bytes` - The contract code. - /// * `origin` - The sender of the contract call. - /// * `storage_deposit_limit` - The storage deposit limit for the contract call. - pub fn upload_contract( + fn upload_contract( &mut self, contract_bytes: Vec, - origin: AccountIdFor, - storage_deposit_limit: Option>, + origin: AccountIdFor, + storage_deposit_limit: Option>, determinism: Determinism, - ) -> CodeUploadResult<::Hash, BalanceOf> - { - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_upload_code( + ) -> CodeUploadResult<::Hash, BalanceOf> { + self.execute_with(|| { + pallet_contracts::Pallet::::bare_upload_code( origin, contract_bytes, storage_deposit_limit, @@ -124,29 +184,18 @@ where }) } - /// Interface for `bare_call` contract call. - /// - /// # Arguments - /// - /// * `address` - The address of the contract to be called. - /// * `value` - The number of tokens to be transferred to the contract. - /// * `data` - The input data to be passed to the contract (including message name). - /// * `origin` - The sender of the contract call. - /// * `gas_limit` - The gas limit for the contract call. - /// * `storage_deposit_limit` - The storage deposit limit for the contract call. - #[allow(clippy::too_many_arguments)] - pub fn call_contract( + fn call_contract( &mut self, - address: AccountIdFor, - value: BalanceOf, + address: AccountIdFor, + value: BalanceOf, data: Vec, - origin: AccountIdFor, + origin: AccountIdFor, gas_limit: Weight, - storage_deposit_limit: Option>, + storage_deposit_limit: Option>, determinism: Determinism, - ) -> ContractExecResultFor { - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_call( + ) -> ContractExecResultFor { + self.execute_with(|| { + pallet_contracts::Pallet::::bare_call( origin, address, value, @@ -177,7 +226,9 @@ mod tests { use super::*; use crate::{ - minimal::RuntimeEvent, sandbox::SandboxConfig, session::NO_SALT, MinimalRuntime, + minimal::{MinimalSandbox, MinimalSandboxRuntime, RuntimeEvent}, + sandbox::prelude::*, + session::NO_SALT, DEFAULT_GAS_LIMIT, }; @@ -196,13 +247,13 @@ mod tests { #[test] fn can_upload_code() { - let mut sandbox = Sandbox::::new().unwrap(); + let mut sandbox = MinimalSandbox::default(); let wasm_binary = compile_module("dummy"); - let hash = <::Hashing>::hash(&wasm_binary); + let hash = <::Hashing>::hash(&wasm_binary); let result = sandbox.upload_contract( wasm_binary, - MinimalRuntime::default_actor(), + MinimalSandbox::default_actor(), None, Determinism::Enforced, ); @@ -213,7 +264,7 @@ mod tests { #[test] fn can_deploy_contract() { - let mut sandbox = Sandbox::::new().unwrap(); + let mut sandbox = MinimalSandbox::default(); let wasm_binary = compile_module("dummy"); let events_before = sandbox.events(); @@ -224,7 +275,7 @@ mod tests { 0, vec![], NO_SALT, - MinimalRuntime::default_actor(), + MinimalSandbox::default_actor(), DEFAULT_GAS_LIMIT, None, ); @@ -236,21 +287,23 @@ mod tests { let instantiation_event = events[event_count - 2].clone(); assert!(matches!( instantiation_event.event, - RuntimeEvent::Contracts(pallet_contracts::Event::::Instantiated { .. }) + RuntimeEvent::Contracts( + pallet_contracts::Event::::Instantiated { .. } + ) )); let deposit_event = events[event_count - 1].clone(); assert!(matches!( deposit_event.event, RuntimeEvent::Contracts( - pallet_contracts::Event::::StorageDepositTransferredAndHeld { .. } + pallet_contracts::Event::::StorageDepositTransferredAndHeld { .. } ) )); } #[test] fn can_call_contract() { - let mut sandbox = Sandbox::::new().unwrap(); - let actor = MinimalRuntime::default_actor(); + let mut sandbox = MinimalSandbox::default(); + let actor = MinimalSandbox::default_actor(); let wasm_binary = compile_module("dummy"); let result = sandbox.deploy_contract( @@ -287,15 +340,17 @@ mod tests { assert_eq!( events[0].event, - RuntimeEvent::Contracts(pallet_contracts::Event::::ContractEmitted { - contract: contract_address.clone(), - data: vec![0, 0, 0, 0], - }) + RuntimeEvent::Contracts( + pallet_contracts::Event::::ContractEmitted { + contract: contract_address.clone(), + data: vec![0, 0, 0, 0], + } + ) ); assert_eq!( events[1].event, - RuntimeEvent::Contracts(pallet_contracts::Event::::Called { + RuntimeEvent::Contracts(pallet_contracts::Event::::Called { contract: contract_address, caller: Origin::Signed(actor), }), diff --git a/drink/src/sandbox/system_api.rs b/drink/src/sandbox/system_api.rs index 8e29e67..e43df60 100644 --- a/drink/src/sandbox/system_api.rs +++ b/drink/src/sandbox/system_api.rs @@ -1,26 +1,37 @@ //! System API for the sandbox. -use frame_support::sp_runtime::{traits::Dispatchable, DispatchResultWithInfo}; +use frame_support::sp_runtime::{ + traits::{Dispatchable, Saturating}, + DispatchResultWithInfo, +}; use frame_system::pallet_prelude::BlockNumberFor; use super::Sandbox; -use crate::{EventRecordOf, RuntimeCall, SandboxConfig}; +use crate::{EventRecordOf, RuntimeCall}; + +/// System API for the sandbox. +pub trait SystemAPI { + /// The runtime system config. + type T: frame_system::Config; + + /// Build a new empty block and return the new height. + fn build_block(&mut self) -> BlockNumberFor; + + /// Build `n` empty blocks and return the new height. + /// + /// # Arguments + /// + /// * `n` - The number of blocks to build. + fn build_blocks(&mut self, n: u32) -> BlockNumberFor; -impl Sandbox { /// Return the current height of the chain. - pub fn block_number(&mut self) -> BlockNumberFor { - self.execute_with(frame_system::Pallet::::block_number) - } + fn block_number(&mut self) -> BlockNumberFor; /// Return the events of the current block so far. - pub fn events(&mut self) -> Vec> { - self.execute_with(frame_system::Pallet::::events) - } + fn events(&mut self) -> Vec>; /// Reset the events of the current block. - pub fn reset_events(&mut self) { - self.execute_with(frame_system::Pallet::::reset_events) - } + fn reset_events(&mut self); /// Execute a runtime call (dispatchable). /// @@ -28,13 +39,55 @@ impl Sandbox { /// /// * `call` - The runtime call to execute. /// * `origin` - The origin of the call. - pub fn runtime_call< - Origin: Into< as Dispatchable>::RuntimeOrigin>, - >( + fn runtime_call as Dispatchable>::RuntimeOrigin>>( + &mut self, + call: RuntimeCall, + origin: Origin, + ) -> DispatchResultWithInfo< as Dispatchable>::PostInfo>; +} + +impl SystemAPI for T +where + T: Sandbox, + T::Runtime: frame_system::Config, +{ + type T = T::Runtime; + + fn build_block(&mut self) -> BlockNumberFor { + self.execute_with(|| { + let mut current_block = frame_system::Pallet::::block_number(); + let block_hash = T::finalize_block(current_block); + current_block.saturating_inc(); + T::initialize_block(current_block, block_hash); + current_block + }) + } + + fn build_blocks(&mut self, n: u32) -> BlockNumberFor { + let mut last_block = None; + for _ in 0..n { + last_block = Some(self.build_block()); + } + last_block.unwrap_or_else(|| self.block_number()) + } + + fn block_number(&mut self) -> BlockNumberFor { + self.execute_with(frame_system::Pallet::::block_number) + } + + fn events(&mut self) -> Vec> { + self.execute_with(frame_system::Pallet::::events) + } + + fn reset_events(&mut self) { + self.execute_with(frame_system::Pallet::::reset_events) + } + + fn runtime_call as Dispatchable>::RuntimeOrigin>>( &mut self, - call: RuntimeCall, + call: RuntimeCall, origin: Origin, - ) -> DispatchResultWithInfo< as Dispatchable>::PostInfo> { + ) -> DispatchResultWithInfo< as Dispatchable>::PostInfo> { self.execute_with(|| call.dispatch(origin.into())) } } @@ -44,40 +97,43 @@ mod tests { use frame_support::sp_runtime::{traits::Dispatchable, DispatchResultWithInfo}; use crate::{ - runtime::{minimal::RuntimeEvent, MinimalRuntime}, - AccountId32, RuntimeCall, Sandbox, SandboxConfig, + minimal::{MinimalSandbox, MinimalSandboxRuntime}, + runtime::minimal::RuntimeEvent, + sandbox::prelude::*, + AccountId32, RuntimeCall, Sandbox, }; fn make_transfer( - sandbox: &mut Sandbox, + sandbox: &mut MinimalSandbox, dest: AccountId32, value: u128, - ) -> DispatchResultWithInfo< as Dispatchable>::PostInfo> { + ) -> DispatchResultWithInfo< as Dispatchable>::PostInfo> + { assert_ne!( - MinimalRuntime::default_actor(), + MinimalSandbox::default_actor(), dest, "make_transfer should send to account different than default_actor" ); sandbox.runtime_call( - RuntimeCall::::Balances( - pallet_balances::Call::::transfer_allow_death { - dest: dest.into(), - value, - }, - ), - Some(MinimalRuntime::default_actor()), + RuntimeCall::::Balances(pallet_balances::Call::< + MinimalSandboxRuntime, + >::transfer_allow_death { + dest: dest.into(), + value, + }), + Some(MinimalSandbox::default_actor()), ) } #[test] fn dry_run_works() { - let mut sandbox = Sandbox::::new().expect("Failed to create sandbox"); - let actor = MinimalRuntime::default_actor(); + let mut sandbox = MinimalSandbox::default(); + let actor = MinimalSandbox::default_actor(); let initial_balance = sandbox.free_balance(&actor); - sandbox.dry_run(|runtime| { - runtime.mint_into(actor.clone(), 100).unwrap(); - assert_eq!(runtime.free_balance(&actor), initial_balance + 100); + sandbox.dry_run(|sandbox| { + sandbox.mint_into(&actor, 100).unwrap(); + assert_eq!(sandbox.free_balance(&actor), initial_balance + 100); }); assert_eq!(sandbox.free_balance(&actor), initial_balance); @@ -85,7 +141,7 @@ mod tests { #[test] fn runtime_call_works() { - let mut sandbox = Sandbox::::new().expect("Failed to create sandbox"); + let mut sandbox = MinimalSandbox::default(); const RECIPIENT: AccountId32 = AccountId32::new([2u8; 32]); let initial_balance = sandbox.free_balance(&RECIPIENT); @@ -99,7 +155,7 @@ mod tests { #[test] fn current_events() { - let mut sandbox = Sandbox::::new().expect("Failed to create sandbox"); + let mut sandbox = MinimalSandbox::default(); const RECIPIENT: AccountId32 = AccountId32::new([2u8; 32]); let events_before = sandbox.events(); @@ -117,7 +173,7 @@ mod tests { #[test] fn resetting_events() { - let mut sandbox = Sandbox::::new().expect("Failed to create sandbox"); + let mut sandbox = MinimalSandbox::default(); const RECIPIENT: AccountId32 = AccountId32::new([3u8; 32]); make_transfer(&mut sandbox, RECIPIENT.clone(), 1).expect("Failed to make transfer"); diff --git a/drink/src/sandbox/timestamp_api.rs b/drink/src/sandbox/timestamp_api.rs index 8c34a4a..c3347cd 100644 --- a/drink/src/sandbox/timestamp_api.rs +++ b/drink/src/sandbox/timestamp_api.rs @@ -1,42 +1,55 @@ //! timestamp API for the sandbox. -use crate::{Sandbox, SandboxConfig}; +use crate::Sandbox; /// Generic Time type. type MomentOf = ::Moment; -impl Sandbox -where - Config::Runtime: pallet_timestamp::Config, -{ +/// Timestamp API used to interact with the timestamp pallet. +pub trait TimestampAPI { + /// The runtime timestamp config. + type T: pallet_timestamp::Config; + /// Return the timestamp of the current block. - pub fn get_timestamp(&mut self) -> MomentOf { - self.execute_with(pallet_timestamp::Pallet::::get) - } + fn get_timestamp(&mut self) -> MomentOf; /// Set the timestamp of the current block. /// /// # Arguments /// /// * `timestamp` - The new timestamp to be set. - pub fn set_timestamp(&mut self, timestamp: MomentOf) { - self.execute_with(|| pallet_timestamp::Pallet::::set_timestamp(timestamp)) + fn set_timestamp(&mut self, timestamp: MomentOf); +} + +impl TimestampAPI for T +where + T: Sandbox, + T::Runtime: pallet_timestamp::Config, +{ + type T = T::Runtime; + + fn get_timestamp(&mut self) -> MomentOf { + self.execute_with(pallet_timestamp::Pallet::::get) + } + + fn set_timestamp(&mut self, timestamp: MomentOf) { + self.execute_with(|| pallet_timestamp::Pallet::::set_timestamp(timestamp)) } } #[cfg(test)] mod tests { - use crate::{runtime::MinimalRuntime, Sandbox}; + use crate::{runtime::MinimalSandbox, sandbox::prelude::*}; #[test] fn getting_and_setting_timestamp_works() { - let mut sandbox = Sandbox::::new().expect("Failed to create sandbox"); + let mut sandbox = MinimalSandbox::default(); for timestamp in 0..10 { assert_ne!(sandbox.get_timestamp(), timestamp); sandbox.set_timestamp(timestamp); assert_eq!(sandbox.get_timestamp(), timestamp); - sandbox.build_block().expect("Failed to build block"); + sandbox.build_block(); } } } diff --git a/drink/src/session.rs b/drink/src/session.rs index ac33641..7b9e740 100644 --- a/drink/src/session.rs +++ b/drink/src/session.rs @@ -15,13 +15,12 @@ use pallet_contracts::Determinism; use parity_scale_codec::Decode; pub use record::{EventBatch, Record}; -use self::mocking_api::MockingApi; use crate::{ runtime::{ pallet_contracts_debugging::{InterceptingExt, TracingExt}, - AccountIdFor, HashFor, MinimalRuntime, + AccountIdFor, HashFor, }, - sandbox::SandboxConfig, + sandbox::prelude::*, session::mock::MockRegistry, ContractExecResultFor, ContractInstantiateResultFor, Sandbox, DEFAULT_GAS_LIMIT, }; @@ -36,7 +35,10 @@ mod transcoding; pub use bundle::ContractBundle; -use crate::{errors::MessageResult, session::transcoding::TranscoderRegistry}; +use self::mocking_api::MockingApi; +use crate::{ + errors::MessageResult, minimal::MinimalSandboxRuntime, session::transcoding::TranscoderRegistry, +}; type BalanceOf = <::Currency as Inspect>>::Balance; @@ -51,7 +53,7 @@ pub const NO_SALT: Vec = vec![]; /// Convenient value for no endowment. /// /// Compatible with any runtime with `u128` as the balance type. -pub const NO_ENDOWMENT: Option> = None; +pub const NO_ENDOWMENT: Option> = None; /// Wrapper around `Sandbox` that provides a convenient API for interacting with multiple contracts. /// @@ -66,7 +68,7 @@ pub const NO_ENDOWMENT: Option> = None; /// # session::Session, /// # AccountId32, /// # session::{NO_ARGS, NO_SALT, NO_ENDOWMENT}, -/// # runtime::MinimalRuntime +/// # runtime::MinimalSandbox /// # }; /// # /// # fn get_transcoder() -> Rc { @@ -77,7 +79,7 @@ pub const NO_ENDOWMENT: Option> = None; /// /// # fn main() -> Result<(), drink::session::error::SessionError> { /// -/// Session::::new()? +/// Session::::default() /// .deploy_and(contract_bytes(), "new", NO_ARGS, NO_SALT, NO_ENDOWMENT, &get_transcoder())? /// .call_and("foo", NO_ARGS, NO_ENDOWMENT)? /// .with_actor(bob()) @@ -92,7 +94,7 @@ pub const NO_ENDOWMENT: Option> = None; /// # use drink::{ /// # session::Session, /// # AccountId32, -/// # runtime::MinimalRuntime, +/// # runtime::MinimalSandbox, /// # session::{NO_ARGS, NO_ENDOWMENT, NO_SALT} /// # }; /// # fn get_transcoder() -> Rc { @@ -103,7 +105,7 @@ pub const NO_ENDOWMENT: Option> = None; /// /// # fn main() -> Result<(), Box> { /// -/// let mut session = Session::::new()?; +/// let mut session = Session::::default(); /// let _address = session.deploy(contract_bytes(), "new", NO_ARGS, NO_SALT, NO_ENDOWMENT, &get_transcoder())?; /// let _result: u32 = session.call("foo", NO_ARGS, NO_ENDOWMENT)??; /// session.set_actor(bob()); @@ -117,73 +119,75 @@ pub const NO_ENDOWMENT: Option> = None; /// # local_contract_file, /// # session::Session, /// # session::{ContractBundle, NO_ARGS, NO_SALT, NO_ENDOWMENT}, -/// # runtime::MinimalRuntime, +/// # runtime::MinimalSandbox, /// # }; /// /// # fn main() -> Result<(), drink::session::error::SessionError> { /// // Simplest way, loading a bundle from the project's directory: -/// Session::::new()? +/// Session::::default() /// .deploy_bundle_and(local_contract_file!(), "new", NO_ARGS, NO_SALT, NO_ENDOWMENT)?; /* ... */ /// /// // Or choosing the file explicitly: /// let contract = ContractBundle::load("path/to/your.contract")?; -/// Session::::new()? +/// Session::::default() /// .deploy_bundle_and(contract, "new", NO_ARGS, NO_SALT, NO_ENDOWMENT)?; /* ... */ /// # Ok(()) } /// ``` -pub struct Session +pub struct Session where - Config::Runtime: pallet_contracts::Config, + T::Runtime: pallet_contracts::Config, { - sandbox: Sandbox, + sandbox: T, - actor: AccountIdFor, + actor: AccountIdFor, gas_limit: Weight, determinism: Determinism, - transcoders: TranscoderRegistry>, - record: Record, - mocks: Arc>>>, + transcoders: TranscoderRegistry>, + record: Record, + mocks: Arc>>>, } -impl Session +impl Default for Session where - Config::Runtime: pallet_contracts::Config, + T::Runtime: pallet_contracts::Config, + T: Default, { - /// Creates a new `Session`. - pub fn new() -> Result { + fn default() -> Self { let mocks = Arc::new(Mutex::new(MockRegistry::new())); - let mut sandbox = Sandbox::new().map_err(SessionError::Drink)?; + let mut sandbox = T::default(); sandbox.register_extension(InterceptingExt(Box::new(MockingExtension { mock_registry: Arc::clone(&mocks), }))); - Ok(Self { + Self { sandbox, mocks, - actor: Config::default_actor(), + actor: T::default_actor(), gas_limit: DEFAULT_GAS_LIMIT, determinism: Determinism::Enforced, transcoders: TranscoderRegistry::new(), record: Default::default(), - }) + } } +} +impl Session +where + T::Runtime: pallet_contracts::Config, +{ /// Sets a new actor and returns updated `self`. - pub fn with_actor(self, actor: AccountIdFor) -> Self { + pub fn with_actor(self, actor: AccountIdFor) -> Self { Self { actor, ..self } } /// Returns currently set actor. - pub fn get_actor(&self) -> AccountIdFor { + pub fn get_actor(&self) -> AccountIdFor { self.actor.clone() } /// Sets a new actor and returns the old one. - pub fn set_actor( - &mut self, - actor: AccountIdFor, - ) -> AccountIdFor { + pub fn set_actor(&mut self, actor: AccountIdFor) -> AccountIdFor { mem::replace(&mut self.actor, actor) } @@ -218,7 +222,7 @@ where /// Register a transcoder for a particular contract and returns updated `self`. pub fn with_transcoder( mut self, - contract_address: AccountIdFor, + contract_address: AccountIdFor, transcoder: &Rc, ) -> Self { self.set_transcoder(contract_address, transcoder); @@ -228,24 +232,24 @@ where /// Registers a transcoder for a particular contract. pub fn set_transcoder( &mut self, - contract_address: AccountIdFor, + contract_address: AccountIdFor, transcoder: &Rc, ) { self.transcoders.register(contract_address, transcoder); } /// The underlying `Sandbox` instance. - pub fn sandbox(&mut self) -> &mut Sandbox { + pub fn sandbox(&mut self) -> &mut T { &mut self.sandbox } /// Returns a reference to the record of the session. - pub fn record(&self) -> &Record { + pub fn record(&self) -> &Record { &self.record } /// Returns a reference for mocking API. - pub fn mocking_api(&mut self) -> &mut impl MockingApi { + pub fn mocking_api(&mut self) -> &mut impl MockingApi { self } @@ -257,7 +261,7 @@ where constructor: &str, args: &[S], salt: Vec, - endowment: Option>, + endowment: Option>, transcoder: &Rc, ) -> Result { self.deploy( @@ -270,8 +274,7 @@ where ) .map(|_| self) } - - fn record_events(&mut self, recording: impl FnOnce(&mut Self) -> T) -> T { + fn record_events(&mut self, recording: impl FnOnce(&mut Self) -> V) -> V { let start = self.sandbox.events().len(); let result = recording(self); let events = self.sandbox.events()[start..].to_vec(); @@ -287,9 +290,9 @@ where constructor: &str, args: &[S], salt: Vec, - endowment: Option>, + endowment: Option>, transcoder: &Rc, - ) -> Result, SessionError> { + ) -> Result, SessionError> { let data = transcoder .encode(constructor, args) .map_err(|err| SessionError::Encoding(err.to_string()))?; @@ -333,8 +336,8 @@ where constructor: &str, args: &[S], salt: Vec, - endowment: Option>, - ) -> Result, SessionError> { + endowment: Option>, + ) -> Result, SessionError> { self.deploy( contract_file.wasm, constructor, @@ -352,8 +355,8 @@ where constructor: &str, args: &[S], salt: Vec, - endowment: Option>, - ) -> Result, SessionError> { + endowment: Option>, + ) -> Result, SessionError> { let data = contract_file .transcoder .encode(constructor, args) @@ -381,7 +384,7 @@ where constructor: &str, args: &[S], salt: Vec, - endowment: Option>, + endowment: Option>, ) -> Result { self.deploy_bundle(contract_file, constructor, args, salt, endowment) .map(|_| self) @@ -393,10 +396,7 @@ where } /// Uploads a raw contract code. In case of success returns the code hash. - pub fn upload( - &mut self, - contract_bytes: Vec, - ) -> Result, SessionError> { + pub fn upload(&mut self, contract_bytes: Vec) -> Result, SessionError> { let result = self.sandbox.upload_contract( contract_bytes, self.actor.clone(), @@ -422,7 +422,7 @@ where pub fn upload_bundle( &mut self, contract_file: ContractBundle, - ) -> Result, SessionError> { + ) -> Result, SessionError> { self.upload(contract_file.wasm) } @@ -431,7 +431,7 @@ where mut self, message: &str, args: &[S], - endowment: Option>, + endowment: Option>, ) -> Result { // We ignore result, so we can pass `()` as the message result type, which will never fail // at decoding. @@ -442,10 +442,10 @@ where /// Calls the last deployed contract. In case of a successful call, returns `self`. pub fn call_with_address_and + Debug>( mut self, - address: AccountIdFor, + address: AccountIdFor, message: &str, args: &[S], - endowment: Option>, + endowment: Option>, ) -> Result { // We ignore result, so we can pass `()` as the message result type, which will never fail // at decoding. @@ -454,35 +454,35 @@ where } /// Calls the last deployed contract. In case of a successful call, returns the encoded result. - pub fn call + Debug, T: Decode>( + pub fn call + Debug, V: Decode>( &mut self, message: &str, args: &[S], - endowment: Option>, - ) -> Result, SessionError> { - self.call_internal::<_, T>(None, message, args, endowment) + endowment: Option>, + ) -> Result, SessionError> { + self.call_internal::<_, V>(None, message, args, endowment) } /// Calls a contract with a given address. In case of a successful call, returns the encoded /// result. - pub fn call_with_address + Debug, T: Decode>( + pub fn call_with_address + Debug, V: Decode>( &mut self, - address: AccountIdFor, + address: AccountIdFor, message: &str, args: &[S], - endowment: Option>, - ) -> Result, SessionError> { + endowment: Option>, + ) -> Result, SessionError> { self.call_internal(Some(address), message, args, endowment) } /// Performs a dry run of a contract call. pub fn dry_run_call + Debug>( &mut self, - address: AccountIdFor, + address: AccountIdFor, message: &str, args: &[S], - endowment: Option>, - ) -> Result, SessionError> { + endowment: Option>, + ) -> Result, SessionError> { let data = self .transcoders .get(&address) @@ -504,13 +504,13 @@ where })) } - fn call_internal + Debug, T: Decode>( + fn call_internal + Debug, V: Decode>( &mut self, - address: Option>, + address: Option>, message: &str, args: &[S], - endowment: Option>, - ) -> Result, SessionError> { + endowment: Option>, + ) -> Result, SessionError> { let address = match address { Some(address) => address, None => self @@ -545,7 +545,7 @@ where Ok(exec_result) if exec_result.did_revert() => Err(SessionError::CallReverted), Ok(exec_result) => { self.record.push_call_return(exec_result.data.clone()); - self.record.last_call_return_decoded::() + self.record.last_call_return_decoded::() } Err(err) => Err(SessionError::CallFailed(*err)), }; diff --git a/drink/src/session/mocking_api.rs b/drink/src/session/mocking_api.rs index 273f749..538141a 100644 --- a/drink/src/session/mocking_api.rs +++ b/drink/src/session/mocking_api.rs @@ -1,6 +1,8 @@ //! Mocking API for the sandbox. use super::Session; -use crate::{runtime::AccountIdFor, session::mock::ContractMock, SandboxConfig, DEFAULT_GAS_LIMIT}; +use crate::{ + runtime::AccountIdFor, sandbox::prelude::*, session::mock::ContractMock, DEFAULT_GAS_LIMIT, +}; /// Interface for basic mocking operations. pub trait MockingApi { @@ -12,11 +14,11 @@ pub trait MockingApi { fn mock_existing_contract(&mut self, _mock: ContractMock, _address: AccountIdFor); } -impl MockingApi for Session +impl MockingApi for Session where - Config::Runtime: pallet_contracts::Config, + T::Runtime: pallet_contracts::Config, { - fn deploy(&mut self, mock: ContractMock) -> AccountIdFor { + fn deploy(&mut self, mock: ContractMock) -> AccountIdFor { // We have to deploy some contract. We use a dummy contract for that. Thanks to that, we // ensure that the pallet will treat our mock just as a regular contract, until we actually // call it. @@ -34,7 +36,7 @@ where 0u32.into(), vec![], salt, - Config::default_actor(), + T::default_actor(), DEFAULT_GAS_LIMIT, None, ) @@ -50,11 +52,7 @@ where mock_address } - fn mock_existing_contract( - &mut self, - _mock: ContractMock, - _address: AccountIdFor, - ) { + fn mock_existing_contract(&mut self, _mock: ContractMock, _address: AccountIdFor) { todo!("soon") } } diff --git a/drink/src/session/record.rs b/drink/src/session/record.rs index e5ad2c3..12af7a4 100644 --- a/drink/src/session/record.rs +++ b/drink/src/session/record.rs @@ -5,7 +5,8 @@ use parity_scale_codec::{Decode, Encode}; use crate::{ errors::MessageResult, - runtime::{minimal::RuntimeEvent, AccountIdFor, MinimalRuntime}, + minimal::MinimalSandboxRuntime, + runtime::{minimal::RuntimeEvent, AccountIdFor}, session::{error::SessionError, BalanceOf}, EventRecordOf, }; @@ -143,20 +144,22 @@ impl EventBatch { } } -impl EventBatch { +impl EventBatch { /// Returns all the contract events that were emitted during the contract interaction. /// /// **WARNING**: This method will return all the events that were emitted by ANY contract. If your /// call triggered multiple contracts, you will have to filter the events yourself. /// /// We have to match against static enum variant, and thus (at least for now) we support only - /// `MinimalRuntime`. + /// `MinimalSandbox`. pub fn contract_events(&self) -> Vec<&[u8]> { self.events .iter() .filter_map(|event| match &event.event { RuntimeEvent::Contracts( - pallet_contracts::Event::::ContractEmitted { data, .. }, + pallet_contracts::Event::::ContractEmitted { + data, .. + }, ) => Some(data.as_slice()), _ => None, }) diff --git a/drink/test-macro/src/lib.rs b/drink/test-macro/src/lib.rs index a61c7f8..20704c3 100644 --- a/drink/test-macro/src/lib.rs +++ b/drink/test-macro/src/lib.rs @@ -45,16 +45,16 @@ type SynResult = Result; /// /// The macro will also create a new mutable session object and pass it to the decorated function by value. You can /// configure which sandbox should be used (by specifying a path to a type implementing -/// `drink::runtime::SandboxConfig` trait. Thus, your testcase function should accept a single argument: +/// `drink::runtime::Sandbox` trait. Thus, your testcase function should accept a single argument: /// `mut session: Session<_>`. /// -/// By default, the macro will use `drink::runtime::MinimalRuntime`. +/// By default, the macro will use `drink::runtime::MinimalSandbox`. /// /// # Example /// /// ```rust, ignore /// #[drink::test] -/// fn testcase(mut session: Session) { +/// fn testcase(mut session: Session) { /// session /// .deploy_bundle(&get_bundle(), "new", NO_ARGS, NO_SALT, NO_ENDOWMENT) /// .unwrap(); @@ -70,7 +70,7 @@ pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream { #[derive(FromMeta)] struct TestAttributes { - config: Option, + sandbox: Option, } /// Auxiliary function to enter ?-based error propagation. @@ -90,15 +90,15 @@ fn test_internal(attr: TokenStream2, item: TokenStream2) -> SynResult::new().expect("Failed to create a session"); + let mut session = Session::<#sandbox>::default(); #fn_block } }) @@ -136,7 +136,7 @@ fn test_internal(attr: TokenStream2, item: TokenStream2) -> SynResult::new()? +/// Session::::default() /// .deploy_bundle_and(BundleProvider::local()?, "new", NO_ARGS, NO_SALT, NO_ENDOWMENT) /// .deploy_bundle_and(BundleProvider::AnotherContract.bundle()?, "new", NO_ARGS, NO_SALT, NO_ENDOWMENT) /// .unwrap(); diff --git a/examples/chain-extension/Cargo.lock b/examples/chain-extension/Cargo.lock index cdfb95f..2398713 100644 --- a/examples/chain-extension/Cargo.lock +++ b/examples/chain-extension/Cargo.lock @@ -1344,7 +1344,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.12.1" +version = "0.13.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1358,6 +1358,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", @@ -1369,7 +1370,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.12.1" +version = "0.13.0" dependencies = [ "cargo_metadata", "contract-build", diff --git a/examples/chain-extension/Cargo.toml b/examples/chain-extension/Cargo.toml index e1b26c8..9f43b19 100644 --- a/examples/chain-extension/Cargo.toml +++ b/examples/chain-extension/Cargo.toml @@ -18,7 +18,7 @@ scale-info = { version = "2.6", default-features = false, features = ["derive"], [dev-dependencies] drink = { path = "../../drink" } -# If you are creating a custom runtime with `drink::create_minimal_runtime!` macro, unfortunately you need to +# If you are creating a custom runtime with `drink::create_minimal_sandbox!` macro, unfortunately you need to # include these dependencies manually. Versions should match the ones in `Cargo.toml` of the `drink` crate. frame-support = "30.0.0" frame-system = "30.0.0" diff --git a/examples/chain-extension/README.md b/examples/chain-extension/README.md index 47303de..9d64110 100644 --- a/examples/chain-extension/README.md +++ b/examples/chain-extension/README.md @@ -3,18 +3,20 @@ This example shows how you can use `drink` to test a chain extension. From the perspective of a contract implementation or writing tests there is nothing special (i.e., more than usual) that you have to do to interact with a chain extension. -The thing that `drink` makes easier for you is combining an arbitrary chain extension with `drink`'s `MinimalRuntime`. +The thing that `drink` makes easier for you is combining an arbitrary chain extension with `drink`'s `MinimalSandbox`. By simply calling: + ```rust -create_minimal_runtime!( - RuntimeWithCustomChainExtension, +create_minimal_sandbox!( + SandboxWithCustomChainExtension, path::to::MyCustomChainExtension ); ``` -you are provided with a runtime that contains your custom chain extension and can be used to test your contract like: +you are provided with a `Sandbox` with a runtime that contains your custom chain extension and can be used to test your contract like: + ```rust -Session::::new()? +Session::::default() .deploy_bundle_and(...)? .call(...)? ``` diff --git a/examples/chain-extension/src/lib.rs b/examples/chain-extension/src/lib.rs index f527043..7c2f1ee 100644 --- a/examples/chain-extension/src/lib.rs +++ b/examples/chain-extension/src/lib.rs @@ -36,7 +36,7 @@ mod contract_calling_chain_extension { #[cfg(test)] mod tests { use drink::{ - create_minimal_runtime, + create_minimal_sandbox, session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT}, }; @@ -46,13 +46,13 @@ mod tests { enum BundleProvider {} // We can inject arbitrary chain extension into the minimal runtime as follows: - create_minimal_runtime!( + create_minimal_sandbox!( SandboxWithCE, crate::chain_extension_runtime_side::StakingExtension ); /// Test that we can call chain extension from ink! contract and get a correct result. - #[drink::test(config = SandboxWithCE)] + #[drink::test(sandbox = SandboxWithCE)] fn we_can_test_chain_extension(mut session: Session) -> Result<(), Box> { let result: u32 = session .deploy_bundle_and( diff --git a/examples/contract-events/Cargo.lock b/examples/contract-events/Cargo.lock index 56c207a..44961b7 100644 --- a/examples/contract-events/Cargo.lock +++ b/examples/contract-events/Cargo.lock @@ -1338,7 +1338,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.12.0" +version = "0.13.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1352,6 +1352,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", @@ -1363,7 +1364,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.12.0" +version = "0.13.0" dependencies = [ "cargo_metadata", "contract-build", diff --git a/examples/contract-events/README.md b/examples/contract-events/README.md index a394780..d9d84af 100644 --- a/examples/contract-events/README.md +++ b/examples/contract-events/README.md @@ -4,8 +4,9 @@ This example shows how we can extract events that were emitted by a contract. When you are working with a `Session` object, you can consult its `Record` - a data structure that collects all the results and events that have been produced while interacting with contracts. For example: + ```rust -let mut session = Session::::new()?; +let mut session = Session::::default(); // .. some contract interaction // `record` is a `Record` object that contains all the results and events that have been produced while interacting with contracts. @@ -13,6 +14,7 @@ let record = session.record(); ``` Given a `Record` object, we can extract the results of the contract interaction: + ```rust // `deploy_returns` returns a vector of contract addresses that have been deployed during the session. let all_deployed_contracts = record.deploy_returns(); @@ -21,6 +23,7 @@ let last_call_value = record.last_call_return_decoded::(); ``` as well as the events that have been emitted by contracts: + ```rust // `last_event_batch` returns the batch of runtime events that have been emitted during last contract interaction. let last_event_batch = record.last_event_batch(); diff --git a/examples/cross-contract-call-tracing/Cargo.lock b/examples/cross-contract-call-tracing/Cargo.lock index d649044..ddd2667 100644 --- a/examples/cross-contract-call-tracing/Cargo.lock +++ b/examples/cross-contract-call-tracing/Cargo.lock @@ -1338,7 +1338,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.11.2" +version = "0.13.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1352,6 +1352,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", @@ -1363,7 +1364,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.11.2" +version = "0.13.0" dependencies = [ "cargo_metadata", "contract-build", diff --git a/examples/dry-running/Cargo.lock b/examples/dry-running/Cargo.lock index 2171355..4aa80ef 100644 --- a/examples/dry-running/Cargo.lock +++ b/examples/dry-running/Cargo.lock @@ -1324,7 +1324,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.11.2" +version = "0.13.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1338,6 +1338,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", @@ -1349,7 +1350,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.11.2" +version = "0.13.0" dependencies = [ "cargo_metadata", "contract-build", diff --git a/examples/dry-running/lib.rs b/examples/dry-running/lib.rs index 145289a..5d5c1ef 100644 --- a/examples/dry-running/lib.rs +++ b/examples/dry-running/lib.rs @@ -30,10 +30,11 @@ mod counter { mod tests { use drink::{ frame_support::sp_runtime::ModuleError, - minimal::RuntimeCall, pallet_balances, + runtime::{minimal::RuntimeCall, MinimalSandbox}, + sandbox::prelude::*, session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT}, - AccountId32, DispatchError, MinimalRuntime, Sandbox, SandboxConfig, + AccountId32, DispatchError, }; #[drink::contract_bundle_provider] @@ -87,7 +88,7 @@ mod tests { #[test] fn we_can_dry_run_normal_runtime_transaction() { - let mut sandbox = Sandbox::::new().expect("Failed to create sandbox"); + let mut sandbox = MinimalSandbox::default(); // Bob will be the recipient of the transfer. let bob = AccountId32::new([2u8; 32]); @@ -103,7 +104,7 @@ mod tests { dest: bob.clone().into(), value: 100, }), - Some(MinimalRuntime::default_actor()), + Some(MinimalSandbox::default_actor()), ) .expect("Failed to execute a call") }); diff --git a/examples/flipper/Cargo.lock b/examples/flipper/Cargo.lock index f23d637..d456276 100644 --- a/examples/flipper/Cargo.lock +++ b/examples/flipper/Cargo.lock @@ -1324,7 +1324,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.12.1" +version = "0.13.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1338,6 +1338,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", @@ -1349,7 +1350,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.12.1" +version = "0.13.0" dependencies = [ "cargo_metadata", "contract-build", @@ -1572,8 +1573,6 @@ name = "flipper" version = "0.1.0" dependencies = [ "drink", - "frame-support", - "frame-system", "ink", "parity-scale-codec", "scale-info", diff --git a/examples/flipper/Cargo.toml b/examples/flipper/Cargo.toml index d71bdb2..ac435a2 100755 --- a/examples/flipper/Cargo.toml +++ b/examples/flipper/Cargo.toml @@ -15,11 +15,6 @@ scale-info = { version = "2.6", default-features = false, features = ["derive"], [dev-dependencies] drink = { path = "../../drink" } -# testing custom runtime -frame-support = "30.0.0" -frame-system = "30.0.0" - - [lib] path = "lib.rs" diff --git a/examples/flipper/lib.rs b/examples/flipper/lib.rs index 37efebe..75b51f6 100755 --- a/examples/flipper/lib.rs +++ b/examples/flipper/lib.rs @@ -71,24 +71,4 @@ mod tests { Ok(()) } - - #[drink::test] - fn test_flipping_with_custom_runtime( - mut session: Session, - ) -> Result<(), Box> { - let contract = BundleProvider::Flipper.bundle()?; - let init_value: bool = session - .deploy_bundle_and(contract, "new", &["true"], NO_SALT, NO_ENDOWMENT)? - .call_and("flip", NO_ARGS, NO_ENDOWMENT)? - .call_and("flip", NO_ARGS, NO_ENDOWMENT)? - .call_and("flip", NO_ARGS, NO_ENDOWMENT)? - .call_and("get", NO_ARGS, NO_ENDOWMENT)? - .record() - .last_call_return_decoded()? - .expect("Call was successful"); - - assert_eq!(init_value, false); - - Ok(()) - } } diff --git a/examples/mocking/Cargo.lock b/examples/mocking/Cargo.lock index be27918..7e8714a 100644 --- a/examples/mocking/Cargo.lock +++ b/examples/mocking/Cargo.lock @@ -1328,7 +1328,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.12.0" +version = "0.13.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1342,6 +1342,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", @@ -1353,7 +1354,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.12.0" +version = "0.13.0" dependencies = [ "cargo_metadata", "contract-build", diff --git a/examples/multiple-contracts/Cargo.lock b/examples/multiple-contracts/Cargo.lock index 2817932..6339365 100644 --- a/examples/multiple-contracts/Cargo.lock +++ b/examples/multiple-contracts/Cargo.lock @@ -1328,7 +1328,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.12.1" +version = "0.13.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1342,6 +1342,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", @@ -1353,7 +1354,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.12.1" +version = "0.13.0" dependencies = [ "cargo_metadata", "contract-build", diff --git a/examples/quick-start-with-drink/Cargo.lock b/examples/quick-start-with-drink/Cargo.lock index fe41a3f..9cf5757 100644 --- a/examples/quick-start-with-drink/Cargo.lock +++ b/examples/quick-start-with-drink/Cargo.lock @@ -1328,7 +1328,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.12.1" +version = "0.13.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1342,6 +1342,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", @@ -1353,7 +1354,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.12.1" +version = "0.13.0" dependencies = [ "cargo_metadata", "contract-build", diff --git a/examples/quick-start-with-drink/lib.rs b/examples/quick-start-with-drink/lib.rs index 6e6a6b1..64a5172 100644 --- a/examples/quick-start-with-drink/lib.rs +++ b/examples/quick-start-with-drink/lib.rs @@ -72,7 +72,7 @@ mod tests { /// /// `drink::test` will already provide us with a `Session` object. It is a wrapper around a runtime and it exposes /// a broad API for interacting with it. Session is generic over the runtime type, but usually and by default, we - /// use `MinimalRuntime`, which is a minimalistic runtime that allows using smart contracts. + /// use `MinimalSandbox`, which is a minimalistic runtime that allows using smart contracts. #[drink::test] fn deploy_and_call_a_contract(mut session: Session) -> Result<(), Box> { // Now we get the contract bundle from the `BundleProvider` enum. Since the current crate diff --git a/examples/runtime-interaction/Cargo.lock b/examples/runtime-interaction/Cargo.lock index 23882de..c3aafe1 100644 --- a/examples/runtime-interaction/Cargo.lock +++ b/examples/runtime-interaction/Cargo.lock @@ -1322,7 +1322,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.12.0" +version = "0.13.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1336,6 +1336,7 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", + "paste", "scale-info", "serde_json", "sp-externalities", @@ -1347,7 +1348,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.12.0" +version = "0.13.0" dependencies = [ "cargo_metadata", "contract-build", diff --git a/examples/runtime-interaction/README.md b/examples/runtime-interaction/README.md index 720c740..902d2b7 100644 --- a/examples/runtime-interaction/README.md +++ b/examples/runtime-interaction/README.md @@ -12,13 +12,14 @@ cargo test --release ## `drink::Sandbox` vs `drink::Session` -While in most examples and showcases for `drink` you will see `drink::Session`, here we are using `drink::Sandbox`. -`Session` is very useful when you are working with contracts, but if you are focusing only on the runtime interaction, `Sandbox` is enough. -You can always switch from `Session` to `Sandbox` with: +While in most examples and showcases for `drink` you will see `drink::Session`, here we are using the associated `Sandbox` implementation directly. +`Session` is very useful when you are working with contracts, but if you are focusing only on the runtime interaction, you can simply use the underlying `Sandbox`. +You can get a reference to the `Sandbox` implementation from the `Session` object using the `sandbox` method: + ```rust - let session = Session::::new(); + let session = Session::::default(); ... - let sandbox = session.sandbox(); // `sandbox` has type `&mut Sandbox` + let sandbox = session.sandbox(); // `sandbox` has type `&mut MinimalSandbox` ``` `Sandbox` is just a runtime wrapper, which enables you to interact directly with the runtime. diff --git a/examples/runtime-interaction/lib.rs b/examples/runtime-interaction/lib.rs index b8fddfa..e6747de 100644 --- a/examples/runtime-interaction/lib.rs +++ b/examples/runtime-interaction/lib.rs @@ -3,21 +3,22 @@ mod tests { use drink::{ pallet_balances, pallet_contracts, pallet_contracts::Determinism, - runtime::{minimal::RuntimeCall, MinimalRuntime}, - AccountId32, Sandbox, SandboxConfig, + runtime::{minimal::RuntimeCall, MinimalSandbox}, + sandbox::prelude::*, + AccountId32, }; #[test] fn we_can_make_a_token_transfer_call() { // We create a sandbox object, which represents a blockchain runtime. - let mut sandbox = Sandbox::::new().expect("Failed to create sandbox"); + let mut sandbox = MinimalSandbox::default(); // Bob will be the recipient of the transfer. const BOB: AccountId32 = AccountId32::new([2u8; 32]); // Firstly, let us check that the recipient account (`BOB`) is not the default actor, that // will be used as the caller. - assert_ne!(MinimalRuntime::default_actor(), BOB); + assert_ne!(MinimalSandbox::default_actor(), BOB); // Recipient's balance before the transfer. let initial_balance = sandbox.free_balance(&BOB); @@ -30,7 +31,7 @@ mod tests { // Submit the call to the runtime. sandbox - .runtime_call(call_object, Some(MinimalRuntime::default_actor())) + .runtime_call(call_object, Some(MinimalSandbox::default_actor())) .expect("Failed to execute a call"); // In the end, the recipient's balance should be increased by 100. @@ -39,14 +40,14 @@ mod tests { #[test] fn we_can_work_with_the_contracts_pallet_in_low_level() { - let mut sandbox = Sandbox::::new().expect("Failed to create sandbox"); + let mut sandbox = MinimalSandbox::default(); // A few runtime calls are also available directly from the sandbox. This includes a part of // the contracts API. let upload_result = sandbox .upload_contract( wat::parse_str(CONTRACT).unwrap(), - MinimalRuntime::default_actor(), + MinimalSandbox::default_actor(), None, Determinism::Enforced, ) @@ -59,7 +60,7 @@ mod tests { }); sandbox - .runtime_call(call_object, Some(MinimalRuntime::default_actor())) + .runtime_call(call_object, Some(MinimalSandbox::default_actor())) .expect("Failed to remove a contract"); }