Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #31 from logion-network/feature/safe_multisig
Browse files Browse the repository at this point in the history
feat: implement vault.
  • Loading branch information
gdethier authored Mar 10, 2022
2 parents 0eb5dd2 + a2eb9c0 commit dc5165a
Show file tree
Hide file tree
Showing 11 changed files with 475 additions and 53 deletions.
20 changes: 11 additions & 9 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions pallets/lo_authority_list/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ codec = { default-features = false, features = ['derive'], package = 'parity-sca
frame-system = { default-features = false, version = '3.0.0' }
frame-support = { default-features = false, version = '3.0.0' }
frame-benchmarking = { default-features = false, optional = true, version = '3.1.0' }
logion-shared = { default-features = false, path = '../shared/', version = '0.1.0' }
serde = { default-features = false, version = "1.0.119" }
sp-std = { default-features = false, version = '3.0.0' }

Expand Down
7 changes: 7 additions & 0 deletions pallets/lo_authority_list/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use frame_support::traits::{EnsureOrigin, Vec};
use frame_system::ensure_signed;
use logion_shared::IsLegalOfficer;
pub use pallet::*;

#[cfg(test)]
Expand Down Expand Up @@ -157,3 +158,9 @@ impl<T: Config> Pallet<T> {
}
}
}

impl<T: Config> IsLegalOfficer<T::AccountId> for Pallet<T> {
fn is_legal_officer(account: &T::AccountId) -> bool {
LegalOfficerSet::<T>::contains_key(account)
}
}
17 changes: 17 additions & 0 deletions pallets/lo_authority_list/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::mock::*;
use frame_support::{assert_err, assert_ok, error::BadOrigin, traits::EnsureOrigin};
use logion_shared::IsLegalOfficer;

const LEGAL_OFFICER_ID: u64 = 1;
const ANOTHER_ID: u64 = 2;
Expand Down Expand Up @@ -51,3 +52,19 @@ fn it_ensures_origin_err_as_expected() {
assert!(result.err().is_some());
});
}

#[test]
fn it_detects_legal_officer() {
new_test_ext().execute_with(|| {
assert_ok!(LoAuthorityList::add_legal_officer(Origin::signed(MANAGER), LEGAL_OFFICER_ID));
assert!(LoAuthorityList::is_legal_officer(&LEGAL_OFFICER_ID));
});
}

#[test]
fn it_detects_regular_user() {
new_test_ext().execute_with(|| {
assert_ok!(LoAuthorityList::add_legal_officer(Origin::signed(MANAGER), LEGAL_OFFICER_ID));
assert!(!LoAuthorityList::is_legal_officer(&ANOTHER_ID));
});
}
35 changes: 34 additions & 1 deletion pallets/shared/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{Parameter, dispatch::GetDispatchInfo, traits::{UnfilteredDispatchable, Vec}};
use frame_support::{
Parameter,
dispatch::{Weight, GetDispatchInfo},
traits::{UnfilteredDispatchable, Vec}
};

pub trait CreateRecoveryCallFactory<Origin, AccountId, BlockNumber> {
type Call: Parameter + UnfilteredDispatchable<Origin = Origin> + GetDispatchInfo;
Expand All @@ -11,3 +15,32 @@ pub trait CreateRecoveryCallFactory<Origin, AccountId, BlockNumber> {
pub trait LocQuery<AccountId> {
fn has_closed_identity_locs(account: &AccountId, legal_officer: &Vec<AccountId>) -> bool;
}

pub trait MultisigApproveAsMultiCallFactory<Origin, AccountId, Timepoint> {
type Call: Parameter + UnfilteredDispatchable<Origin = Origin> + GetDispatchInfo;

fn build_approve_as_multi_call(
threshold: u16,
other_signatories: Vec<AccountId>,
maybe_timepoint: Option<Timepoint>,
call_hash: [u8; 32],
max_weight: Weight,
) -> Self::Call;
}

pub trait MultisigAsMultiCallFactory<Origin, AccountId, Timepoint> {
type Call: Parameter + UnfilteredDispatchable<Origin = Origin> + GetDispatchInfo;

fn build_as_multi_call(
threshold: u16,
other_signatories: Vec<AccountId>,
maybe_timepoint: Option<Timepoint>,
call: Vec<u8>,
store_call: bool,
max_weight: Weight,
) -> Self::Call;
}

pub trait IsLegalOfficer<AccountId> {
fn is_legal_officer(account: &AccountId) -> bool;
}
47 changes: 47 additions & 0 deletions pallets/vault/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[package]
authors = ['Logion Team <https://github.com/logion-network>']
description = 'Pallet implements Logion Vault.'
edition = '2018'
homepage = 'https://logion.network'
license = 'Apache 2.0'
name = 'pallet-logion-vault'
repository = 'https://github.com/logion-network/logion-node'
version = '0.1.0'

[package.metadata.docs.rs]
targets = ['x86_64-unknown-linux-gnu']

[dependencies]
codec = { default-features = false, features = ['derive'], package = 'parity-scale-codec', version = '2.0.0' }
frame-system = { default-features = false, version = '3.0.0' }
frame-support = { default-features = false, version = '3.0.0' }
frame-benchmarking = { default-features = false, optional = true, version = '3.1.0' }
logion-shared = { default-features = false, path = '../shared/', version = '0.1.0' }
pallet-multisig = { default-features = false, version='3.0.0' }
serde = { default-features = false, version = "1.0.119" }
sp-std = { default-features = false, version = '3.0.0' }

[dev-dependencies]
sp-core = { default-features = false, version = '3.0.0' }
sp-io = { default-features = false, version = '3.0.0' }
sp-runtime = { default-features = false, version = '3.0.0' }

[features]
default = ['std']
std = [
'codec/std',
'frame-support/std',
'frame-system/std',
'frame-benchmarking/std',
'pallet-multisig/std',
'serde/std',
'sp-std/std',
]
runtime-benchmarks = [
'frame-benchmarking',
'frame-support/runtime-benchmarks',
'frame-system/runtime-benchmarks',
]
# Note: frame-support `try-runtime` feature is released after v3.
# Uncomment the following line when `frame-support` version > `3.0.0`.
# try-runtime = ['frame-support/try-runtime']
156 changes: 156 additions & 0 deletions pallets/vault/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;

#[frame_support::pallet]
pub mod pallet {
use frame_system::pallet_prelude::*;
use frame_support::{
dispatch::DispatchResultWithPostInfo,
pallet_prelude::*
};
use logion_shared::{MultisigApproveAsMultiCallFactory, MultisigAsMultiCallFactory, IsLegalOfficer};
use frame_support::traits::{Vec, UnfilteredDispatchable};
use pallet_multisig::{WeightInfo, Timepoint, OpaqueCall};

#[pallet::config]
pub trait Config: frame_system::Config {

/// Implementation of multisig "approve as multi"
type MultisigApproveAsMultiCallFactory: MultisigApproveAsMultiCallFactory<Self::Origin, Self::AccountId, Timepoint<Self::BlockNumber>>;

/// Implementation of multisig "as multi"
type MultisigAsMultiCallFactory: MultisigAsMultiCallFactory<Self::Origin, Self::AccountId, Timepoint<Self::BlockNumber>>;

/// Query for checking that a signer is a legal officer
type IsLegalOfficer: IsLegalOfficer<Self::AccountId>;

/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;

/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
}

#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);

#[pallet::event]
pub enum Event<T: Config> {

}

#[pallet::error]
pub enum Error<T> {
/// The set of signatories is invalid (size <> from 2 or does not contain only legal officers on transfer creation).
InvalidSignatories,
/// The transfer initiator is a legal officer.
WrongInitiator,
}

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}

#[pallet::call]
impl<T:Config> Pallet<T> {

/// Create a vault transfer. The creator must not be a legal officer.
#[pallet::weight({
// Weight computation comes from multisig pallet
let s = legal_officers.len() as u32;
T::WeightInfo::approve_as_multi_create(s)
.max(T::WeightInfo::approve_as_multi_approve(s))
.max(T::WeightInfo::approve_as_multi_complete(s))
.saturating_add(*max_weight)
})]
pub fn request_call(
origin: OriginFor<T>,
legal_officers: Vec<T::AccountId>,
call_hash: [u8; 32],
max_weight: Weight,
) -> DispatchResultWithPostInfo {
if legal_officers.len() != 2
|| !T::IsLegalOfficer::is_legal_officer(&legal_officers[0])
|| !T::IsLegalOfficer::is_legal_officer(&legal_officers[1]) {
Err(Error::<T>::InvalidSignatories)?
} else {
let who = ensure_signed(origin.clone())?;
if T::IsLegalOfficer::is_legal_officer(&who) {
Err(Error::<T>::WrongInitiator)?
} else {
Self::dispatch_create_multi(origin, legal_officers, call_hash, max_weight)
}
}
}

/// Approves a vault transfer.
#[pallet::weight({
// Weight computation comes from multisig pallet
let s = other_signatories.len() as u32;
let z = call.len() as u32;
T::WeightInfo::as_multi_create(s, z)
.max(T::WeightInfo::as_multi_create_store(s, z))
.max(T::WeightInfo::as_multi_approve(s, z))
.max(T::WeightInfo::as_multi_complete(s, z))
.saturating_add(*max_weight)
})]
pub fn approve_call(
origin: OriginFor<T>,
other_signatories: Vec<T::AccountId>,
call: OpaqueCall,
timepoint: Timepoint<T::BlockNumber>,
max_weight: Weight,
) -> DispatchResultWithPostInfo {
if other_signatories.len() != 2 {
Err(Error::<T>::InvalidSignatories)?
} else {
Self::dispatch_as_multi(origin, other_signatories, call, timepoint, max_weight)
}
}
}

impl<T: Config> Pallet<T> {
fn dispatch_create_multi(
origin: OriginFor<T>,
legal_officers: Vec<T::AccountId>,
call_hash: [u8; 32],
max_weight: Weight,
) -> DispatchResultWithPostInfo {
let call = <T as Config>::MultisigApproveAsMultiCallFactory::build_approve_as_multi_call(
2,
legal_officers,
None,
call_hash,
max_weight
);
call.dispatch_bypass_filter(origin)
}

fn dispatch_as_multi(
origin: OriginFor<T>,
other_signatories: Vec<T::AccountId>,
call: OpaqueCall,
timepoint: Timepoint<T::BlockNumber>,
max_weight: Weight,
) -> DispatchResultWithPostInfo {
let call = <T as Config>::MultisigAsMultiCallFactory::build_as_multi_call(
2,
other_signatories,
Some(timepoint),
call,
false,
max_weight
);
call.dispatch_bypass_filter(origin)
}
}
}
Loading

0 comments on commit dc5165a

Please sign in to comment.