Skip to content

Commit

Permalink
Merge branch 'main' into pg/update-macros
Browse files Browse the repository at this point in the history
  • Loading branch information
pgherveou committed Mar 6, 2024
2 parents da6ef4f + e110d55 commit 83d2d43
Show file tree
Hide file tree
Showing 34 changed files with 48,646 additions and 236 deletions.
257 changes: 142 additions & 115 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ homepage = "https://github.com/Cardinal-Cryptography/drink"
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/Cardinal-Cryptography/drink"
version = "0.11.0"
version = "0.13.0"

[workspace.dependencies]
paste = { version = "1.0.7" }
anyhow = { version = "1.0.71" }
cargo_metadata = { version = "0.18.1" }
clap = { version = "4.3.4" }
contract-build = { version = "4.0.0-rc.2" }
contract-metadata = { version = "4.0.0-rc.2" }
contract-transcode = { version = "4.0.0-rc.2" }
contract-build = { version = "4.0.0-rc.3" }
contract-metadata = { version = "4.0.0-rc.3" }
contract-transcode = { version = "4.0.0-rc.3" }
convert_case = { version = "0.6.0" }
crossterm = { version = "0.26.0" }
darling = { version = "0.20.3" }
Expand Down Expand Up @@ -58,5 +58,5 @@ sp-runtime-interface = { version = "24.0.0" }

# Local dependencies

drink = { version = "0.11.0", path = "drink" }
drink-test-macro = { version = "0.11.0", path = "drink/test-macro" }
drink = { version = "=0.13.0", path = "drink" }
drink-test-macro = { version = "=0.13.0", path = "drink/test-macro" }
4 changes: 3 additions & 1 deletion drink/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ drink-test-macro = { workspace = true }
default = [
# This is required for the runtime-interface to work properly in the std env.
"std",
"session"
"session",
"macros"
]
session = ["contract-metadata", "contract-transcode", "serde_json"]
macros = ["contract-metadata", "contract-transcode", "serde_json"]
std = []
83 changes: 5 additions & 78 deletions drink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,27 @@
#![warn(missing_docs)]

mod bundle;
pub mod errors;
mod mock;
pub mod runtime;
pub mod sandbox;
pub use sandbox::*;
#[cfg(feature = "session")]
pub mod session;

use std::sync::{Arc, Mutex};

pub use bundle::ContractBundle;
#[cfg(feature = "macros")]
pub use drink_test_macro::{contract_bundle_provider, test};
pub use errors::Error;
pub use frame_support::{
sp_runtime::{AccountId32, DispatchError},
weights::Weight,
};
use frame_system::EventRecord;
pub use mock::{mock_message, ContractMock, MessageMock, MockedCallResult, Selector};
use pallet_contracts::{debug::ExecResult, ExecReturnValue};
use pallet_contracts_uapi::ReturnFlags;
use parity_scale_codec::{Decode, Encode};
/// Export crates that are used in the create_minimal_sandbox macro.
pub use {
frame_support, frame_system, pallet_balances, pallet_contracts, pallet_timestamp, paste,
sp_externalities::Extension, sp_io::TestExternalities,
};
#[cfg(feature = "session")]
pub use session::mock::{mock_message, ContractMock, MessageMock, MockedCallResult, Selector};
/// Export pallets that are used in the minimal runtime.
pub use {frame_support, frame_system, pallet_balances, pallet_contracts, pallet_timestamp};

pub use crate::runtime::minimal::{self, MinimalSandbox};
use crate::{
errors::MessageResult, mock::MockRegistry,
runtime::pallet_contracts_debugging::InterceptingExtT,
};

/// Alias for `frame-system`'s `RuntimeCall` type.
pub type RuntimeCall<R> = <R as frame_system::Config>::RuntimeCall;
Expand All @@ -53,63 +40,3 @@ pub type EventRecordOf<T> =

/// Default gas limit.
pub const DEFAULT_GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024);

/// Runtime extension enabling contract call interception.
struct MockingExtension<AccountId: Ord> {
/// Mock registry, shared with the sandbox.
///
/// Potentially the runtime is executed in parallel and thus we need to wrap the registry in
/// `Arc<Mutex>` instead of `Rc<RefCell>`.
mock_registry: Arc<Mutex<MockRegistry<AccountId>>>,
}

impl<AccountId: Ord + Decode> InterceptingExtT for MockingExtension<AccountId> {
fn intercept_call(
&self,
contract_address: Vec<u8>,
_is_call: bool,
input_data: Vec<u8>,
) -> Vec<u8> {
let contract_address = Decode::decode(&mut &contract_address[..])
.expect("Contract address should be decodable");

match self
.mock_registry
.lock()
.expect("Should be able to acquire registry")
.get(&contract_address)
{
// There is no mock registered for this address, so we return `None` to indicate that
// the call should be executed normally.
None => None::<()>.encode(),
// We intercept the call and return the result of the mock.
Some(mock) => {
let (selector, call_data) = input_data.split_at(4);
let selector: Selector = selector
.try_into()
.expect("Input data should contain at least selector bytes");

let result = mock
.call(selector, call_data.to_vec())
.expect("TODO: let the user define the fallback mechanism");

// Although we don't know the exact type, thanks to the SCALE encoding we know
// that `()` will always succeed (we only care about the `Ok`/`Err` distinction).
let decoded_result: MessageResult<()> =
Decode::decode(&mut &result[..]).expect("Mock result should be decodable");

let flags = match decoded_result {
Ok(_) => ReturnFlags::empty(),
Err(_) => ReturnFlags::REVERT,
};

let result: ExecResult = Ok(ExecReturnValue {
flags,
data: result,
});

Some(result).encode()
}
}
}
}
13 changes: 8 additions & 5 deletions drink/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,29 @@ use parity_scale_codec::Decode;
pub use record::{EventBatch, Record};

use crate::{
mock::MockRegistry,
runtime::{
pallet_contracts_debugging::{InterceptingExt, TracingExt},
AccountIdFor, HashFor,
},
sandbox::prelude::*,
MockingExtension, Sandbox, DEFAULT_GAS_LIMIT,
session::mock::MockRegistry,
Sandbox, DEFAULT_GAS_LIMIT,
};

pub mod mock;
use mock::MockingExtension;
pub mod bundle;
pub mod error;
pub mod mocking_api;
mod record;
mod transcoding;

pub use bundle::ContractBundle;
use error::SessionError;

use self::mocking_api::MockingApi;
use crate::{
bundle::ContractBundle, errors::MessageResult, minimal::MinimalSandboxRuntime,
errors::MessageResult, minimal::MinimalSandboxRuntime, runtime::MinimalRuntime,
session::transcoding::TranscoderRegistry,
};

Expand Down Expand Up @@ -115,9 +119,8 @@ pub const NO_ENDOWMENT: Option<BalanceOf<MinimalSandboxRuntime>> = None;
/// # use drink::{
/// # local_contract_file,
/// # session::Session,
/// # session::{NO_ARGS, NO_SALT, NO_ENDOWMENT},
/// # session::{ContractBundle, NO_ARGS, NO_SALT, NO_ENDOWMENT},
/// # runtime::MinimalSandbox,
/// # ContractBundle,
/// # };
///
/// # fn main() -> Result<(), drink::session::error::SessionError> {
Expand Down
2 changes: 1 addition & 1 deletion drink/src/bundle.rs → drink/src/session/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl ContractBundle {
#[macro_export]
macro_rules! local_contract_file {
() => {
drink::ContractBundle::local(
drink::session::ContractBundle::local(
env!("CARGO_MANIFEST_DIR"),
env!("CARGO_CRATE_NAME").to_owned() + ".contract",
)
Expand Down
5 changes: 4 additions & 1 deletion drink/src/mock.rs → drink/src/session/mock.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
//! Mocking utilities for contract calls.
mod contract;
mod error;

mod extension;
use std::collections::BTreeMap;

pub use contract::{mock_message, ContractMock, MessageMock, Selector};
use error::MockingError;
pub(crate) use extension::MockingExtension;

/// Untyped result of a mocked call.
pub type MockedCallResult = Result<Vec<u8>, MockingError>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use parity_scale_codec::{Decode, Encode};

use crate::{
errors::LangError,
mock::{error::MockingError, MockedCallResult},
session::mock::{error::MockingError, MockedCallResult},
};

/// Alias for a 4-byte selector.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use thiserror::Error;

use crate::Selector;
use crate::session::mock::Selector;

/// Error type for mocking operations.
#[derive(Error, Debug)]
Expand Down
71 changes: 71 additions & 0 deletions drink/src/session/mock/extension.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use std::sync::{Arc, Mutex};

use pallet_contracts::{debug::ExecResult, ExecReturnValue};
use pallet_contracts_uapi::ReturnFlags;
use parity_scale_codec::{Decode, Encode};

use crate::{
errors::MessageResult,
runtime::pallet_contracts_debugging::InterceptingExtT,
session::mock::{MockRegistry, Selector},
};

/// Runtime extension enabling contract call interception.
pub(crate) struct MockingExtension<AccountId: Ord> {
/// Mock registry, shared with the sandbox.
///
/// Potentially the runtime is executed in parallel and thus we need to wrap the registry in
/// `Arc<Mutex>` instead of `Rc<RefCell>`.
pub mock_registry: Arc<Mutex<MockRegistry<AccountId>>>,
}

impl<AccountId: Ord + Decode> InterceptingExtT for MockingExtension<AccountId> {
fn intercept_call(
&self,
contract_address: Vec<u8>,
_is_call: bool,
input_data: Vec<u8>,
) -> Vec<u8> {
let contract_address = Decode::decode(&mut &contract_address[..])
.expect("Contract address should be decodable");

match self
.mock_registry
.lock()
.expect("Should be able to acquire registry")
.get(&contract_address)
{
// There is no mock registered for this address, so we return `None` to indicate that
// the call should be executed normally.
None => None::<()>.encode(),
// We intercept the call and return the result of the mock.
Some(mock) => {
let (selector, call_data) = input_data.split_at(4);
let selector: Selector = selector
.try_into()
.expect("Input data should contain at least selector bytes");

let result = mock
.call(selector, call_data.to_vec())
.expect("TODO: let the user define the fallback mechanism");

// Although we don't know the exact type, thanks to the SCALE encoding we know
// that `()` will always succeed (we only care about the `Ok`/`Err` distinction).
let decoded_result: MessageResult<()> =
Decode::decode(&mut &result[..]).expect("Mock result should be decodable");

let flags = match decoded_result {
Ok(_) => ReturnFlags::empty(),
Err(_) => ReturnFlags::REVERT,
};

let result: ExecResult = Ok(ExecReturnValue {
flags,
data: result,
});

Some(result).encode()
}
}
}
}
3 changes: 2 additions & 1 deletion drink/src/session/mocking_api.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Mocking API for the sandbox.
use super::Session;
use crate::{
mock::ContractMock, runtime::AccountIdFor, sandbox::prelude::*, Sandbox, DEFAULT_GAS_LIMIT,
runtime::AccountIdFor, sandbox::prelude::*, session::mock::ContractMock, SandboxConfig,
DEFAULT_GAS_LIMIT,
};

/// Interface for basic mocking operations.
Expand Down
2 changes: 1 addition & 1 deletion drink/src/session/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ impl EventBatch<MinimalSandboxRuntime> {
// We have to `encode` the data because `decode_contract_event` is targeted
// at decoding the data from the runtime, and not directly from the contract
// events.
.decode_contract_event(&signature_topic.into(), &mut &*data.encode())
.decode_contract_event(&signature_topic, &mut &*data.encode())
{
return Some(decoded);
}
Expand Down
8 changes: 4 additions & 4 deletions drink/test-macro/src/bundle_provision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ impl BundleProviderGenerator {
Some(root_name) => {
let local_bundle = self.bundles[root_name].to_str().expect("Invalid path");
quote! {
pub fn local() -> ::drink::DrinkResult<::drink::ContractBundle> {
::drink::ContractBundle::load(#local_bundle)
pub fn local() -> ::drink::DrinkResult<::drink::session::ContractBundle> {
::drink::session::ContractBundle::load(#local_bundle)
}
}
}
Expand All @@ -58,7 +58,7 @@ impl BundleProviderGenerator {
let name_ident = Ident::new(name, Span::call_site());
let path = self.bundles[name].to_str().expect("Invalid path");
let matcher = quote! {
#enum_name::#name_ident => ::drink::ContractBundle::load(#path),
#enum_name::#name_ident => ::drink::session::ContractBundle::load(#path),
};
(name_ident, matcher)
})
Expand All @@ -74,7 +74,7 @@ impl BundleProviderGenerator {
impl #enum_name {
#local

pub fn bundle(self) -> ::drink::DrinkResult<::drink::ContractBundle> {
pub fn bundle(self) -> ::drink::DrinkResult<::drink::session::ContractBundle> {
match self {
#(#matches)*
}
Expand Down
Loading

0 comments on commit 83d2d43

Please sign in to comment.