From 1187c423df0a30a158e378f639530e8fda2ad049 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 5 Feb 2024 17:51:39 +0100 Subject: [PATCH 01/13] Update sandbox --- Cargo.lock | 170 ++++------- Cargo.toml | 6 +- drink-cli/src/app_state/mod.rs | 6 +- drink/Cargo.toml | 2 +- drink/src/lib.rs | 6 +- drink/src/runtime.rs | 43 +-- drink/src/runtime/minimal.rs | 151 +++++----- drink/src/sandbox.rs | 8 +- drink/src/sandbox/balance_api.rs | 22 +- drink/src/sandbox/contract_api.rs | 61 ++-- drink/src/sandbox/contracts_api.rs | 296 ++++++++++++++++++++ drink/src/sandbox/runtime_api.rs | 25 +- drink/src/sandbox/sandbox_core.rs | 45 +++ drink/src/sandbox/system_api.rs | 24 +- drink/src/sandbox/timestamp_api.rs | 13 +- drink/src/session.rs | 79 +++--- drink/src/session/mocking_api.rs | 23 +- drink/src/session/record.rs | 61 ++-- drink/test-macro/Cargo.toml | 2 +- drink/test-macro/src/lib.rs | 12 +- examples/chain-extension/Cargo.toml | 2 +- examples/chain-extension/README.md | 2 +- examples/chain-extension/src/lib.rs | 8 +- examples/cross-contract-call-tracing/lib.rs | 1 + examples/flipper/Cargo.toml | 6 + examples/flipper/lib.rs | 32 +++ examples/mocking/lib.rs | 1 + examples/runtime-interaction/lib.rs | 4 +- 28 files changed, 736 insertions(+), 375 deletions(-) create mode 100644 drink/src/sandbox/contracts_api.rs create mode 100644 drink/src/sandbox/sandbox_core.rs diff --git a/Cargo.lock b/Cargo.lock index 6b83c68..0159b63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.10", + "getrandom", "once_cell", "version_check", ] @@ -55,7 +55,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.10", + "getrandom", "once_cell", "version_check", ] @@ -296,7 +296,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.5", + "rand", ] [[package]] @@ -401,7 +401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", + "rand", "rand_core 0.6.4", "serde", "unicode-normalization", @@ -772,7 +772,7 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" dependencies = [ - "getrandom 0.2.10", + "getrandom", "once_cell", "proc-macro-hack", "tiny-keccak", @@ -1325,18 +1325,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] -name = "drink" -version = "0.8.6" +name = "drink-cli" +version = "0.8.10" +dependencies = [ + "anyhow", + "clap", + "contract-build", + "contract-transcode", + "crossterm 0.26.1", + "drink-next", + "pallet-contracts-next", + "ratatui", + "sp-core", + "thiserror", +] + +[[package]] +name = "drink-next" +version = "0.8.10" dependencies = [ "contract-metadata", "contract-transcode", - "drink-test-macro", + "drink-test-macro-next", "frame-metadata", "frame-support", "frame-system", "pallet-balances", - "pallet-contracts", - "pallet-contracts-uapi", + "pallet-contracts-next", + "pallet-contracts-uapi-next", "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", @@ -1350,24 +1366,8 @@ dependencies = [ ] [[package]] -name = "drink-cli" -version = "0.8.6" -dependencies = [ - "anyhow", - "clap", - "contract-build", - "contract-transcode", - "crossterm 0.26.1", - "drink", - "pallet-contracts", - "ratatui", - "sp-core", - "thiserror", -] - -[[package]] -name = "drink-test-macro" -version = "0.8.6" +name = "drink-test-macro-next" +version = "0.8.10" dependencies = [ "cargo_metadata", "contract-build", @@ -1591,7 +1591,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.5", + "rand", "rustc-hex", "static_assertions", ] @@ -1878,17 +1878,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.10" @@ -1897,7 +1886,7 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -1906,7 +1895,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" dependencies = [ - "rand 0.8.5", + "rand", "rand_core 0.6.4", ] @@ -2503,7 +2492,7 @@ dependencies = [ "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", - "rand 0.8.5", + "rand", "serde", "sha2 0.9.9", "typenum", @@ -2766,7 +2755,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] @@ -2962,10 +2951,10 @@ dependencies = [ ] [[package]] -name = "pallet-contracts" -version = "27.0.0" +name = "pallet-contracts-next" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c24580e4e9b9c000f62be094e1be4cd92cf1e0b5ec54b9b6fb78cc6ed8f3efc" +checksum = "41fe236f281eb1a31c1f869cc17e2ddbd96fcd8ee331ae6f329923780d0bfdf6" dependencies = [ "bitflags 1.3.2", "environmental", @@ -2976,9 +2965,9 @@ dependencies = [ "log", "pallet-balances", "pallet-contracts-proc-macro", - "pallet-contracts-uapi", + "pallet-contracts-uapi-next", "parity-scale-codec", - "rand 0.8.5", + "rand", "scale-info", "serde", "smallvec", @@ -3005,10 +2994,10 @@ dependencies = [ ] [[package]] -name = "pallet-contracts-uapi" +name = "pallet-contracts-uapi-next" version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a992d0815b9dc36acbe0800b05b4f875398bb9d9b1aa15c8b1afdcb87f66df2a" +checksum = "306384c85b1e7629cb37b245afcfb9ba067eb575a345f46e1e0fb720248b16e6" dependencies = [ "bitflags 1.3.2", "parity-scale-codec", @@ -3213,15 +3202,15 @@ dependencies = [ [[package]] name = "polkavm-common" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecd2caacfc4a7ee34243758dd7348859e6dec73f5e5df059890f5742ee46f0e" +checksum = "88b4e215c80fe876147f3d58158d5dfeae7dabdd6047e175af77095b78d0035c" [[package]] name = "polkavm-derive" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db65a500d4adf574893c726ae365e37e4fbb7f2cbd403f6eaa1b665457456adc" +checksum = "6380dbe1fb03ecc74ad55d841cfc75480222d153ba69ddcb00977866cbdabdb8" dependencies = [ "polkavm-derive-impl", "syn 2.0.48", @@ -3229,9 +3218,9 @@ dependencies = [ [[package]] name = "polkavm-derive-impl" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99f4e7a9ff434ef9c885b874c99d824c3a5693bf5e3e8569bb1d2245a8c1b7f" +checksum = "dc8211b3365bbafb2fb32057d68b0e1ca55d079f5cf6f9da9b98079b94b3987d" dependencies = [ "polkavm-common", "proc-macro2", @@ -3360,19 +3349,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -3380,20 +3356,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", + "rand_chacha", "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - [[package]] name = "rand_chacha" version = "0.3.1" @@ -3409,9 +3375,6 @@ name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] [[package]] name = "rand_core" @@ -3419,16 +3382,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -3763,9 +3717,7 @@ dependencies = [ "arrayref", "arrayvec 0.5.2", "curve25519-dalek 2.1.3", - "getrandom 0.1.16", "merlin 2.0.1", - "rand 0.7.3", "rand_core 0.5.1", "sha2 0.8.2", "subtle", @@ -4193,7 +4145,7 @@ dependencies = [ "parking_lot", "paste", "primitive-types", - "rand 0.8.5", + "rand", "scale-info", "schnorrkel 0.11.4", "secp256k1", @@ -4362,7 +4314,7 @@ dependencies = [ "log", "parity-scale-codec", "paste", - "rand 0.8.5", + "rand", "scale-info", "serde", "simple-mermaid", @@ -4432,7 +4384,7 @@ dependencies = [ "log", "parity-scale-codec", "parking_lot", - "rand 0.8.5", + "rand", "smallvec", "sp-core", "sp-externalities", @@ -4504,7 +4456,7 @@ dependencies = [ "nohash-hasher", "parity-scale-codec", "parking_lot", - "rand 0.8.5", + "rand", "scale-info", "schnellru", "sp-core", @@ -4732,9 +4684,9 @@ dependencies = [ [[package]] name = "substrate-bip39" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +checksum = "e620c7098893ba667438b47169c00aacdd9e7c10e042250ce2b60b087ec97328" dependencies = [ "hmac 0.11.0", "pbkdf2", @@ -5131,7 +5083,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.7", - "rand 0.8.5", + "rand", "static_assertions", ] @@ -5247,8 +5199,8 @@ dependencies = [ "arrayref", "constcat", "digest 0.10.7", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand", + "rand_chacha", "rand_core 0.6.4", "sha2 0.10.7", "sha3", @@ -5275,12 +5227,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5561,7 +5507,7 @@ dependencies = [ "memfd", "memoffset", "paste", - "rand 0.8.5", + "rand", "rustix 0.36.14", "wasmtime-asm-macros", "wasmtime-environ", diff --git a/Cargo.toml b/Cargo.toml index 9e42ab6..98b70ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ homepage = "https://github.com/Cardinal-Cryptography/drink" license = "Apache-2.0" readme = "README.md" repository = "https://github.com/Cardinal-Cryptography/drink" -version = "0.8.6" +version = "0.8.10" [workspace.dependencies] anyhow = { version = "1.0.71" } @@ -57,5 +57,5 @@ sp-runtime-interface = { version = "24.0.0" } # Local dependencies -drink = { version = "0.8.6", path = "drink" } -drink-test-macro = { version = "0.8.6", path = "drink/test-macro" } +drink = { version = "0.8.10", path = "drink" } +drink-test-macro = { package = "drink-test-macro-next", version = "0.8.10", path = "drink/test-macro" } diff --git a/drink-cli/src/app_state/mod.rs b/drink-cli/src/app_state/mod.rs index 89e6378..a8517fb 100644 --- a/drink-cli/src/app_state/mod.rs +++ b/drink-cli/src/app_state/mod.rs @@ -1,11 +1,7 @@ use std::{env, path::PathBuf}; pub use contracts::{Contract, ContractIndex, ContractRegistry}; -use drink::{ - runtime::{MinimalRuntime, Runtime}, - session::Session, - Weight, DEFAULT_GAS_LIMIT, -}; +use drink::{runtime::MinimalRuntime, session::Session, SandboxConfig, Weight, DEFAULT_GAS_LIMIT}; use sp_core::crypto::AccountId32; pub use user_input::UserInput; diff --git a/drink/Cargo.toml b/drink/Cargo.toml index c6d7a42..f3a5902 100644 --- a/drink/Cargo.toml +++ b/drink/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "drink" +name = "drink-next" authors.workspace = true edition.workspace = true homepage.workspace = true diff --git a/drink/src/lib.rs b/drink/src/lib.rs index e8ffcf4..79cf4c8 100644 --- a/drink/src/lib.rs +++ b/drink/src/lib.rs @@ -29,10 +29,10 @@ use parity_scale_codec::{Decode, Encode}; /// Export pallets that are used in the minimal runtime. pub use {frame_support, frame_system, pallet_balances, pallet_contracts, pallet_timestamp}; +pub use crate::runtime::minimal::{self, MinimalRuntime}; use crate::{ - errors::MessageResult, - mock::MockRegistry, - runtime::{pallet_contracts_debugging::InterceptingExtT, *}, + errors::MessageResult, mock::MockRegistry, + runtime::pallet_contracts_debugging::InterceptingExtT, }; /// Alias for `frame-system`'s `RuntimeCall` type. diff --git a/drink/src/runtime.rs b/drink/src/runtime.rs index 3f4b5b7..7291381 100644 --- a/drink/src/runtime.rs +++ b/drink/src/runtime.rs @@ -2,53 +2,12 @@ //! `drink` with any runtime that implements the `Runtime` trait. pub mod minimal; -pub mod pallet_contracts_debugging; - pub use frame_metadata::RuntimeMetadataPrefixed; -use frame_support::sp_runtime::{traits::Dispatchable, Storage}; -use frame_system::pallet_prelude::BlockNumberFor; pub use minimal::MinimalRuntime; +pub mod pallet_contracts_debugging; /// The type of an account identifier. pub type AccountIdFor = ::AccountId; /// The type of a hash. pub type HashFor = ::Hash; - -/// A runtime to use. -pub trait Runtime: frame_system::Config { - /// Initialize the storage at the genesis block. - fn initialize_storage(_storage: &mut Storage) -> Result<(), String> { - Ok(()) - } - - /// Initialize a new block at particular height. - fn initialize_block( - _height: BlockNumberFor, - _parent_hash: ::Hash, - ) -> Result<(), String> { - Ok(()) - } - - /// Finalize a block at particular height. - fn finalize_block( - _height: BlockNumberFor, - ) -> Result<::Hash, String> { - Ok(Default::default()) - } - - /// Default actor for the runtime. - 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; -} - -/// Convenient umbrella trait for `Runtime + pallet_contracts::Config` -pub trait RuntimeWithContracts: Runtime + pallet_contracts::Config {} -impl RuntimeWithContracts for T {} diff --git a/drink/src/runtime/minimal.rs b/drink/src/runtime/minimal.rs index a3091a1..be776f5 100644 --- a/drink/src/runtime/minimal.rs +++ b/drink/src/runtime/minimal.rs @@ -1,13 +1,89 @@ #![allow(missing_docs)] // `construct_macro` doesn't allow doc comments for the runtime type. +#[macro_export] +macro_rules! impl_sandbox_config { + ( + struct $name:ident { + runtime: $runtime:tt; + default_balance: $default_balance:expr; + default_actor: $default_actor:expr; + } + ) => { + 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() + } + } + }; +} + /// 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::Runtime`. #[macro_export] -macro_rules! create_minimal_runtime { +macro_rules! create_minimal_sandbox { ($name:ident) => { - create_minimal_runtime!($name, ()); + create_minimal_sandbox!($name, ()); }; ($name:ident, $chain_extension: ty) => { @@ -128,83 +204,22 @@ mod construct_runtime { type Xcm = (); } -// ------------ Implement `drink::Runtime` trait --------------------------------------------------- - - use std::time::SystemTime; - - use $crate::frame_support::{ - sp_runtime::{traits::Dispatchable, BuildStorage, Storage}, - traits::Hooks, - }; - - use $crate::runtime::{AccountIdFor, Runtime, RuntimeMetadataPrefixed}; - + // ------------ Implement `drink::Runtime` trait --------------------------------------------------- /// 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])); +} - impl Runtime for $name { - fn initialize_storage(storage: &mut Storage) -> Result<(), String> { - $crate::pallet_balances::GenesisConfig:: { - balances: vec![(Self::default_actor(), INITIAL_BALANCE)], - } - .assimilate_storage(storage) - } - - fn initialize_block( - height: $crate::frame_system::pallet_prelude::BlockNumberFor, - parent_hash: H256, - ) -> Result<(), String> { - System::reset_events(); - System::initialize(&height, &parent_hash, &Default::default()); - - Balances::on_initialize(height); - Timestamp::set_timestamp( - SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .expect("Time went backwards") - .as_secs(), - ); - Timestamp::on_initialize(height); - Contracts::on_initialize(height); - - System::note_finished_initialize(); - - Ok(()) - } - - fn finalize_block( - height: $crate::frame_system::pallet_prelude::BlockNumberFor, - ) -> Result { - Contracts::on_finalize(height); - Timestamp::on_finalize(height); - Balances::on_finalize(height); - Ok(System::finalize().hash()) - } - fn default_actor() -> AccountIdFor { - AccountId32::new([1u8; 32]) - } - - fn get_metadata() -> RuntimeMetadataPrefixed { - Self::metadata() - } - - fn convert_account_to_origin( - account: AccountIdFor, - ) -> <::RuntimeCall as 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, - RuntimeOrigin, System, Timestamp, INITIAL_BALANCE, + RuntimeOrigin, System, Timestamp, }; }; } -create_minimal_runtime!(MinimalRuntime); +create_minimal_sandbox!(MinimalRuntime); diff --git a/drink/src/sandbox.rs b/drink/src/sandbox.rs index 22275ef..8095282 100644 --- a/drink/src/sandbox.rs +++ b/drink/src/sandbox.rs @@ -1,5 +1,7 @@ //! A sandboxed runtime. +mod sandbox_core; +pub use sandbox_core::SandboxConfig; pub mod balance_api; pub mod contract_api; pub mod runtime_api; @@ -12,12 +14,12 @@ use sp_externalities::Extension; use sp_io::TestExternalities; /// A sandboxed runtime. -pub struct Sandbox { +pub struct Sandbox { externalities: TestExternalities, - _phantom: std::marker::PhantomData, + _phantom: std::marker::PhantomData, } -impl Sandbox { +impl Sandbox { /// Execute the given closure with the inner externallities. /// /// Returns the result of the given closure. diff --git a/drink/src/sandbox/balance_api.rs b/drink/src/sandbox/balance_api.rs index 9174b16..104c151 100644 --- a/drink/src/sandbox/balance_api.rs +++ b/drink/src/sandbox/balance_api.rs @@ -4,7 +4,10 @@ use frame_support::{sp_runtime::DispatchError, traits::fungible::Mutate}; use super::Sandbox; use crate::{runtime::AccountIdFor, BalanceOf}; -impl Sandbox { +impl Sandbox +where + Config::Runtime: crate::pallet_balances::Config, +{ /// Mint tokens to an account. /// /// # Arguments @@ -13,10 +16,12 @@ impl Sandbox { /// * `amount` - The number of tokens to add. pub 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> { + self.execute_with(|| { + pallet_balances::Pallet::::mint_into(&address, amount) + }) } /// Return the free balance of an account. @@ -24,7 +29,10 @@ impl Sandbox { /// # Arguments /// /// * `address` - The address of the account to query. - pub fn free_balance(&mut self, address: &AccountIdFor) -> BalanceOf { - self.execute_with(|| pallet_balances::Pallet::::free_balance(address)) + pub fn free_balance( + &mut self, + address: &AccountIdFor, + ) -> BalanceOf { + self.execute_with(|| pallet_balances::Pallet::::free_balance(address)) } } diff --git a/drink/src/sandbox/contract_api.rs b/drink/src/sandbox/contract_api.rs index 2ef6abd..a709f9f 100644 --- a/drink/src/sandbox/contract_api.rs +++ b/drink/src/sandbox/contract_api.rs @@ -2,7 +2,7 @@ use std::ops::Not; use frame_support::{traits::fungible::Inspect, weights::Weight}; -use frame_system::Config; +use frame_system::Config as SysConfig; use pallet_contracts::{ Code, CodeUploadResult, CollectEvents, ContractExecResult, ContractInstantiateResult, DebugInfo, Determinism, @@ -14,7 +14,10 @@ use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox}; type BalanceOf = <::Currency as Inspect>>::Balance; -impl Sandbox { +impl Sandbox +where + Config::Runtime: pallet_contracts::Config, +{ /// Interface for `bare_instantiate` contract call with a simultaneous upload. /// /// # Arguments @@ -30,15 +33,19 @@ impl Sandbox { pub 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>, - ) -> ContractInstantiateResult, BalanceOf, EventRecordOf> { + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult< + AccountIdFor, + BalanceOf, + EventRecordOf, + > { self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( + pallet_contracts::Pallet::::bare_instantiate( origin, value, gas_limit, @@ -67,22 +74,27 @@ impl Sandbox { pub 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, BalanceOf, EventRecordOf> { + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult< + AccountIdFor, + BalanceOf, + EventRecordOf, + > { let mut code_hash = &code_hash[..]; self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( + pallet_contracts::Pallet::::bare_instantiate( origin, value, gas_limit, storage_deposit_limit, Code::Existing( - ::Hash::decode(&mut code_hash).expect("Invalid code hash"), + ::Hash::decode(&mut code_hash) + .expect("Invalid code hash"), ), data, salt, @@ -102,12 +114,13 @@ impl Sandbox { pub 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> { + ) -> CodeUploadResult<::Hash, BalanceOf> + { self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_upload_code( + pallet_contracts::Pallet::::bare_upload_code( origin, contract_bytes, storage_deposit_limit, @@ -129,16 +142,16 @@ impl Sandbox { #[allow(clippy::too_many_arguments)] pub 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, - ) -> ContractExecResult, EventRecordOf> { + ) -> ContractExecResult, EventRecordOf> { self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_call( + pallet_contracts::Pallet::::bare_call( origin, address, value, @@ -169,7 +182,7 @@ mod tests { use super::*; use crate::{ - minimal::RuntimeEvent, runtime::Runtime, session::NO_SALT, MinimalRuntime, + minimal::RuntimeEvent, sandbox::SandboxConfig, session::NO_SALT, MinimalRuntime, DEFAULT_GAS_LIMIT, }; diff --git a/drink/src/sandbox/contracts_api.rs b/drink/src/sandbox/contracts_api.rs new file mode 100644 index 0000000..5182577 --- /dev/null +++ b/drink/src/sandbox/contracts_api.rs @@ -0,0 +1,296 @@ +//! Contracts API for the sandbox. +use std::ops::Not; + +use frame_support::{traits::fungible::Inspect, weights::Weight}; +use frame_system::Config; +use pallet_contracts::{ + Code, CodeUploadResult, CollectEvents, ContractExecResult, ContractInstantiateResult, + DebugInfo, Determinism, +}; +use parity_scale_codec::Decode as _; + +use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox}; + +type BalanceOf = + <::Currency as Inspect>>::Balance; + +pub trait ContractsApi: crate::SandboxApi { + /// Interface for `bare_instantiate` contract call with a simultaneous upload. + /// + /// # Arguments + /// + /// * `contract_bytes` - The contract code. + /// * `value` - The number of tokens to be transferred to the contract. + /// * `data` - The input data to be passed to the contract (including constructor name). + /// * `salt` - The salt to be used for contract address derivation. + /// * `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)] + fn deploy_contract( + &mut self, + contract_bytes: Vec, + value: BalanceOf, + data: Vec, + salt: Vec, + origin: AccountIdFor, + gas_limit: Weight, + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult, BalanceOf, EventRecordOf> { + 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, + ) + }) + } + + /// Interface for `bare_instantiate` contract call for a previously uploaded contract. + /// + /// # Arguments + /// + /// * `code_hash` - The code hash of the contract to instantiate. + /// * `value` - The number of tokens to be transferred to the contract. + /// * `data` - The input data to be passed to the contract (including constructor name). + /// * `salt` - The salt to be used for contract address derivation. + /// * `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)] + 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> { + let mut code_hash = &code_hash[..]; + self.externalities().execute_with(|| { + pallet_contracts::Pallet::::bare_instantiate( + origin, + value, + gas_limit, + storage_deposit_limit, + Code::Existing( + ::Hash::decode(&mut code_hash).expect("Invalid code hash"), + ), + data, + salt, + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + ) + }) + } + + /// 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> { + self.externalities().execute_with(|| { + pallet_contracts::Pallet::::bare_upload_code( + origin, + contract_bytes, + storage_deposit_limit, + determinism, + ) + }) + } + + /// 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)] + fn call_contract( + &mut self, + address: AccountIdFor, + value: BalanceOf, + data: Vec, + origin: AccountIdFor, + gas_limit: Weight, + storage_deposit_limit: Option>, + determinism: Determinism, + ) -> ContractExecResult, EventRecordOf> { + self.externalities().execute_with(|| { + pallet_contracts::Pallet::::bare_call( + origin, + address, + value, + gas_limit, + storage_deposit_limit, + data, + DebugInfo::UnsafeDebug, + CollectEvents::UnsafeCollect, + determinism, + ) + }) + } +} + +/// Converts bytes to a '\n'-split string, ignoring empty lines. +pub fn decode_debug_buffer(buffer: &[u8]) -> Vec { + let decoded = buffer.iter().map(|b| *b as char).collect::(); + decoded + .split('\n') + .filter_map(|s| s.is_empty().not().then_some(s.to_string())) + .collect() +} + +#[cfg(test)] +mod tests { + use frame_support::sp_runtime::traits::Hash; + use pallet_contracts::Origin; + + use super::*; + use crate::{ + minimal::RuntimeEvent, runtime::Runtime, session::NO_SALT, MinimalRuntime, + DEFAULT_GAS_LIMIT, + }; + + fn compile_module(contract_name: &str) -> Vec { + let path = [ + std::env::var("CARGO_MANIFEST_DIR") + .as_deref() + .unwrap_or("drink"), + "/test-resources/", + contract_name, + ".wat", + ] + .concat(); + wat::parse_file(path).expect("Failed to parse wat file") + } + + #[test] + fn can_upload_code() { + let mut sandbox = Sandbox::::new().unwrap(); + let wasm_binary = compile_module("dummy"); + let hash = <::Hashing>::hash(&wasm_binary); + + let result = sandbox.upload_contract( + wasm_binary, + MinimalRuntime::default_actor(), + None, + Determinism::Enforced, + ); + + assert!(result.is_ok()); + assert_eq!(hash, result.unwrap().code_hash); + } + + #[test] + fn can_deploy_contract() { + let mut sandbox = Sandbox::::new().unwrap(); + let wasm_binary = compile_module("dummy"); + + let events_before = sandbox.events(); + assert!(events_before.is_empty()); + + let result = sandbox.deploy_contract( + wasm_binary, + 0, + vec![], + NO_SALT, + MinimalRuntime::default_actor(), + DEFAULT_GAS_LIMIT, + None, + ); + assert!(result.result.is_ok()); + assert!(!result.result.unwrap().result.did_revert()); + + let events = result.events.expect("Drink should collect events"); + let event_count = events.len(); + let instantiation_event = events[event_count - 2].clone(); + assert!(matches!( + instantiation_event.event, + 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 { .. } + ) + )); + } + + #[test] + fn can_call_contract() { + let mut sandbox = Sandbox::::new().unwrap(); + let actor = MinimalRuntime::default_actor(); + let wasm_binary = compile_module("dummy"); + + let result = sandbox.deploy_contract( + wasm_binary, + 0, + vec![], + NO_SALT, + actor.clone(), + DEFAULT_GAS_LIMIT, + None, + ); + + let contract_address = result + .result + .expect("Contract should be deployed") + .account_id; + + sandbox.reset_events(); + + let result = sandbox.call_contract( + contract_address.clone(), + 0, + vec![], + actor.clone(), + DEFAULT_GAS_LIMIT, + None, + Determinism::Enforced, + ); + assert!(result.result.is_ok()); + assert!(!result.result.unwrap().did_revert()); + + let events = result.events.expect("Drink should collect events"); + assert_eq!(events.len(), 2); + + assert_eq!( + events[0].event, + 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 { + contract: contract_address, + caller: Origin::Signed(actor), + }), + ); + } +} diff --git a/drink/src/sandbox/runtime_api.rs b/drink/src/sandbox/runtime_api.rs index 35a8719..6fdb2d9 100644 --- a/drink/src/sandbox/runtime_api.rs +++ b/drink/src/sandbox/runtime_api.rs @@ -8,9 +8,9 @@ use frame_system::{pallet_prelude::BlockNumberFor, GenesisConfig}; use sp_io::TestExternalities; use super::Sandbox; -use crate::{DrinkResult, Error, Runtime}; +use crate::{DrinkResult, Error}; -impl Sandbox { +impl Sandbox { /// Creates a new sandbox. /// /// Returns an error if the storage could not be initialized. @@ -18,11 +18,11 @@ impl Sandbox { /// The storage is initialized with a genesis block with a single account `R::default_actor()` with /// `INITIAL_BALANCE`. pub fn new() -> DrinkResult { - let mut storage = GenesisConfig::::default() + let mut storage = GenesisConfig::::default() .build_storage() .map_err(Error::StorageBuilding)?; - R::initialize_storage(&mut storage).map_err(Error::StorageBuilding)?; + Config::initialize_storage(&mut storage).map_err(Error::StorageBuilding)?; let mut sandbox = Self { externalities: TestExternalities::new(storage), @@ -33,18 +33,23 @@ impl Sandbox { .externalities // We start the chain from the 1st block, so that events are collected (they are not // recorded for the genesis block...). - .execute_with(|| R::initialize_block(BlockNumberFor::::one(), Default::default())) + .execute_with(|| { + Config::initialize_block( + BlockNumberFor::::one(), + Default::default(), + ) + }) .map_err(Error::BlockInitialize)?; Ok(sandbox) } /// Build a new empty block and return the new height. - pub fn build_block(&mut self) -> DrinkResult> { + pub fn build_block(&mut self) -> DrinkResult> { self.execute_with(|| { - let mut current_block = frame_system::Pallet::::block_number(); - let block_hash = R::finalize_block(current_block).map_err(Error::BlockFinalize)?; + let mut current_block = frame_system::Pallet::::block_number(); + let block_hash = Config::finalize_block(current_block).map_err(Error::BlockFinalize)?; current_block.saturating_inc(); - R::initialize_block(current_block, block_hash).map_err(Error::BlockInitialize)?; + Config::initialize_block(current_block, block_hash).map_err(Error::BlockInitialize)?; Ok(current_block) }) } @@ -53,7 +58,7 @@ impl Sandbox { /// # Arguments /// /// * `n` - The number of blocks to build. - pub fn build_blocks(&mut self, n: u32) -> DrinkResult> { + pub fn build_blocks(&mut self, n: u32) -> DrinkResult> { let mut last_block = None; for _ in 0..n { last_block = Some(self.build_block()?); diff --git a/drink/src/sandbox/sandbox_core.rs b/drink/src/sandbox/sandbox_core.rs new file mode 100644 index 0000000..74cddf0 --- /dev/null +++ b/drink/src/sandbox/sandbox_core.rs @@ -0,0 +1,45 @@ +//! Module containing the core trait use to customize the sandboxed runtime. + +use frame_metadata::RuntimeMetadataPrefixed; +use frame_support::sp_runtime::{traits::Dispatchable, Storage}; +use frame_system::pallet_prelude::BlockNumberFor; + +/// The type of an account identifier. +pub type AccountIdFor = ::AccountId; + +/// A runtime to use. +pub trait SandboxConfig { + /// The runtime associated with the sandbox. + type Runtime: frame_system::Config; + + /// Initialize the storage at the genesis block. + fn initialize_storage(_storage: &mut Storage) -> Result<(), String> { + Ok(()) + } + + /// Initialize a new block at particular height. + fn initialize_block( + _height: BlockNumberFor, + _parent_hash: ::Hash, + ) -> Result<(), String> { + Ok(()) + } + + /// Finalize a block at particular height. + fn finalize_block( + _height: BlockNumberFor, + ) -> Result<::Hash, String> { + Ok(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/system_api.rs b/drink/src/sandbox/system_api.rs index c66f518..315cd29 100644 --- a/drink/src/sandbox/system_api.rs +++ b/drink/src/sandbox/system_api.rs @@ -6,20 +6,20 @@ use frame_system::pallet_prelude::BlockNumberFor; use super::Sandbox; use crate::{EventRecordOf, RuntimeCall}; -impl Sandbox { +impl Sandbox { /// Return the current height of the chain. - pub fn block_number(&mut self) -> BlockNumberFor { - self.execute_with(|| frame_system::Pallet::::block_number()) + pub fn block_number(&mut self) -> BlockNumberFor { + self.execute_with(|| frame_system::Pallet::::block_number()) } /// Return the events of the current block so far. - pub fn events(&mut self) -> Vec> { - self.execute_with(frame_system::Pallet::::events) + pub fn events(&mut self) -> Vec> { + self.execute_with(frame_system::Pallet::::events) } /// Reset the events of the current block. pub fn reset_events(&mut self) { - self.execute_with(frame_system::Pallet::::reset_events) + self.execute_with(frame_system::Pallet::::reset_events) } /// Execute a runtime call (dispatchable). @@ -28,11 +28,13 @@ impl Sandbox { /// /// * `call` - The runtime call to execute. /// * `origin` - The origin of the call. - pub fn runtime_call as Dispatchable>::RuntimeOrigin>>( + pub fn runtime_call< + Origin: Into< 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())) } } @@ -42,8 +44,8 @@ mod tests { use frame_support::sp_runtime::{traits::Dispatchable, DispatchResultWithInfo}; use crate::{ - runtime::{minimal::RuntimeEvent, MinimalRuntime, Runtime}, - AccountId32, RuntimeCall, Sandbox, + runtime::{minimal::RuntimeEvent, MinimalRuntime}, + AccountId32, RuntimeCall, Sandbox, SandboxConfig, }; fn make_transfer( diff --git a/drink/src/sandbox/timestamp_api.rs b/drink/src/sandbox/timestamp_api.rs index 4f93ffa..a17460e 100644 --- a/drink/src/sandbox/timestamp_api.rs +++ b/drink/src/sandbox/timestamp_api.rs @@ -5,10 +5,13 @@ use crate::Sandbox; /// Generic Time type. type MomentOf = ::Moment; -impl Sandbox { +impl Sandbox +where + Config::Runtime: pallet_timestamp::Config, +{ /// Return the timestamp of the current block. - pub fn get_timestamp(&mut self) -> MomentOf { - self.execute_with(pallet_timestamp::Pallet::::get) + pub fn get_timestamp(&mut self) -> MomentOf { + self.execute_with(pallet_timestamp::Pallet::::get) } /// Set the timestamp of the current block. @@ -16,8 +19,8 @@ impl Sandbox { /// # 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)) + pub fn set_timestamp(&mut self, timestamp: MomentOf) { + self.execute_with(|| pallet_timestamp::Pallet::::set_timestamp(timestamp)) } } diff --git a/drink/src/session.rs b/drink/src/session.rs index 8846175..4291f51 100644 --- a/drink/src/session.rs +++ b/drink/src/session.rs @@ -18,8 +18,9 @@ use crate::{ mock::MockRegistry, runtime::{ pallet_contracts_debugging::{InterceptingExt, TracingExt}, - AccountIdFor, HashFor, RuntimeWithContracts, + AccountIdFor, HashFor, }, + sandbox::SandboxConfig, MockingExtension, Sandbox, DEFAULT_GAS_LIMIT, }; @@ -130,19 +131,25 @@ pub const NO_ENDOWMENT: Option> = None; /// .deploy_bundle_and(contract, "new", NO_ARGS, NO_SALT, NO_ENDOWMENT)?; /* ... */ /// # Ok(()) } /// ``` -pub struct Session { - sandbox: Sandbox, +pub struct Session +where + Config::Runtime: pallet_contracts::Config, +{ + sandbox: Sandbox, - actor: AccountIdFor, + actor: AccountIdFor, gas_limit: Weight, determinism: Determinism, - transcoders: TranscoderRegistry>, - record: Record, - mocks: Arc>>>, + transcoders: TranscoderRegistry>, + record: Record, + mocks: Arc>>>, } -impl Session { +impl Session +where + Config::Runtime: pallet_contracts::Config, +{ /// Creates a new `Session`. pub fn new() -> Result { let mocks = Arc::new(Mutex::new(MockRegistry::new())); @@ -154,7 +161,7 @@ impl Session { Ok(Self { sandbox, mocks, - actor: R::default_actor(), + actor: Config::default_actor(), gas_limit: DEFAULT_GAS_LIMIT, determinism: Determinism::Enforced, transcoders: TranscoderRegistry::new(), @@ -163,17 +170,20 @@ impl Session { } /// 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) } @@ -208,7 +218,7 @@ impl Session { /// 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); @@ -218,24 +228,24 @@ impl Session { /// 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 Sandbox { &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 } @@ -247,7 +257,7 @@ impl Session { constructor: &str, args: &[S], salt: Vec, - endowment: Option>, + endowment: Option>, transcoder: &Rc, ) -> Result { self.deploy( @@ -269,9 +279,9 @@ impl Session { 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()))?; @@ -316,8 +326,8 @@ impl Session { constructor: &str, args: &[S], salt: Vec, - endowment: Option>, - ) -> Result, SessionError> { + endowment: Option>, + ) -> Result, SessionError> { self.deploy( contract_file.wasm, constructor, @@ -337,7 +347,7 @@ impl Session { constructor: &str, args: &[S], salt: Vec, - endowment: Option>, + endowment: Option>, ) -> Result { self.deploy_bundle(contract_file, constructor, args, salt, endowment) .map(|_| self) @@ -349,7 +359,10 @@ impl Session { } /// 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(), @@ -375,7 +388,7 @@ impl Session { pub fn upload_bundle( &mut self, contract_file: ContractBundle, - ) -> Result, SessionError> { + ) -> Result, SessionError> { self.upload(contract_file.wasm) } @@ -384,7 +397,7 @@ impl Session { 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. @@ -395,10 +408,10 @@ impl Session { /// 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. @@ -411,7 +424,7 @@ impl Session { &mut self, message: &str, args: &[S], - endowment: Option>, + endowment: Option>, ) -> Result, SessionError> { self.call_internal::<_, T>(None, message, args, endowment) } @@ -420,20 +433,20 @@ impl Session { /// result. pub fn call_with_address + Debug, T: Decode>( &mut self, - address: AccountIdFor, + address: AccountIdFor, message: &str, args: &[S], - endowment: Option>, + endowment: Option>, ) -> Result, SessionError> { self.call_internal(Some(address), message, args, endowment) } fn call_internal + Debug, T: Decode>( &mut self, - address: Option>, + address: Option>, message: &str, args: &[S], - endowment: Option>, + endowment: Option>, ) -> Result, SessionError> { let address = match address { Some(address) => address, diff --git a/drink/src/session/mocking_api.rs b/drink/src/session/mocking_api.rs index 4d7d9e4..3c0957b 100644 --- a/drink/src/session/mocking_api.rs +++ b/drink/src/session/mocking_api.rs @@ -1,13 +1,9 @@ //! Mocking API for the sandbox. use super::Session; -use crate::{ - mock::ContractMock, - runtime::{AccountIdFor, RuntimeWithContracts}, - DEFAULT_GAS_LIMIT, -}; +use crate::{mock::ContractMock, runtime::AccountIdFor, SandboxConfig, DEFAULT_GAS_LIMIT}; /// Interface for basic mocking operations. -pub trait MockingApi { +pub trait MockingApi { /// Deploy `mock` as a standard contract. Returns the address of the deployed contract. fn deploy(&mut self, mock: ContractMock) -> AccountIdFor; @@ -16,8 +12,11 @@ pub trait MockingApi { fn mock_existing_contract(&mut self, _mock: ContractMock, _address: AccountIdFor); } -impl MockingApi for Session { - fn deploy(&mut self, mock: ContractMock) -> AccountIdFor { +impl MockingApi for Session +where + Config::Runtime: pallet_contracts::Config, +{ + 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. @@ -35,7 +34,7 @@ impl MockingApi for Session { 0u32.into(), vec![], salt, - R::default_actor(), + Config::default_actor(), DEFAULT_GAS_LIMIT, None, ) @@ -51,7 +50,11 @@ impl MockingApi for Session { 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 51a6c50..f6cd4c8 100644 --- a/drink/src/session/record.rs +++ b/drink/src/session/record.rs @@ -5,9 +5,9 @@ use parity_scale_codec::{Decode, Encode}; use crate::{ errors::MessageResult, - runtime::{minimal::RuntimeEvent, AccountIdFor, MinimalRuntime, RuntimeWithContracts}, + runtime::{minimal::RuntimeEvent, AccountIdFor, MinimalRuntime}, session::{error::SessionError, BalanceOf}, - EventRecordOf, Sandbox, + EventRecordOf, Sandbox, SandboxConfig, }; type ContractInstantiateResult = @@ -21,20 +21,23 @@ type ContractExecResult = pallet_contracts::ContractExecResult, /// By `result` we mean the full result (enriched with some context information) of the contract /// interaction, like `ContractExecResult`. By `return` we mean the return value of the contract /// execution, like a value returned from a message or the address of a newly instantiated contract. -pub struct Record { +pub struct Record +where + Config::Runtime: pallet_contracts::Config, +{ /// The results of contract instantiation. - deploy_results: Vec>, + deploy_results: Vec>, /// The return values of contract instantiation (i.e. the addresses of the newly instantiated /// contracts). - deploy_returns: Vec>, + deploy_returns: Vec>, /// The results of contract calls. - call_results: Vec>, + call_results: Vec>, /// The return values of contract calls (in the SCALE-encoded form). call_returns: Vec>, /// The events emitted by the contracts. - event_batches: Vec>, + event_batches: Vec>, /// Because `drink` normally doesn't have a continuous block production, everything implicitly /// happens within a single block (unless user explicitly trigger a new block). This means that @@ -46,7 +49,10 @@ pub struct Record { block_events_so_far: Option, } -impl Default for Record { +impl Default for Record +where + Config::Runtime: pallet_contracts::Config, +{ fn default() -> Self { Self { deploy_results: Vec::new(), @@ -60,16 +66,22 @@ impl Default for Record { } // API for `Session` to record results and events related to contract interaction. -impl Record { - pub(super) fn push_deploy_result(&mut self, result: ContractInstantiateResult) { +impl Record +where + Config::Runtime: pallet_contracts::Config, +{ + pub(super) fn push_deploy_result( + &mut self, + result: ContractInstantiateResult, + ) { self.deploy_results.push(result); } - pub(super) fn push_deploy_return(&mut self, return_value: AccountIdFor) { + pub(super) fn push_deploy_return(&mut self, return_value: AccountIdFor) { self.deploy_returns.push(return_value); } - pub(super) fn push_call_result(&mut self, result: ContractExecResult) { + pub(super) fn push_call_result(&mut self, result: ContractExecResult) { self.call_results.push(result); } @@ -77,7 +89,7 @@ impl Record { self.call_returns.push(return_value); } - pub(super) fn start_recording_events(&mut self, sandbox: &mut Sandbox) { + pub(super) fn start_recording_events(&mut self, sandbox: &mut Sandbox) { assert!( self.block_events_so_far.is_none(), "Already recording events" @@ -85,7 +97,7 @@ impl Record { self.block_events_so_far = Some(sandbox.events().len()); } - pub(super) fn stop_recording_events(&mut self, sandbox: &mut Sandbox) { + pub(super) fn stop_recording_events(&mut self, sandbox: &mut Sandbox) { let start = self .block_events_so_far .take() @@ -97,37 +109,40 @@ impl Record { } // API for the end user. -impl Record { +impl Record +where + Config::Runtime: pallet_contracts::Config, +{ /// Returns all the results of contract instantiations that happened during the session. - pub fn deploy_results(&self) -> &[ContractInstantiateResult] { + pub fn deploy_results(&self) -> &[ContractInstantiateResult] { &self.deploy_results } /// Returns the last result of contract instantiation that happened during the session. Panics /// if there were no contract instantiations. - pub fn last_deploy_result(&self) -> &ContractInstantiateResult { + pub fn last_deploy_result(&self) -> &ContractInstantiateResult { self.deploy_results.last().expect("No deploy results") } /// Returns all the return values of contract instantiations that happened during the session. - pub fn deploy_returns(&self) -> &[AccountIdFor] { + pub fn deploy_returns(&self) -> &[AccountIdFor] { &self.deploy_returns } /// Returns the last return value of contract instantiation that happened during the session. /// Panics if there were no contract instantiations. - pub fn last_deploy_return(&self) -> &AccountIdFor { + pub fn last_deploy_return(&self) -> &AccountIdFor { self.deploy_returns.last().expect("No deploy returns") } /// Returns all the results of contract calls that happened during the session. - pub fn call_results(&self) -> &[ContractExecResult] { + pub fn call_results(&self) -> &[ContractExecResult] { &self.call_results } /// Returns the last result of contract call that happened during the session. Panics if there /// were no contract calls. - pub fn last_call_result(&self) -> &ContractExecResult { + pub fn last_call_result(&self) -> &ContractExecResult { self.call_results.last().expect("No call results") } @@ -155,13 +170,13 @@ impl Record { /// Returns all the event batches that were recorded for contract interactions during the /// session. - pub fn event_batches(&self) -> &[EventBatch] { + pub fn event_batches(&self) -> &[EventBatch] { &self.event_batches } /// Returns the last event batch that was recorded for contract interactions during the session. /// Panics if there were no event batches. - pub fn last_event_batch(&self) -> &EventBatch { + pub fn last_event_batch(&self) -> &EventBatch { self.event_batches.last().expect("No event batches") } } diff --git a/drink/test-macro/Cargo.toml b/drink/test-macro/Cargo.toml index f308883..d94e0af 100644 --- a/drink/test-macro/Cargo.toml +++ b/drink/test-macro/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "drink-test-macro" +name = "drink-test-macro-next" authors.workspace = true edition.workspace = true homepage.workspace = true diff --git a/drink/test-macro/src/lib.rs b/drink/test-macro/src/lib.rs index b5b2b99..4a6c57d 100644 --- a/drink/test-macro/src/lib.rs +++ b/drink/test-macro/src/lib.rs @@ -39,8 +39,8 @@ type SynResult = Result; /// # Creating a session object /// /// The macro will also create a new mutable session object and pass it to the decorated function by value. You can -/// configure which runtime should be used (by specifying a path to a type implementing -/// `drink::runtime::RuntimeWithContracts` trait. Thus, your testcase function should accept a single argument: +/// 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: /// `mut session: Session<_>`. /// /// By default, the macro will use `drink::runtime::MinimalRuntime`. @@ -65,7 +65,7 @@ pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream { #[derive(FromMeta)] struct TestAttributes { - runtime: Option, + config: Option, } /// Auxiliary function to enter ?-based error propagation. @@ -85,15 +85,15 @@ fn test_internal(attr: TokenStream2, item: TokenStream2) -> SynResult::new().expect("Failed to create a session"); + let mut session = Session::<#config>::new().expect("Failed to create a session"); #fn_block } }) diff --git a/examples/chain-extension/Cargo.toml b/examples/chain-extension/Cargo.toml index 87a7da6..5e42178 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 = "28.0.0" frame-system = "28.0.0" diff --git a/examples/chain-extension/README.md b/examples/chain-extension/README.md index 47303de..172e100 100644 --- a/examples/chain-extension/README.md +++ b/examples/chain-extension/README.md @@ -6,7 +6,7 @@ From the perspective of a contract implementation or writing tests there is noth The thing that `drink` makes easier for you is combining an arbitrary chain extension with `drink`'s `MinimalRuntime`. By simply calling: ```rust -create_minimal_runtime!( +create_minimal_sandbox!( RuntimeWithCustomChainExtension, path::to::MyCustomChainExtension ); diff --git a/examples/chain-extension/src/lib.rs b/examples/chain-extension/src/lib.rs index 0ef3fc3..5125594 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!( - RuntimeWithCE, + 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(runtime = RuntimeWithCE)] + #[drink::test(config = SandboxWithCE)] fn we_can_test_chain_extension(mut session: Session) -> Result<(), Box> { let result: u32 = session .deploy_bundle_and( diff --git a/examples/cross-contract-call-tracing/lib.rs b/examples/cross-contract-call-tracing/lib.rs index 0620326..fbcc37c 100644 --- a/examples/cross-contract-call-tracing/lib.rs +++ b/examples/cross-contract-call-tracing/lib.rs @@ -12,6 +12,7 @@ mod contract { impl CrossCallingContract { #[ink(constructor)] + #[allow(clippy::new_without_default)] pub fn new() -> Self { Self {} } diff --git a/examples/flipper/Cargo.toml b/examples/flipper/Cargo.toml index 31826d0..f733a6c 100755 --- a/examples/flipper/Cargo.toml +++ b/examples/flipper/Cargo.toml @@ -15,6 +15,12 @@ scale-info = { version = "2.6", default-features = false, features = ["derive"], [dev-dependencies] drink = { path = "../../drink" } +# testing custom runtime +frame-support = "28.0.0" +frame-system = "28.0.0" +pallet-contracts-mock-network = "3.0.0" + + [lib] path = "lib.rs" diff --git a/examples/flipper/lib.rs b/examples/flipper/lib.rs index 75b51f6..b023f2e 100755 --- a/examples/flipper/lib.rs +++ b/examples/flipper/lib.rs @@ -71,4 +71,36 @@ mod tests { Ok(()) } + + + + use pallet_contracts_mock_network::{ALICE, parachain::Runtime as ParachainRuntime}; + use drink::{impl_sandbox_config}; + + impl_sandbox_config!( + struct ParachainSandbox { + runtime: ParachainRuntime; + default_balance: 1_000_000_000_000_000; + default_actor: ALICE; + } + ); + + #[drink::test(config = ParachainSandbox)] + 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/lib.rs b/examples/mocking/lib.rs index 8425c52..6c45c42 100755 --- a/examples/mocking/lib.rs +++ b/examples/mocking/lib.rs @@ -17,6 +17,7 @@ mod proxy { impl Proxy { #[ink(constructor)] + #[allow(clippy::new_without_default)] pub fn new() -> Self { Self {} } diff --git a/examples/runtime-interaction/lib.rs b/examples/runtime-interaction/lib.rs index 55fb2ad..b8fddfa 100644 --- a/examples/runtime-interaction/lib.rs +++ b/examples/runtime-interaction/lib.rs @@ -3,8 +3,8 @@ mod tests { use drink::{ pallet_balances, pallet_contracts, pallet_contracts::Determinism, - runtime::{minimal::RuntimeCall, MinimalRuntime, Runtime}, - AccountId32, Sandbox, + runtime::{minimal::RuntimeCall, MinimalRuntime}, + AccountId32, Sandbox, SandboxConfig, }; #[test] From 177c62ac69020604a8c4835ab4e674e99a4ab688 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 5 Feb 2024 17:56:07 +0100 Subject: [PATCH 02/13] restore package name --- Cargo.lock | 70 ++++++++++++++++++------------------- Cargo.toml | 6 ++-- drink/Cargo.toml | 2 +- drink/test-macro/Cargo.toml | 2 +- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0159b63..931e5e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1325,34 +1325,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] -name = "drink-cli" -version = "0.8.10" -dependencies = [ - "anyhow", - "clap", - "contract-build", - "contract-transcode", - "crossterm 0.26.1", - "drink-next", - "pallet-contracts-next", - "ratatui", - "sp-core", - "thiserror", -] - -[[package]] -name = "drink-next" -version = "0.8.10" +name = "drink" +version = "0.8.7" dependencies = [ "contract-metadata", "contract-transcode", - "drink-test-macro-next", + "drink-test-macro", "frame-metadata", "frame-support", "frame-system", "pallet-balances", - "pallet-contracts-next", - "pallet-contracts-uapi-next", + "pallet-contracts", + "pallet-contracts-uapi", "pallet-timestamp", "parity-scale-codec", "parity-scale-codec-derive", @@ -1366,8 +1350,24 @@ dependencies = [ ] [[package]] -name = "drink-test-macro-next" -version = "0.8.10" +name = "drink-cli" +version = "0.8.7" +dependencies = [ + "anyhow", + "clap", + "contract-build", + "contract-transcode", + "crossterm 0.26.1", + "drink", + "pallet-contracts", + "ratatui", + "sp-core", + "thiserror", +] + +[[package]] +name = "drink-test-macro" +version = "0.8.7" dependencies = [ "cargo_metadata", "contract-build", @@ -2951,10 +2951,10 @@ dependencies = [ ] [[package]] -name = "pallet-contracts-next" -version = "27.0.1" +name = "pallet-contracts" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe236f281eb1a31c1f869cc17e2ddbd96fcd8ee331ae6f329923780d0bfdf6" +checksum = "1c24580e4e9b9c000f62be094e1be4cd92cf1e0b5ec54b9b6fb78cc6ed8f3efc" dependencies = [ "bitflags 1.3.2", "environmental", @@ -2965,7 +2965,7 @@ dependencies = [ "log", "pallet-balances", "pallet-contracts-proc-macro", - "pallet-contracts-uapi-next", + "pallet-contracts-uapi", "parity-scale-codec", "rand", "scale-info", @@ -2994,10 +2994,10 @@ dependencies = [ ] [[package]] -name = "pallet-contracts-uapi-next" +name = "pallet-contracts-uapi" version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306384c85b1e7629cb37b245afcfb9ba067eb575a345f46e1e0fb720248b16e6" +checksum = "a992d0815b9dc36acbe0800b05b4f875398bb9d9b1aa15c8b1afdcb87f66df2a" dependencies = [ "bitflags 1.3.2", "parity-scale-codec", @@ -3202,15 +3202,15 @@ dependencies = [ [[package]] name = "polkavm-common" -version = "0.5.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b4e215c80fe876147f3d58158d5dfeae7dabdd6047e175af77095b78d0035c" +checksum = "fecd2caacfc4a7ee34243758dd7348859e6dec73f5e5df059890f5742ee46f0e" [[package]] name = "polkavm-derive" -version = "0.5.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6380dbe1fb03ecc74ad55d841cfc75480222d153ba69ddcb00977866cbdabdb8" +checksum = "db65a500d4adf574893c726ae365e37e4fbb7f2cbd403f6eaa1b665457456adc" dependencies = [ "polkavm-derive-impl", "syn 2.0.48", @@ -3218,9 +3218,9 @@ dependencies = [ [[package]] name = "polkavm-derive-impl" -version = "0.5.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc8211b3365bbafb2fb32057d68b0e1ca55d079f5cf6f9da9b98079b94b3987d" +checksum = "c99f4e7a9ff434ef9c885b874c99d824c3a5693bf5e3e8569bb1d2245a8c1b7f" dependencies = [ "polkavm-common", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 98b70ba..797b858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ homepage = "https://github.com/Cardinal-Cryptography/drink" license = "Apache-2.0" readme = "README.md" repository = "https://github.com/Cardinal-Cryptography/drink" -version = "0.8.10" +version = "0.8.7" [workspace.dependencies] anyhow = { version = "1.0.71" } @@ -57,5 +57,5 @@ sp-runtime-interface = { version = "24.0.0" } # Local dependencies -drink = { version = "0.8.10", path = "drink" } -drink-test-macro = { package = "drink-test-macro-next", version = "0.8.10", path = "drink/test-macro" } +drink = { version = "0.8.7", path = "drink" } +drink-test-macro = { package = "drink-test-macro", version = "0.8.7", path = "drink/test-macro" } diff --git a/drink/Cargo.toml b/drink/Cargo.toml index f3a5902..c6d7a42 100644 --- a/drink/Cargo.toml +++ b/drink/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "drink-next" +name = "drink" authors.workspace = true edition.workspace = true homepage.workspace = true diff --git a/drink/test-macro/Cargo.toml b/drink/test-macro/Cargo.toml index d94e0af..f308883 100644 --- a/drink/test-macro/Cargo.toml +++ b/drink/test-macro/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "drink-test-macro-next" +name = "drink-test-macro" authors.workspace = true edition.workspace = true homepage.workspace = true From e92da18160cd2755e1b625a5849f01898409efe6 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 5 Feb 2024 18:05:05 +0100 Subject: [PATCH 03/13] restore name --- drink/src/runtime/minimal.rs | 20 ++++++++++++++++---- examples/chain-extension/Cargo.toml | 2 +- examples/chain-extension/README.md | 2 +- examples/chain-extension/src/lib.rs | 4 ++-- examples/flipper/lib.rs | 2 -- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/drink/src/runtime/minimal.rs b/drink/src/runtime/minimal.rs index be776f5..54ac2c0 100644 --- a/drink/src/runtime/minimal.rs +++ b/drink/src/runtime/minimal.rs @@ -1,5 +1,17 @@ #![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. +/// +/// Example usage: +/// The following code will generate an implementation of `drink::SandboxConfig` for `CustomRuntime` +/// ```no_run +/// impl_sandbox_config!( +/// struct CustomSandbox { +/// runtime: CustomRuntime; +/// default_balance: 1_000_000_000_000_000; +/// default_actor: ALICE; +/// } +/// ); #[macro_export] macro_rules! impl_sandbox_config { ( @@ -79,11 +91,11 @@ macro_rules! impl_sandbox_config { /// 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::Runtime`. +/// The new macro will automatically implement `drink::SandboxConfig`. #[macro_export] -macro_rules! create_minimal_sandbox { +macro_rules! create_minimal_runtime { ($name:ident) => { - create_minimal_sandbox!($name, ()); + create_minimal_runtime!($name, ()); }; ($name:ident, $chain_extension: ty) => { @@ -222,4 +234,4 @@ pub use construct_runtime::{ }; } -create_minimal_sandbox!(MinimalRuntime); +create_minimal_runtime!(MinimalRuntime); diff --git a/examples/chain-extension/Cargo.toml b/examples/chain-extension/Cargo.toml index 5e42178..87a7da6 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_sandbox!` macro, unfortunately you need to +# If you are creating a custom runtime with `drink::create_minimal_runtime!` macro, unfortunately you need to # include these dependencies manually. Versions should match the ones in `Cargo.toml` of the `drink` crate. frame-support = "28.0.0" frame-system = "28.0.0" diff --git a/examples/chain-extension/README.md b/examples/chain-extension/README.md index 172e100..47303de 100644 --- a/examples/chain-extension/README.md +++ b/examples/chain-extension/README.md @@ -6,7 +6,7 @@ From the perspective of a contract implementation or writing tests there is noth The thing that `drink` makes easier for you is combining an arbitrary chain extension with `drink`'s `MinimalRuntime`. By simply calling: ```rust -create_minimal_sandbox!( +create_minimal_runtime!( RuntimeWithCustomChainExtension, path::to::MyCustomChainExtension ); diff --git a/examples/chain-extension/src/lib.rs b/examples/chain-extension/src/lib.rs index 5125594..f527043 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_sandbox, + create_minimal_runtime, session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT}, }; @@ -46,7 +46,7 @@ mod tests { enum BundleProvider {} // We can inject arbitrary chain extension into the minimal runtime as follows: - create_minimal_sandbox!( + create_minimal_runtime!( SandboxWithCE, crate::chain_extension_runtime_side::StakingExtension ); diff --git a/examples/flipper/lib.rs b/examples/flipper/lib.rs index b023f2e..9438b25 100755 --- a/examples/flipper/lib.rs +++ b/examples/flipper/lib.rs @@ -72,8 +72,6 @@ mod tests { Ok(()) } - - use pallet_contracts_mock_network::{ALICE, parachain::Runtime as ParachainRuntime}; use drink::{impl_sandbox_config}; From 2d7833c51afa22a54cb74627e18c98abb8993db5 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 5 Feb 2024 18:26:43 +0100 Subject: [PATCH 04/13] Nit fixes --- drink/src/runtime/minimal.rs | 11 ----------- drink/src/sandbox.rs | 8 ++++---- drink/src/sandbox/contract_api.rs | 4 ++-- drink/src/sandbox/contracts_api.rs | 4 ++-- .../sandbox/{sandbox_core.rs => sandbox_config.rs} | 0 drink/src/sandbox/system_api.rs | 2 +- examples/flipper/lib.rs | 2 +- 7 files changed, 10 insertions(+), 21 deletions(-) rename drink/src/sandbox/{sandbox_core.rs => sandbox_config.rs} (100%) diff --git a/drink/src/runtime/minimal.rs b/drink/src/runtime/minimal.rs index 54ac2c0..6f3cda9 100644 --- a/drink/src/runtime/minimal.rs +++ b/drink/src/runtime/minimal.rs @@ -1,17 +1,6 @@ #![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. -/// -/// Example usage: -/// The following code will generate an implementation of `drink::SandboxConfig` for `CustomRuntime` -/// ```no_run -/// impl_sandbox_config!( -/// struct CustomSandbox { -/// runtime: CustomRuntime; -/// default_balance: 1_000_000_000_000_000; -/// default_actor: ALICE; -/// } -/// ); #[macro_export] macro_rules! impl_sandbox_config { ( diff --git a/drink/src/sandbox.rs b/drink/src/sandbox.rs index 8095282..882598d 100644 --- a/drink/src/sandbox.rs +++ b/drink/src/sandbox.rs @@ -1,7 +1,7 @@ //! A sandboxed runtime. -mod sandbox_core; -pub use sandbox_core::SandboxConfig; +mod sandbox_config; +pub use sandbox_config::SandboxConfig; pub mod balance_api; pub mod contract_api; pub mod runtime_api; @@ -14,12 +14,12 @@ use sp_externalities::Extension; use sp_io::TestExternalities; /// A sandboxed runtime. -pub struct Sandbox { +pub struct Sandbox { externalities: TestExternalities, _phantom: std::marker::PhantomData, } -impl Sandbox { +impl Sandbox { /// Execute the given closure with the inner externallities. /// /// Returns the result of the given closure. diff --git a/drink/src/sandbox/contract_api.rs b/drink/src/sandbox/contract_api.rs index a709f9f..86426a3 100644 --- a/drink/src/sandbox/contract_api.rs +++ b/drink/src/sandbox/contract_api.rs @@ -29,7 +29,7 @@ where /// * `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)] + #[allow(clippy::type_complexity, clippy::too_many_arguments)] pub fn deploy_contract( &mut self, contract_bytes: Vec, @@ -70,7 +70,7 @@ where /// * `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)] + #[allow(clippy::type_complexity, clippy::too_many_arguments)] pub fn instantiate_contract( &mut self, code_hash: Vec, diff --git a/drink/src/sandbox/contracts_api.rs b/drink/src/sandbox/contracts_api.rs index 5182577..73c4285 100644 --- a/drink/src/sandbox/contracts_api.rs +++ b/drink/src/sandbox/contracts_api.rs @@ -9,12 +9,12 @@ use pallet_contracts::{ }; use parity_scale_codec::Decode as _; -use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox}; +use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox, SandboxApi}; type BalanceOf = <::Currency as Inspect>>::Balance; -pub trait ContractsApi: crate::SandboxApi { +pub trait ContractsApi: SandboxApi { /// Interface for `bare_instantiate` contract call with a simultaneous upload. /// /// # Arguments diff --git a/drink/src/sandbox/sandbox_core.rs b/drink/src/sandbox/sandbox_config.rs similarity index 100% rename from drink/src/sandbox/sandbox_core.rs rename to drink/src/sandbox/sandbox_config.rs diff --git a/drink/src/sandbox/system_api.rs b/drink/src/sandbox/system_api.rs index 315cd29..6cf20d6 100644 --- a/drink/src/sandbox/system_api.rs +++ b/drink/src/sandbox/system_api.rs @@ -9,7 +9,7 @@ use crate::{EventRecordOf, RuntimeCall}; impl Sandbox { /// Return the current height of the chain. pub fn block_number(&mut self) -> BlockNumberFor { - self.execute_with(|| frame_system::Pallet::::block_number()) + self.execute_with(frame_system::Pallet::::block_number) } /// Return the events of the current block so far. diff --git a/examples/flipper/lib.rs b/examples/flipper/lib.rs index 9438b25..34d20e4 100755 --- a/examples/flipper/lib.rs +++ b/examples/flipper/lib.rs @@ -73,7 +73,7 @@ mod tests { } use pallet_contracts_mock_network::{ALICE, parachain::Runtime as ParachainRuntime}; - use drink::{impl_sandbox_config}; + use drink::impl_sandbox_config; impl_sandbox_config!( struct ParachainSandbox { From 6e034720660839a211d98fd06ae846e9f401a47c Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 5 Feb 2024 20:21:56 +0100 Subject: [PATCH 05/13] clean up --- drink-cli/src/app_state/print.rs | 2 +- drink/src/runtime.rs | 2 +- drink/src/sandbox.rs | 2 +- drink/src/sandbox/balance_api.rs | 6 +- drink/src/sandbox/contract_api.rs | 309 ------------------------- drink/src/sandbox/contracts_api.rs | 83 ++++--- examples/quick-start-with-drink/lib.rs | 2 +- 7 files changed, 55 insertions(+), 351 deletions(-) delete mode 100644 drink/src/sandbox/contract_api.rs diff --git a/drink-cli/src/app_state/print.rs b/drink-cli/src/app_state/print.rs index 95f69c2..a1b26e5 100644 --- a/drink-cli/src/app_state/print.rs +++ b/drink-cli/src/app_state/print.rs @@ -1,4 +1,4 @@ -use drink::contract_api::decode_debug_buffer; +use drink::contracts_api::decode_debug_buffer; use pallet_contracts::ContractResult; use ratatui::{ style::{Color, Modifier, Style}, diff --git a/drink/src/runtime.rs b/drink/src/runtime.rs index 7291381..2ba1039 100644 --- a/drink/src/runtime.rs +++ b/drink/src/runtime.rs @@ -2,9 +2,9 @@ //! `drink` with any runtime that implements the `Runtime` trait. pub mod minimal; +pub mod pallet_contracts_debugging; pub use frame_metadata::RuntimeMetadataPrefixed; pub use minimal::MinimalRuntime; -pub mod pallet_contracts_debugging; /// The type of an account identifier. pub type AccountIdFor = ::AccountId; diff --git a/drink/src/sandbox.rs b/drink/src/sandbox.rs index 882598d..b9f8b5b 100644 --- a/drink/src/sandbox.rs +++ b/drink/src/sandbox.rs @@ -3,7 +3,7 @@ mod sandbox_config; pub use sandbox_config::SandboxConfig; pub mod balance_api; -pub mod contract_api; +pub mod contracts_api; pub mod runtime_api; pub mod system_api; pub mod timestamp_api; diff --git a/drink/src/sandbox/balance_api.rs b/drink/src/sandbox/balance_api.rs index 104c151..9740abf 100644 --- a/drink/src/sandbox/balance_api.rs +++ b/drink/src/sandbox/balance_api.rs @@ -2,11 +2,11 @@ use frame_support::{sp_runtime::DispatchError, traits::fungible::Mutate}; use super::Sandbox; -use crate::{runtime::AccountIdFor, BalanceOf}; +use crate::{runtime::AccountIdFor, BalanceOf, SandboxConfig}; -impl Sandbox +impl Sandbox where - Config::Runtime: crate::pallet_balances::Config, + Config::Runtime: pallet_balances::Config, { /// Mint tokens to an account. /// diff --git a/drink/src/sandbox/contract_api.rs b/drink/src/sandbox/contract_api.rs deleted file mode 100644 index 86426a3..0000000 --- a/drink/src/sandbox/contract_api.rs +++ /dev/null @@ -1,309 +0,0 @@ -//! Contracts API for the sandbox. -use std::ops::Not; - -use frame_support::{traits::fungible::Inspect, weights::Weight}; -use frame_system::Config as SysConfig; -use pallet_contracts::{ - Code, CodeUploadResult, CollectEvents, ContractExecResult, ContractInstantiateResult, - DebugInfo, Determinism, -}; -use parity_scale_codec::Decode as _; - -use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox}; - -type BalanceOf = - <::Currency as Inspect>>::Balance; - -impl Sandbox -where - Config::Runtime: pallet_contracts::Config, -{ - /// Interface for `bare_instantiate` contract call with a simultaneous upload. - /// - /// # Arguments - /// - /// * `contract_bytes` - The contract code. - /// * `value` - The number of tokens to be transferred to the contract. - /// * `data` - The input data to be passed to the contract (including constructor name). - /// * `salt` - The salt to be used for contract address derivation. - /// * `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)] - pub fn deploy_contract( - &mut self, - contract_bytes: Vec, - value: BalanceOf, - data: Vec, - salt: Vec, - origin: AccountIdFor, - gas_limit: Weight, - storage_deposit_limit: Option>, - ) -> ContractInstantiateResult< - AccountIdFor, - BalanceOf, - EventRecordOf, - > { - 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, - ) - }) - } - - /// Interface for `bare_instantiate` contract call for a previously uploaded contract. - /// - /// # Arguments - /// - /// * `code_hash` - The code hash of the contract to instantiate. - /// * `value` - The number of tokens to be transferred to the contract. - /// * `data` - The input data to be passed to the contract (including constructor name). - /// * `salt` - The salt to be used for contract address derivation. - /// * `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)] - pub fn instantiate_contract( - &mut self, - code_hash: Vec, - value: BalanceOf, - data: Vec, - salt: Vec, - origin: AccountIdFor, - gas_limit: Weight, - storage_deposit_limit: Option>, - ) -> ContractInstantiateResult< - AccountIdFor, - BalanceOf, - EventRecordOf, - > { - let mut code_hash = &code_hash[..]; - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( - origin, - value, - gas_limit, - storage_deposit_limit, - Code::Existing( - ::Hash::decode(&mut code_hash) - .expect("Invalid code hash"), - ), - data, - salt, - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - ) - }) - } - - /// 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( - &mut self, - contract_bytes: Vec, - origin: AccountIdFor, - storage_deposit_limit: Option>, - determinism: Determinism, - ) -> CodeUploadResult<::Hash, BalanceOf> - { - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_upload_code( - origin, - contract_bytes, - storage_deposit_limit, - determinism, - ) - }) - } - - /// 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( - &mut self, - address: AccountIdFor, - value: BalanceOf, - data: Vec, - origin: AccountIdFor, - gas_limit: Weight, - storage_deposit_limit: Option>, - determinism: Determinism, - ) -> ContractExecResult, EventRecordOf> { - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_call( - origin, - address, - value, - gas_limit, - storage_deposit_limit, - data, - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - determinism, - ) - }) - } -} - -/// Converts bytes to a '\n'-split string, ignoring empty lines. -pub fn decode_debug_buffer(buffer: &[u8]) -> Vec { - let decoded = buffer.iter().map(|b| *b as char).collect::(); - decoded - .split('\n') - .filter_map(|s| s.is_empty().not().then_some(s.to_string())) - .collect() -} - -#[cfg(test)] -mod tests { - use frame_support::sp_runtime::traits::Hash; - use pallet_contracts::Origin; - - use super::*; - use crate::{ - minimal::RuntimeEvent, sandbox::SandboxConfig, session::NO_SALT, MinimalRuntime, - DEFAULT_GAS_LIMIT, - }; - - fn compile_module(contract_name: &str) -> Vec { - let path = [ - std::env::var("CARGO_MANIFEST_DIR") - .as_deref() - .unwrap_or("drink"), - "/test-resources/", - contract_name, - ".wat", - ] - .concat(); - wat::parse_file(path).expect("Failed to parse wat file") - } - - #[test] - fn can_upload_code() { - let mut sandbox = Sandbox::::new().unwrap(); - let wasm_binary = compile_module("dummy"); - let hash = <::Hashing>::hash(&wasm_binary); - - let result = sandbox.upload_contract( - wasm_binary, - MinimalRuntime::default_actor(), - None, - Determinism::Enforced, - ); - - assert!(result.is_ok()); - assert_eq!(hash, result.unwrap().code_hash); - } - - #[test] - fn can_deploy_contract() { - let mut sandbox = Sandbox::::new().unwrap(); - let wasm_binary = compile_module("dummy"); - - let events_before = sandbox.events(); - assert!(events_before.is_empty()); - - let result = sandbox.deploy_contract( - wasm_binary, - 0, - vec![], - NO_SALT, - MinimalRuntime::default_actor(), - DEFAULT_GAS_LIMIT, - None, - ); - assert!(result.result.is_ok()); - assert!(!result.result.unwrap().result.did_revert()); - - let events = result.events.expect("Drink should collect events"); - let event_count = events.len(); - let instantiation_event = events[event_count - 2].clone(); - assert!(matches!( - instantiation_event.event, - 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 { .. } - ) - )); - } - - #[test] - fn can_call_contract() { - let mut sandbox = Sandbox::::new().unwrap(); - let actor = MinimalRuntime::default_actor(); - let wasm_binary = compile_module("dummy"); - - let result = sandbox.deploy_contract( - wasm_binary, - 0, - vec![], - NO_SALT, - actor.clone(), - DEFAULT_GAS_LIMIT, - None, - ); - - let contract_address = result - .result - .expect("Contract should be deployed") - .account_id; - - sandbox.reset_events(); - - let result = sandbox.call_contract( - contract_address.clone(), - 0, - vec![], - actor.clone(), - DEFAULT_GAS_LIMIT, - None, - Determinism::Enforced, - ); - assert!(result.result.is_ok()); - assert!(!result.result.unwrap().did_revert()); - - let events = result.events.expect("Drink should collect events"); - assert_eq!(events.len(), 2); - - assert_eq!( - events[0].event, - 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 { - contract: contract_address, - caller: Origin::Signed(actor), - }), - ); - } -} diff --git a/drink/src/sandbox/contracts_api.rs b/drink/src/sandbox/contracts_api.rs index 73c4285..86426a3 100644 --- a/drink/src/sandbox/contracts_api.rs +++ b/drink/src/sandbox/contracts_api.rs @@ -2,19 +2,22 @@ use std::ops::Not; use frame_support::{traits::fungible::Inspect, weights::Weight}; -use frame_system::Config; +use frame_system::Config as SysConfig; use pallet_contracts::{ Code, CodeUploadResult, CollectEvents, ContractExecResult, ContractInstantiateResult, DebugInfo, Determinism, }; use parity_scale_codec::Decode as _; -use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox, SandboxApi}; +use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox}; type BalanceOf = <::Currency as Inspect>>::Balance; -pub trait ContractsApi: SandboxApi { +impl Sandbox +where + Config::Runtime: pallet_contracts::Config, +{ /// Interface for `bare_instantiate` contract call with a simultaneous upload. /// /// # Arguments @@ -26,19 +29,23 @@ pub trait ContractsApi: SandboxApi { /// * `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)] - fn deploy_contract( + #[allow(clippy::type_complexity, clippy::too_many_arguments)] + pub 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>, - ) -> ContractInstantiateResult, BalanceOf, EventRecordOf> { - self.externalities().execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult< + AccountIdFor, + BalanceOf, + EventRecordOf, + > { + self.externalities.execute_with(|| { + pallet_contracts::Pallet::::bare_instantiate( origin, value, gas_limit, @@ -63,26 +70,31 @@ pub trait ContractsApi: SandboxApi { /// * `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)] - fn instantiate_contract( + #[allow(clippy::type_complexity, clippy::too_many_arguments)] + pub 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, BalanceOf, EventRecordOf> { + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult< + AccountIdFor, + BalanceOf, + EventRecordOf, + > { let mut code_hash = &code_hash[..]; - self.externalities().execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( + self.externalities.execute_with(|| { + pallet_contracts::Pallet::::bare_instantiate( origin, value, gas_limit, storage_deposit_limit, Code::Existing( - ::Hash::decode(&mut code_hash).expect("Invalid code hash"), + ::Hash::decode(&mut code_hash) + .expect("Invalid code hash"), ), data, salt, @@ -99,15 +111,16 @@ pub trait ContractsApi: SandboxApi { /// * `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( + pub 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.externalities.execute_with(|| { + pallet_contracts::Pallet::::bare_upload_code( origin, contract_bytes, storage_deposit_limit, @@ -127,18 +140,18 @@ pub trait ContractsApi: SandboxApi { /// * `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)] - fn call_contract( + pub 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, - ) -> ContractExecResult, EventRecordOf> { - self.externalities().execute_with(|| { - pallet_contracts::Pallet::::bare_call( + ) -> ContractExecResult, EventRecordOf> { + self.externalities.execute_with(|| { + pallet_contracts::Pallet::::bare_call( origin, address, value, @@ -169,7 +182,7 @@ mod tests { use super::*; use crate::{ - minimal::RuntimeEvent, runtime::Runtime, session::NO_SALT, MinimalRuntime, + minimal::RuntimeEvent, sandbox::SandboxConfig, session::NO_SALT, MinimalRuntime, DEFAULT_GAS_LIMIT, }; diff --git a/examples/quick-start-with-drink/lib.rs b/examples/quick-start-with-drink/lib.rs index b15e6b8..557fa25 100644 --- a/examples/quick-start-with-drink/lib.rs +++ b/examples/quick-start-with-drink/lib.rs @@ -43,7 +43,7 @@ mod flipper { #[cfg(test)] mod tests { use drink::{ - contract_api::decode_debug_buffer, + contracts_api::decode_debug_buffer, session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT}, }; From 270a7ce80440c0e72bd187c575c28c7c8dfba71e Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 5 Feb 2024 20:25:33 +0100 Subject: [PATCH 06/13] fix extraneous dep --- examples/chain-extension/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/chain-extension/Cargo.toml b/examples/chain-extension/Cargo.toml index 87a7da6..d3975b5 100644 --- a/examples/chain-extension/Cargo.toml +++ b/examples/chain-extension/Cargo.toml @@ -22,7 +22,6 @@ drink = { path = "../../drink" } # include these dependencies manually. Versions should match the ones in `Cargo.toml` of the `drink` crate. frame-support = "28.0.0" frame-system = "28.0.0" -pallet-balances = "28.0.0" [features] default = ["std"] From 7d41029633df0737af42f43081539592f6ddac37 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 5 Feb 2024 20:35:32 +0100 Subject: [PATCH 07/13] fix missing :$crate --- drink/src/runtime/minimal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drink/src/runtime/minimal.rs b/drink/src/runtime/minimal.rs index 6f3cda9..8a4944f 100644 --- a/drink/src/runtime/minimal.rs +++ b/drink/src/runtime/minimal.rs @@ -122,7 +122,7 @@ mod construct_runtime { type Block = $crate::frame_system::mocking::MockBlockU32<$name>; type Version = (); type BlockHashCount = ConstU32<250>; - type AccountData = $crate::pallet_balances::AccountData<<$name as pallet_balances::Config>::Balance>; + type AccountData = $crate::pallet_balances::AccountData<<$name as $crate::pallet_balances::Config>::Balance>; } // ------------ Configure pallet balances ------------------------------------------------------ From 606bea65f7dea275225c61c39f592ecc3f31afee Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 5 Feb 2024 20:57:09 +0100 Subject: [PATCH 08/13] fix --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 797b858..82f1f16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,4 +58,4 @@ sp-runtime-interface = { version = "24.0.0" } # Local dependencies drink = { version = "0.8.7", path = "drink" } -drink-test-macro = { package = "drink-test-macro", version = "0.8.7", path = "drink/test-macro" } +drink-test-macro = { version = "0.8.7", path = "drink/test-macro" } From 7d2f8e6bc70a34f221c4ec4b4f772cbdad80a84e Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 5 Feb 2024 20:59:40 +0100 Subject: [PATCH 09/13] nit --- drink/src/sandbox/system_api.rs | 4 ++-- drink/src/sandbox/timestamp_api.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drink/src/sandbox/system_api.rs b/drink/src/sandbox/system_api.rs index 6cf20d6..8e29e67 100644 --- a/drink/src/sandbox/system_api.rs +++ b/drink/src/sandbox/system_api.rs @@ -4,9 +4,9 @@ use frame_support::sp_runtime::{traits::Dispatchable, DispatchResultWithInfo}; use frame_system::pallet_prelude::BlockNumberFor; use super::Sandbox; -use crate::{EventRecordOf, RuntimeCall}; +use crate::{EventRecordOf, RuntimeCall, SandboxConfig}; -impl Sandbox { +impl Sandbox { /// Return the current height of the chain. pub fn block_number(&mut self) -> BlockNumberFor { self.execute_with(frame_system::Pallet::::block_number) diff --git a/drink/src/sandbox/timestamp_api.rs b/drink/src/sandbox/timestamp_api.rs index a17460e..8c34a4a 100644 --- a/drink/src/sandbox/timestamp_api.rs +++ b/drink/src/sandbox/timestamp_api.rs @@ -1,11 +1,11 @@ //! timestamp API for the sandbox. -use crate::Sandbox; +use crate::{Sandbox, SandboxConfig}; /// Generic Time type. type MomentOf = ::Moment; -impl Sandbox +impl Sandbox where Config::Runtime: pallet_timestamp::Config, { From 01a2ab8bfb7fc14d04a480b8f1ac703de6b29296 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Tue, 6 Feb 2024 11:19:23 +0100 Subject: [PATCH 10/13] Add optional visibility / attributes --- drink/src/runtime/minimal.rs | 6 ++++-- examples/flipper/lib.rs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drink/src/runtime/minimal.rs b/drink/src/runtime/minimal.rs index 8a4944f..c737d1a 100644 --- a/drink/src/runtime/minimal.rs +++ b/drink/src/runtime/minimal.rs @@ -4,13 +4,15 @@ #[macro_export] macro_rules! impl_sandbox_config { ( - struct $name:ident { + $( #[ $attr:meta ] )* + $vis:vis struct $name:ident { runtime: $runtime:tt; default_balance: $default_balance:expr; default_actor: $default_actor:expr; } ) => { - struct $name; + $( #[ $attr ] )* + $vis struct $name; impl_sandbox_config!($name, $runtime, $default_balance, $default_actor); }; ( diff --git a/examples/flipper/lib.rs b/examples/flipper/lib.rs index 34d20e4..8ae6ab8 100755 --- a/examples/flipper/lib.rs +++ b/examples/flipper/lib.rs @@ -76,14 +76,14 @@ mod tests { use drink::impl_sandbox_config; impl_sandbox_config!( - struct ParachainSandbox { + struct ParachainConfig { runtime: ParachainRuntime; default_balance: 1_000_000_000_000_000; default_actor: ALICE; } ); - #[drink::test(config = ParachainSandbox)] + #[drink::test(config = ParachainConfig)] fn test_flipping_with_custom_runtime(mut session: Session) -> Result<(), Box> { let contract = BundleProvider::Flipper.bundle()?; let init_value: bool = session From 3cdac7da69c05bb874857c78c8280c530fd60e65 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Tue, 6 Feb 2024 12:49:52 +0100 Subject: [PATCH 11/13] Bump patch --- Cargo.lock | 6 +++--- Cargo.toml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 931e5e6..8fd60df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1326,7 +1326,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "drink" -version = "0.8.7" +version = "0.9.0" dependencies = [ "contract-metadata", "contract-transcode", @@ -1351,7 +1351,7 @@ dependencies = [ [[package]] name = "drink-cli" -version = "0.8.7" +version = "0.9.0" dependencies = [ "anyhow", "clap", @@ -1367,7 +1367,7 @@ dependencies = [ [[package]] name = "drink-test-macro" -version = "0.8.7" +version = "0.9.0" dependencies = [ "cargo_metadata", "contract-build", diff --git a/Cargo.toml b/Cargo.toml index 82f1f16..fb43627 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ homepage = "https://github.com/Cardinal-Cryptography/drink" license = "Apache-2.0" readme = "README.md" repository = "https://github.com/Cardinal-Cryptography/drink" -version = "0.8.7" +version = "0.9.0" [workspace.dependencies] anyhow = { version = "1.0.71" } @@ -57,5 +57,5 @@ sp-runtime-interface = { version = "24.0.0" } # Local dependencies -drink = { version = "0.8.7", path = "drink" } -drink-test-macro = { version = "0.8.7", path = "drink/test-macro" } +drink = { version = "0.9.0", path = "drink" } +drink-test-macro = { version = "0.9.0", path = "drink/test-macro" } From c8e6464a25bcbc94d83b3c126b53e67cb556d243 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 8 Feb 2024 12:38:11 +0100 Subject: [PATCH 12/13] Refactor Record & recording --- drink/src/session.rs | 58 ++++++++++++++------------ drink/src/session/record.rs | 81 ++++++++++--------------------------- 2 files changed, 53 insertions(+), 86 deletions(-) diff --git a/drink/src/session.rs b/drink/src/session.rs index 4291f51..7164a17 100644 --- a/drink/src/session.rs +++ b/drink/src/session.rs @@ -12,7 +12,7 @@ use contract_transcode::ContractMessageTranscoder; use frame_support::{traits::fungible::Inspect, weights::Weight}; use pallet_contracts::Determinism; use parity_scale_codec::Decode; -pub use record::Record; +pub use record::{EventBatch, Record}; use crate::{ mock::MockRegistry, @@ -142,7 +142,7 @@ where determinism: Determinism, transcoders: TranscoderRegistry>, - record: Record, + record: Record, mocks: Arc>>>, } @@ -240,7 +240,7 @@ where } /// Returns a reference to the record of the session. - pub fn record(&self) -> &Record { + pub fn record(&self) -> &Record { &self.record } @@ -270,6 +270,13 @@ where ) .map(|_| self) } + fn record_events(&mut self, recording: impl FnOnce(&mut Self) -> T) -> T { + let start = self.sandbox.events().len(); + let result = recording(self); + let events = self.sandbox.events()[start..].to_vec(); + self.record.push_event_batches(events); + result + } /// Deploys a contract with a given constructor, arguments, salt and endowment. In case of /// success, returns the address of the deployed contract. @@ -286,17 +293,17 @@ where .encode(constructor, args) .map_err(|err| SessionError::Encoding(err.to_string()))?; - self.record.start_recording_events(&mut self.sandbox); - let result = self.sandbox.deploy_contract( - contract_bytes, - endowment.unwrap_or_default(), - data, - salt, - self.actor.clone(), - self.gas_limit, - None, - ); - self.record.stop_recording_events(&mut self.sandbox); + let result = self.record_events(|session| { + session.sandbox.deploy_contract( + contract_bytes, + endowment.unwrap_or_default(), + data, + salt, + session.actor.clone(), + session.gas_limit, + None, + ) + }); let ret = match &result.result { Ok(exec_result) if exec_result.result.did_revert() => { @@ -305,7 +312,6 @@ where Ok(exec_result) => { let address = exec_result.account_id.clone(); self.record.push_deploy_return(address.clone()); - self.transcoders.register(address.clone(), transcoder); Ok(address) @@ -466,17 +472,17 @@ where .encode(message, args) .map_err(|err| SessionError::Encoding(err.to_string()))?; - self.record.start_recording_events(&mut self.sandbox); - let result = self.sandbox.call_contract( - address, - endowment.unwrap_or_default(), - data, - self.actor.clone(), - self.gas_limit, - None, - self.determinism, - ); - self.record.stop_recording_events(&mut self.sandbox); + let result = self.record_events(|session| { + session.sandbox.call_contract( + address, + endowment.unwrap_or_default(), + data, + session.actor.clone(), + session.gas_limit, + None, + session.determinism, + ) + }); let ret = match &result.result { Ok(exec_result) if exec_result.did_revert() => Err(SessionError::CallReverted), diff --git a/drink/src/session/record.rs b/drink/src/session/record.rs index f6cd4c8..e99222f 100644 --- a/drink/src/session/record.rs +++ b/drink/src/session/record.rs @@ -7,7 +7,7 @@ use crate::{ errors::MessageResult, runtime::{minimal::RuntimeEvent, AccountIdFor, MinimalRuntime}, session::{error::SessionError, BalanceOf}, - EventRecordOf, Sandbox, SandboxConfig, + EventRecordOf, }; type ContractInstantiateResult = @@ -21,38 +21,23 @@ type ContractExecResult = pallet_contracts::ContractExecResult, /// By `result` we mean the full result (enriched with some context information) of the contract /// interaction, like `ContractExecResult`. By `return` we mean the return value of the contract /// execution, like a value returned from a message or the address of a newly instantiated contract. -pub struct Record -where - Config::Runtime: pallet_contracts::Config, -{ +pub struct Record { /// The results of contract instantiation. - deploy_results: Vec>, + deploy_results: Vec>, /// The return values of contract instantiation (i.e. the addresses of the newly instantiated /// contracts). - deploy_returns: Vec>, + deploy_returns: Vec>, /// The results of contract calls. - call_results: Vec>, + call_results: Vec>, /// The return values of contract calls (in the SCALE-encoded form). call_returns: Vec>, /// The events emitted by the contracts. - event_batches: Vec>, - - /// Because `drink` normally doesn't have a continuous block production, everything implicitly - /// happens within a single block (unless user explicitly trigger a new block). This means that - /// all runtime events (from consecutive transactions) are stacked up in a common buffer. - /// `Record` is capable of recording only the events that happened during a single transaction - /// by remembering the number of events that were already in the buffer before the transaction - /// started. However, this is must be explicitly enabled by calling `start_recording_events` - /// before the transaction and `stop_recording_events` after the transaction. - block_events_so_far: Option, + event_batches: Vec>, } -impl Default for Record -where - Config::Runtime: pallet_contracts::Config, -{ +impl Default for Record { fn default() -> Self { Self { deploy_results: Vec::new(), @@ -60,28 +45,21 @@ where call_results: Vec::new(), call_returns: Vec::new(), event_batches: Vec::new(), - block_events_so_far: None, } } } // API for `Session` to record results and events related to contract interaction. -impl Record -where - Config::Runtime: pallet_contracts::Config, -{ - pub(super) fn push_deploy_result( - &mut self, - result: ContractInstantiateResult, - ) { +impl Record { + pub(super) fn push_deploy_result(&mut self, result: ContractInstantiateResult) { self.deploy_results.push(result); } - pub(super) fn push_deploy_return(&mut self, return_value: AccountIdFor) { + pub(super) fn push_deploy_return(&mut self, return_value: AccountIdFor) { self.deploy_returns.push(return_value); } - pub(super) fn push_call_result(&mut self, result: ContractExecResult) { + pub(super) fn push_call_result(&mut self, result: ContractExecResult) { self.call_results.push(result); } @@ -89,60 +67,43 @@ where self.call_returns.push(return_value); } - pub(super) fn start_recording_events(&mut self, sandbox: &mut Sandbox) { - assert!( - self.block_events_so_far.is_none(), - "Already recording events" - ); - self.block_events_so_far = Some(sandbox.events().len()); - } - - pub(super) fn stop_recording_events(&mut self, sandbox: &mut Sandbox) { - let start = self - .block_events_so_far - .take() - .expect("Not recording events"); - let end = sandbox.events().len(); - let events = sandbox.events()[start..end].to_vec(); + pub(super) fn push_event_batches(&mut self, events: Vec>) { self.event_batches.push(EventBatch { events }); } } // API for the end user. -impl Record -where - Config::Runtime: pallet_contracts::Config, -{ +impl Record { /// Returns all the results of contract instantiations that happened during the session. - pub fn deploy_results(&self) -> &[ContractInstantiateResult] { + pub fn deploy_results(&self) -> &[ContractInstantiateResult] { &self.deploy_results } /// Returns the last result of contract instantiation that happened during the session. Panics /// if there were no contract instantiations. - pub fn last_deploy_result(&self) -> &ContractInstantiateResult { + pub fn last_deploy_result(&self) -> &ContractInstantiateResult { self.deploy_results.last().expect("No deploy results") } /// Returns all the return values of contract instantiations that happened during the session. - pub fn deploy_returns(&self) -> &[AccountIdFor] { + pub fn deploy_returns(&self) -> &[AccountIdFor] { &self.deploy_returns } /// Returns the last return value of contract instantiation that happened during the session. /// Panics if there were no contract instantiations. - pub fn last_deploy_return(&self) -> &AccountIdFor { + pub fn last_deploy_return(&self) -> &AccountIdFor { self.deploy_returns.last().expect("No deploy returns") } /// Returns all the results of contract calls that happened during the session. - pub fn call_results(&self) -> &[ContractExecResult] { + pub fn call_results(&self) -> &[ContractExecResult] { &self.call_results } /// Returns the last result of contract call that happened during the session. Panics if there /// were no contract calls. - pub fn last_call_result(&self) -> &ContractExecResult { + pub fn last_call_result(&self) -> &ContractExecResult { self.call_results.last().expect("No call results") } @@ -170,13 +131,13 @@ where /// Returns all the event batches that were recorded for contract interactions during the /// session. - pub fn event_batches(&self) -> &[EventBatch] { + pub fn event_batches(&self) -> &[EventBatch] { &self.event_batches } /// Returns the last event batch that was recorded for contract interactions during the session. /// Panics if there were no event batches. - pub fn last_event_batch(&self) -> &EventBatch { + pub fn last_event_batch(&self) -> &EventBatch { self.event_batches.last().expect("No event batches") } } From a496295910aebcf1f4aa733f5d2e9e8bd8b014d5 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Thu, 8 Feb 2024 13:25:10 +0100 Subject: [PATCH 13/13] use DefaultNobound --- drink/src/session/record.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drink/src/session/record.rs b/drink/src/session/record.rs index e99222f..a09e102 100644 --- a/drink/src/session/record.rs +++ b/drink/src/session/record.rs @@ -21,6 +21,7 @@ type ContractExecResult = pallet_contracts::ContractExecResult, /// By `result` we mean the full result (enriched with some context information) of the contract /// interaction, like `ContractExecResult`. By `return` we mean the return value of the contract /// execution, like a value returned from a message or the address of a newly instantiated contract. +#[derive(frame_support::DefaultNoBound)] pub struct Record { /// The results of contract instantiation. deploy_results: Vec>, @@ -37,18 +38,6 @@ pub struct Record { event_batches: Vec>, } -impl Default for Record { - fn default() -> Self { - Self { - deploy_results: Vec::new(), - deploy_returns: Vec::new(), - call_results: Vec::new(), - call_returns: Vec::new(), - event_batches: Vec::new(), - } - } -} - // API for `Session` to record results and events related to contract interaction. impl Record { pub(super) fn push_deploy_result(&mut self, result: ContractInstantiateResult) {