Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move code to "macros" and "session" features #110

Merged
merged 8 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 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.0"
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.0", path = "drink" }
drink-test-macro = { version = "0.11.0", path = "drink/test-macro" }
drink = { version = "0.12.0", path = "drink" }
drink-test-macro = { version = "0.12.0", path = "drink/test-macro" }
pgherveou marked this conversation as resolved.
Show resolved Hide resolved
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 = "session")]
pub use drink_test_macro::{contract_bundle_provider, test};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

am I correct, that it is necessary to put only contract_bundle_provider behind feature gate? test should be fine

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just looked and the drink_test_macro crate also brings in contract-build and contract-metadata crates which would also cause a circular dependency, so maybe we should make drink_test_macroa dependency feature of drink?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, yeah, that sounds more reasonable... especially that I don't like hiding macros behind session feature

so I suggest introducing second default feature macros and feature-gate reexport here (and the dependency as well)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I am testing this with ink! fyi, to make sure we don't forget stuff.
Quick question we should also build with just the std feature (the one we will use in ink!)
What's the best way to check that the code does not generate warning

just a new step in the rust-checks.yml?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what warning do you have in mind? and yes, I guess adding some check to CI is the best idea

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()
}
}
}
}
2 changes: 1 addition & 1 deletion drink/src/session/mocking_api.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Mocking API for the sandbox.
use super::Session;
use crate::{mock::ContractMock, runtime::AccountIdFor, SandboxConfig, DEFAULT_GAS_LIMIT};
use crate::{runtime::AccountIdFor, session::mock::ContractMock, SandboxConfig, DEFAULT_GAS_LIMIT};

/// Interface for basic mocking operations.
pub trait MockingApi<R: pallet_contracts::Config> {
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