Skip to content

Commit

Permalink
Merge pull request #218 from InvArch/francisco-docs_inv4
Browse files Browse the repository at this point in the history
Reworked INV4 documentation
  • Loading branch information
arrudagates authored Feb 18, 2024
2 parents 89b79fb + df69b04 commit 6db87cb
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 10 deletions.
26 changes: 19 additions & 7 deletions INV4/pallet-inv4/src/account_derivation.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
//! Core Account Derivation.
//!
//! ## Overview
//!
//! This module defines a method for generating account addresses, and how it's implemented within this
//! pallet. We use a custom derivation scheme to ensure that when a multisig is created, its AccountId
//! remains consistent across different parachains, promoting seamless interaction.
//!
//! ### The module contains:
//! - `CoreAccountDerivation` trait: The interface for our derivation method.
//! - Pallet implementation: The specific logic used to derive AccountIds.

use crate::{Config, Pallet};
use codec::{Compact, Encode};
use frame_support::traits::Get;
use sp_io::hashing::blake2_256;
use xcm::latest::{BodyId, BodyPart, Junction, Junctions};

// Trait providing the XCM location and the derived account of a core.
/// Trait providing the XCM location and the derived account of a core.
pub trait CoreAccountDerivation<T: Config> {
/// Derives the core's AccountId.
fn derive_core_account(core_id: T::CoreId) -> T::AccountId;
/// Specifies a core's location.
fn core_location(core_id: T::CoreId) -> Junctions;
}

impl<T: Config> CoreAccountDerivation<T> for Pallet<T>
where
T::AccountId: From<[u8; 32]>,
{
/// HashedDescription of the core location from the perspective of a sibling chain.
/// This derivation allows the local account address to match the account address in other parachains.
/// Reference: https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/xcm-builder/src/location_conversion.rs
fn derive_core_account(core_id: T::CoreId) -> T::AccountId {
// HashedDescription of the core location from the perspective of a sibling chain.
// This derivation allows the local account address to match the account address in other parachains.
// Reference: https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/xcm-builder/src/location_conversion.rs
blake2_256(
&(
b"SiblingChain",
Expand All @@ -28,9 +41,8 @@ where
)
.into()
}

/// Core location is defined as a plurality within the parachain.
fn core_location(core_id: T::CoreId) -> Junctions {
// Core location is defined as a plurality within the parachain.
Junctions::X2(
Junction::Parachain(T::ParaId::get()),
Junction::Plurality {
Expand Down
11 changes: 10 additions & 1 deletion INV4/pallet-inv4/src/dispatch.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
//! Dispatches calls internally, charging fees to the multisig account.
//!
//! ## Overview
//!
//! This module employs a custom `MultisigInternalOrigin` to ensure calls originate
//! from the multisig account itself, automating fee payments. The `dispatch_call` function
//! includes pre and post dispatch handling for streamlined fee management within the multisig context.

use crate::{
fee_handling::{FeeAsset, MultisigFeeHandler},
origin::{INV4Origin, MultisigInternalOrigin},
Expand All @@ -8,7 +16,7 @@ use frame_support::{
pallet_prelude::*,
};

// Dispatch a call executing pre/post dispatch for proper fee handling.
/// Dispatch a call executing pre/post dispatch for proper fee handling.
pub fn dispatch_call<T: Config>(
core_id: <T as Config>::CoreId,
fee_asset: &FeeAsset,
Expand All @@ -17,6 +25,7 @@ pub fn dispatch_call<T: Config>(
where
T::AccountId: From<[u8; 32]>,
{
// Create new custom origin as the multisig.
let internal_origin = MultisigInternalOrigin::new(core_id);
let multisig_account = internal_origin.to_account_id();
let origin = INV4Origin::Multisig(internal_origin).into();
Expand Down
26 changes: 25 additions & 1 deletion INV4/pallet-inv4/src/fee_handling.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
//! MultisigFeeHandler trait.
//!
//! ## Overview
//!
//! Defines how transaction fees are charged to the multisig account.
//! This trait requires proper runtime implementation to allow the usage of native or non-native assets.

use crate::Config;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
Expand All @@ -10,23 +17,38 @@ use sp_runtime::{
DispatchResult,
};

/// Asset to be used by the multisig for paying fees transaction fees.
/// Represents the asset to be used by the multisig for paying transaction fees.
///
/// This enum defines the assets that can be used to pay for transaction fees.
#[derive(Clone, TypeInfo, Encode, Decode, MaxEncodedLen, Debug, PartialEq, Eq)]
pub enum FeeAsset {
Native,
Relay,
}

/// Represents a potential negative asset balance incurred during fee payment operations
/// within a multisig context.
///
/// This enum handles imbalances in either the native token or
/// a relay chain asset used for fees.
///
/// - `Native(NativeNegativeImbalance)`: Indicates a deficit balance in the chain's native asset.
/// - `Relay(RelayNegativeImbalance)`: Indicates a deficit balance in an asset originating on the relay chain.
///
/// This enum plays a role in resolving deficit balances in the `MultisigFeeHandler` trait.
pub enum FeeAssetNegativeImbalance<NativeNegativeImbalance, RelayNegativeImbalance> {
Native(NativeNegativeImbalance),
Relay(RelayNegativeImbalance),
}

/// Fee handler trait.
///
/// This should be implemented properly in the runtime to account for native and non-native assets.
pub trait MultisigFeeHandler<T: Config> {
/// Type returned by `pre_dispatch` - implementation dependent.
type Pre;

/// Checks if the fee can be paid using the selected asset.
fn pre_dispatch(
asset: &FeeAsset,
who: &T::AccountId,
Expand All @@ -35,6 +57,7 @@ pub trait MultisigFeeHandler<T: Config> {
len: usize,
) -> Result<Self::Pre, TransactionValidityError>;

/// Charges the call dispatching fee from the multisig directly.
fn post_dispatch(
asset: &FeeAsset,
pre: Option<Self::Pre>,
Expand All @@ -44,6 +67,7 @@ pub trait MultisigFeeHandler<T: Config> {
result: &DispatchResult,
) -> Result<(), TransactionValidityError>;

/// Charges the fee for creating the core (multisig).
fn handle_creation_fee(
imbalance: FeeAssetNegativeImbalance<
<T::Currency as Currency<T::AccountId>>::NegativeImbalance,
Expand Down
10 changes: 10 additions & 0 deletions INV4/pallet-inv4/src/inv4_core.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
//! Core creation and internal management.
//!
//! ## Overview
//!
//! This module handles the mechanics of creating multisigs (referred to as "cores") and their lifecycle management. Key functions include:
//!
//! - `inner_create_core`: Sets up a new core, deriving its AccountId, distributing voting tokens, and handling creation fees.
//! - `inner_set_parameters`: Updates the core's operational rules.
//! - `is_asset_frozen`: Utility function for checking if a core's voting asset is frozen (can't be transferred by the owner).

use super::pallet::*;
use crate::{
account_derivation::CoreAccountDerivation,
Expand Down
3 changes: 3 additions & 0 deletions INV4/pallet-inv4/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
//! ## Overview
//! This pallet handles advanced virtual multisigs (internally called cores).
//!
//! Lower level implementation details of this pallet's calls are contained in separate modules, each of them
//! containing their own docs.
//!
//! ### Pallet Functions
//!
//! - `create_core` - Create a new core
Expand Down
13 changes: 13 additions & 0 deletions INV4/pallet-inv4/src/lookup.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
//! Custom account lookup implementation.
//!
//! ## Overview
//!
//! This module implements the [`StaticLookup`] trait allowing for convenient lookup of a core's
//! AccountId from its CoreId.
//! This implementation abstracts on top of two lower level functions:
//! - `lookup_core`: Used for accessing the storage and retrieving a core's AccountId.
//! - `lookup_address`: Used for converting from a `MultiAddress::Index` that contains a CoreId to this core's AccountId.

use crate::{Config, CoreByAccount, CoreStorage, Pallet};
use core::marker::PhantomData;
use frame_support::error::LookupError;
use sp_runtime::{traits::StaticLookup, MultiAddress};

impl<T: Config> Pallet<T> {
/// Queries `CoreStorage` to retrieve the AccountId of a core.
pub fn lookup_core(core_id: T::CoreId) -> Option<T::AccountId> {
CoreStorage::<T>::get(core_id).map(|core| core.account)
}

/// Matches `MultiAddress` to allow for a `MultiAddress::Index` containing a CoreId to be converted
/// to it's derived AccountId.
pub fn lookup_address(a: MultiAddress<T::AccountId, T::CoreId>) -> Option<T::AccountId> {
match a {
MultiAddress::Id(i) => Some(i),
Expand Down
14 changes: 13 additions & 1 deletion INV4/pallet-inv4/src/multisig.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
//! Multisig Operations.
//!
//! ## Overview
//!
//! Handles the core actions within an already established multisig.
//!
//! ### Core functionalities:
//! - Minting/Burning voting tokens to existing and new members.
//! - Handling proposal votes.
//! - Dispatching approved proposals when both support and approval meet/exceed their minimum required thresholds.
//! - Canceling proposals.

use super::pallet::{self, *};
use crate::{
account_derivation::CoreAccountDerivation,
Expand Down Expand Up @@ -202,7 +214,7 @@ where
// Get the voting token balance of the caller
let voter_balance: BalanceOf<T> = T::AssetsProvider::balance(core_id, &owner);

// If caller doesn't own the token, they have no voting power
// If caller doesn't own the token, they have no voting power.
ensure!(!voter_balance.is_zero(), Error::<T>::NoPermission);

// Get the multisig call data from the storage
Expand Down
12 changes: 12 additions & 0 deletions INV4/pallet-inv4/src/origin.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
//! Custom Multisig Origin (`INV4Origin`).
//!
//! ## Overview
//!
//! This module introduces a custom origin [`INV4Origin`], enabling self-management for cores and
//! includes the [`ensure_multisig`] function to guarantee calls genuinely come from the multisig account.
//! This is an efficient approach considering that converting from CoreId to AccountId is a one-way operation,
//! so the origin brings the CoreId to dispatchable calls.
//! Converting to a `RawOrigin::Signed` origin for other calls is handled in the runtime.

use crate::{
account_derivation::CoreAccountDerivation,
pallet::{self, Origin, Pallet},
Expand All @@ -13,6 +23,7 @@ pub enum INV4Origin<T: pallet::Config> {
Multisig(MultisigInternalOrigin<T>),
}

/// Internal origin for identifying the multisig CoreId.
#[derive(PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen, Clone, RuntimeDebug)]
pub struct MultisigInternalOrigin<T: pallet::Config> {
pub id: T::CoreId,
Expand All @@ -31,6 +42,7 @@ where
}
}

/// Ensures the passed origin is a multisig, returning [`MultisigInternalOrigin`].
pub fn ensure_multisig<T: Config, OuterOrigin>(
o: OuterOrigin,
) -> Result<MultisigInternalOrigin<T>, BadOrigin>
Expand Down
16 changes: 16 additions & 0 deletions INV4/pallet-inv4/src/voting.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
//! Voting Mechanism.
//!
//! ## Overview
//!
//! This module provides a weighted voting [`Tally`] implementation used for managing the multisig's proposals.
//! Members each have a balance in voting tokens and this balance differentiate their voting power
//! as every vote utilizes the entire `power` of the said member.
//! This empowers decision-making where certain members possess greater influence.

use crate::{origin::INV4Origin, BalanceOf, Config, CoreStorage, Error, Multisig, Pallet};
use codec::{Decode, Encode, HasCompact, MaxEncodedLen};
use core::marker::PhantomData;
Expand Down Expand Up @@ -38,6 +47,7 @@ pub struct Tally<T: Config> {
}

impl<T: Config> Tally<T> {
/// Allows for building a `Tally` manually.
pub fn from_parts(
ayes: Votes<T>,
nays: Votes<T>,
Expand All @@ -51,6 +61,7 @@ impl<T: Config> Tally<T> {
}
}

/// Check if a vote is valid and add the member's total voting token balance to the tally.
pub fn process_vote(
&mut self,
account: T::AccountId,
Expand Down Expand Up @@ -202,19 +213,24 @@ impl<T: Config> CustomPolling<Tally<T>> for Pallet<T> {
}
}

/// Represents a proposal vote within a multisig context.
///
/// This is both the vote and how many voting tokens it carries.
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum Vote<Votes> {
Aye(Votes),
Nay(Votes),
}

/// Type alias for [`Vote`] with [`BalanceOf`].
pub type VoteRecord<T> = Vote<Votes<T>>;

impl<T: Config> Pallet<T>
where
Result<INV4Origin<T>, <T as frame_system::Config>::RuntimeOrigin>:
From<<T as frame_system::Config>::RuntimeOrigin>,
{
/// Returns the minimum support and required approval thresholds of a core.
pub fn minimum_support_and_required_approval(core_id: T::CoreId) -> Option<(Perbill, Perbill)> {
CoreStorage::<T>::get(core_id).map(|core| (core.minimum_support, core.required_approval))
}
Expand Down

0 comments on commit 6db87cb

Please sign in to comment.