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

feat: implement currency locking and extend testing #158

Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Common jobs (fmt, clippy, test)
on:
pull_request:
paths:
- src/**
- pallet/**
- Cargo.toml
- rpc/**
- .github/workflows/common.yml
Expand Down
22 changes: 18 additions & 4 deletions pallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ pub mod pallet {
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::format;
use blake2::{Blake2s256, Digest};
use blake2::{digest::consts::U8, Blake2b, Blake2s256, Digest};
use core::convert::AsRef;

use codec::{FullCodec, FullEncode};
use frame_support::{
dispatch::DispatchResultWithPostInfo,
pallet_prelude::*,
traits::{Currency, Get, LockableCurrency, ReservableCurrency},
traits::{
tokens::currency::LockIdentifier, Currency, Get, LockableCurrency, ReservableCurrency,
},
};
use frame_system::pallet_prelude::*;
pub use move_core_types::language_storage::TypeTag;
Expand Down Expand Up @@ -247,7 +249,6 @@ pub mod pallet {
gas_limit: u64,
cheque_limit: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
// TODO(neutrinoks): Add currency locking in multi-signature executions.
// A signer for the extrinsic and a signer for the Move script.
let who = ensure_signed(origin)?;

Expand Down Expand Up @@ -286,7 +287,8 @@ pub mod pallet {
)
};
if signer_count > 0 {
signature_handler.sign_script(&who, cheque_limit.into())?;
let lock_id = Self::multi_signer_lock_id(&who, &transaction_bc[..], &cheque_limit);
signature_handler.sign_script(&who, cheque_limit.into(), lock_id)?;
}
neutrinoks marked this conversation as resolved.
Show resolved Hide resolved

// If the script is signed correctly, we can execute it in MoveVM and update the
Expand Down Expand Up @@ -561,6 +563,18 @@ pub mod pallet {
hasher.update(transaction_bc);
hasher.finalize().into()
}

pub fn multi_signer_lock_id(
who: &T::AccountId,
transaction_bc: &[u8],
cheque_limit: &BalanceOf<T>,
) -> LockIdentifier {
let mut hasher = Blake2b::<U8>::new();
hasher.update(transaction_bc);
hasher.update(&who.encode()[..]);
hasher.update(&cheque_limit.encode()[..]);
hasher.finalize().into()
}
}

#[pallet::error]
Expand Down
48 changes: 36 additions & 12 deletions pallet/src/mock.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use frame_support::{
dispatch::DispatchErrorWithPostInfo,
pallet_prelude::{DispatchError, DispatchResultWithPostInfo},
parameter_types,
traits::{ConstU128, ConstU16, ConstU32, ConstU64, OnFinalize, OnIdle, OnInitialize},
};
Expand All @@ -16,7 +18,17 @@ pub use move_core_types::account_address::AccountAddress;
pub use move_vm_backend_common::types::ScriptTransaction;
pub use sp_runtime::AccountId32;

type Block = frame_system::mocking::MockBlock<Test>;
// Primitive type definitions for this mockup.
pub type Balance = u128;
pub type Block = frame_system::mocking::MockBlock<Test>;
// Key constants or frequently used ones.
pub const EXISTENTIAL_DEPOSIT: Balance = 100;
pub const EMPTY_CHEQUE: Balance = 0;
pub const CAFE_ADDR: &str = "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSv4fmh4G"; // == 0xCAFE
pub const BOB_ADDR: &str = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty";
pub const ALICE_ADDR: &str = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
pub const DAVE_ADDR: &str = "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy";
pub const EVE_ADDR: &str = "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw";

// Configure a mock runtime to test the pallet.
frame_support::construct_runtime!(
Expand Down Expand Up @@ -54,9 +66,6 @@ impl frame_system::Config for Test {
type MaxConsumers = frame_support::traits::ConstU32<16>;
}

pub type Balance = u128;
pub const EXISTENTIAL_DEPOSIT: u128 = 100;

impl pallet_balances::Config for Test {
type MaxLocks = ConstU32<50>;
type MaxReserves = ();
Expand Down Expand Up @@ -142,31 +151,27 @@ impl ExtBuilder {
}
}

// Common constants accross the tests.
pub const EMPTY_CHEQUE: u128 = 0; // Not all scripts need the `cheque_amount` parameter.
pub const CAFE_ADDR: &str = "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSv4fmh4G"; // == 0xCAFE
pub const BOB_ADDR: &str = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty";
pub const ALICE_ADDR: &str = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
pub const DAVE_ADDR: &str = "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy";
pub const EVE_ADDR: &str = "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw";

/// Creates a native 32-byte address from a given ss58 string.
pub(crate) fn addr32_from_ss58(ss58addr: &str) -> Result<AccountId32, Error<Test>> {
let (pk, _) = Public::from_ss58check_with_version(ss58addr)
.map_err(|_| Error::<Test>::InvalidAccountSize)?;
let account: AccountId32 = pk.into();
Ok(account)
}

/// Converts a native 32-byte address into a Move memory address.
pub(crate) fn addr32_to_move(addr32: &AccountId32) -> Result<AccountAddress, Error<Test>> {
MoveModule::to_move_address(addr32)
}

/// Creates a native 32-byte address and it's Move memory address by given ss58 string.
pub(crate) fn addrs_from_ss58(ss58: &str) -> Result<(AccountId32, AccountAddress), Error<Test>> {
let addr_32 = addr32_from_ss58(ss58)?;
let addr_mv = addr32_to_move(&addr_32)?;
Ok((addr_32, addr_mv))
}

/// Rolls forward in history to the given block height.
pub(crate) fn roll_to(n: BlockNumberFor<Test>) {
let weight = <Test as pallet_move::Config>::WeightInfo::chore_multisig_storage() * 2;
while System::block_number() < n {
Expand All @@ -177,10 +182,29 @@ pub(crate) fn roll_to(n: BlockNumberFor<Test>) {
}
}

/// Returns the last emitted event by the blockchain.
pub(crate) fn last_event() -> RuntimeEvent {
System::events().pop().expect("Event expected").event
}

/// In case of an error returned from MoveVM, this method compares the encapsuled error string at
/// the level of the returned `DispatchResultWithPostInfo`.
pub(crate) fn verify_module_error_with_msg(
res: DispatchResultWithPostInfo,
e_msg: &str,
) -> Result<bool, String> {
if let Err(DispatchErrorWithPostInfo {
error: DispatchError::Module(moderr),
..
}) = res
{
if let Some(msg) = moderr.message {
return Ok(msg == e_msg);
}
}
Err(format!("{res:?} does not match '{e_msg}'"))
}

pub mod assets {
const MOVE_PROJECTS: &str = "src/tests/assets/move-projects";

Expand Down
19 changes: 18 additions & 1 deletion pallet/src/signer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
use core::marker::PhantomData;

use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{pallet_prelude::RuntimeDebug, traits::Get, BoundedBTreeMap, Parameter};
use frame_support::{
pallet_prelude::RuntimeDebug,
traits::{
tokens::{
currency::{LockIdentifier, LockableCurrency},
WithdrawReasons,
},
Get,
},
BoundedBTreeMap, Parameter,
};
use frame_system::{pallet_prelude::BlockNumberFor, Config as SysConfig};
use scale_info::TypeInfo;
use sp_runtime::traits::MaybeSerializeDeserialize;
Expand Down Expand Up @@ -54,6 +64,8 @@ pub struct SignerData {
pub signature: Signature,
/// Individual cheque-limit.
pub cheque_limit: u128,
/// Lock ID for locked currency.
pub lock_id: LockIdentifier,
}

/// Storage struct definition for a multi-signer request.
Expand Down Expand Up @@ -183,13 +195,17 @@ where
&mut self,
account: &T::AccountId,
cheque_limit: u128,
lock_id: LockIdentifier,
) -> Result<(), Error<T>> {
if let Some(ms_data) = self.multisig_info.get_mut(account) {
if matches!(ms_data.signature, Signature::Approved) {
Err(Error::<T>::UserHasAlreadySigned)
} else {
ms_data.signature = Signature::Approved;
ms_data.cheque_limit = cheque_limit;
ms_data.lock_id = lock_id;
let amount = BalanceOf::<T>::from(cheque_limit);
T::Currency::set_lock(lock_id, account, amount, WithdrawReasons::all());
Ok(())
}
} else {
Expand All @@ -209,6 +225,7 @@ where
pub(crate) fn write_cheques(&self) -> Result<BalanceAdapter<T>, Error<T>> {
let mut balances = BalanceAdapter::<T>::new();
for (account, ms_data) in self.multisig_info.iter() {
T::Currency::remove_lock(ms_data.lock_id, account);
balances
.write_cheque(account, &BalanceOf::<T>::from(ms_data.cheque_limit))
.map_err(|_| Error::<T>::InsufficientBalance)?;
Expand Down
Loading
Loading