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

Reworked INV4 documentation #218

Merged
merged 3 commits into from
Feb 18, 2024
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
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
Loading