Skip to content

Commit

Permalink
Move code to "macros" and "session" features (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
pgherveou authored Mar 5, 2024
1 parent 0e33fb1 commit e110d55
Show file tree
Hide file tree
Showing 14 changed files with 125 additions and 110 deletions.
24 changes: 12 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.11.1"
version = "0.12.0"

[workspace.dependencies]
anyhow = { version = "1.0.71" }
Expand Down Expand Up @@ -57,5 +57,5 @@ sp-runtime-interface = { version = "24.0.0" }

# Local dependencies

drink = { version = "0.11.1", path = "drink" }
drink-test-macro = { version = "0.11.1", path = "drink/test-macro" }
drink = { version = "=0.12.0", path = "drink" }
drink-test-macro = { version = "=0.12.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 @@ -36,7 +36,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 = []
76 changes: 3 additions & 73 deletions drink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +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};
#[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, MinimalRuntime};
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 @@ -50,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()
}
}
}
}
14 changes: 8 additions & 6 deletions drink/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +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::SandboxConfig,
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, runtime::MinimalRuntime,
session::transcoding::TranscoderRegistry,
errors::MessageResult, runtime::MinimalRuntime, session::transcoding::TranscoderRegistry,
};

type BalanceOf<R> =
Expand Down Expand Up @@ -115,9 +118,8 @@ pub const NO_ENDOWMENT: Option<BalanceOf<MinimalRuntime>> = None;
/// # use drink::{
/// # local_contract_file,
/// # session::Session,
/// # session::{NO_ARGS, NO_SALT, NO_ENDOWMENT},
/// # session::{ContractBundle, NO_ARGS, NO_SALT, NO_ENDOWMENT},
/// # runtime::MinimalRuntime,
/// # 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()
}
}
}
}
Loading

0 comments on commit e110d55

Please sign in to comment.