Rust多重签名插件库pallet-multisig的使用:实现安全多方交易与权限管理的Substrate模块

Rust多重签名插件库pallet-multisig的使用:实现安全多方交易与权限管理的Substrate模块

概述

这个模块包含多重签名调用的功能,是一种(可能)有状态的操作,允许多个签名来源(账户)协调并从一个可确定性推导的已知来源调用,该来源可以从账户ID集合和必须批准该调用的账户阈值数量中推导出来。如果阈值仅为1,则这是一个无状态操作。这对于无法或不希望使用加密阈值签名的多重签名钱包非常有用。

接口

可调用函数

  • as_multi - 批准并从由多个签名来源组成的复合来源调用(如果可能的话)
  • approve_as_multi - 批准来自复合来源的调用
  • cancel_as_multi - 取消来自复合来源的调用

许可证:Apache-2.0

完整示例代码

// 导入必要的模块
use frame_support::{decl_module, dispatch::DispatchResult};
use frame_system::{self as system, ensure_signed};

// 定义模块配置
pub trait Config: system::Config {
    /// 事件类型
    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
}

// 定义事件枚举
decl_event!(
    pub enum Event<T> where AccountId = <T as system::Config>::AccountId {
        /// 多重签名调用已创建
        MultisigCreated(AccountId, Vec<AccountId>, u16, CallHash),
        /// 多重签名调用已批准
        MultisigApproved(AccountId, CallHash),
        /// 多重签名调用已执行
        MultisigExecuted(AccountId, CallHash, DispatchResult),
        /// 多重签名调用已取消
        MultisigCancelled(AccountId, CallHash),
    }
);

// 定义存储项
decl_storage! {
    trait Store for Module<T: Config> as Multisig {
        /// 多重签名调用等待批准
        pub Multisigs: double_map hasher(blake2_128_concat) T::AccountId, hasher(blake2_128_concat) CallHash => Option<Multisig<T::AccountId>>;
    }
}

// 定义模块
decl_module! {
    pub struct Module<T: Config> for enum Call where origin: T::Origin {
        fn deposit_event() = default;

        /// 作为多重签名的一部分批准并执行调用
        #[weight = 10_000]
        pub fn as_multi(
            origin,
            threshold: u16,
            other_signatories: Vec<T::AccountId>,
            call_hash: CallHash,
        ) -> DispatchResult {
            let who = ensure_signed(origin)?;
            
            // 实现多重签名逻辑...
            
            Self::deposit_event(RawEvent::MultisigExecuted(who, call_hash, Ok(())));
            Ok(())
        }

        /// 批准作为多重签名的一部分
        #[weight = 10_000]
        pub fn approve_as_multi(
            origin,
            threshold: u16,
            other_signatories: Vec<T::AccountId>,
            call_hash: CallHash,
        ) -> DispatchResult {
            let who = ensure_signed(origin)?;
            
            // 实现批准逻辑...
            
            Self::deposit_event(RawEvent::MultisigApproved(who, call_hash));
            Ok(())
        }

        /// 取消作为多重签名的一部分
        #[weight = 10_000]
        pub fn cancel_as_multi(
            origin,
            threshold: u16,
            other_signatories: Vec<T::AccountId>,
            call_hash: CallHash,
        ) -> DispatchResult {
            let who = ensure_signed(origin)?;
            
            // 实现取消逻辑...
            
            Self::deposit_event(RawEvent::MultisigCancelled(who, call_hash));
            Ok(())
        }
    }
}

安装

在项目目录中运行以下Cargo命令:

cargo add pallet-multisig

或者在Cargo.toml中添加以下行:

pallet-multisig = "42.0.0"

完整示例DEMO

// 完整的多重签名实现示例
use frame_support::{
    decl_module, decl_event, decl_storage,
    dispatch::DispatchResult,
    traits::Currency,
    weights::Weight,
};
use frame_system::{self as system, ensure_signed};
use sp_runtime::traits::Hash;
use codec::{Encode, Decode};

// 定义存储结构体
#[derive(Encode, Decode, Clone, PartialEq, Eq)]
pub struct Multisig<AccountId, BlockNumber> {
    /// 当多重签名调用被批准时
    when: BlockNumber,
    /// 账户ID集合
    who: Vec<AccountId>,
    /// 所需阈值
    threshold: u16,
    /// 调用哈希
    deposit: BalanceOf,
    /// 调用数据长度
    length: u32,
}

// 定义模块配置
pub trait Config: system::Config {
    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
    type Currency: Currency<Self::AccountId>;
}

// 定义事件
decl_event!(
    pub enum Event<T> where AccountId = <T as system::Config>::AccountId {
        MultisigCreated(AccountId, Vec<AccountId>, u16, T::Hash),
        MultisigApproved(AccountId, T::Hash),
        MultisigExecuted(AccountId, T::Hash, DispatchResult),
        MultisigCancelled(AccountId, T::Hash),
    }
);

// 定义存储
decl_storage! {
    trait Store for Module<T: Config> as Multisig {
        pub Multisigs: double_map
            hasher(blake2_128_concat) T::AccountId,
            hasher(blake2_128_concat) T::Hash
            => Option<Multisig<T::AccountId, T::BlockNumber>>;
    }
}

// 实现模块
decl_module! {
    pub struct Module<T: Config> for enum Call where origin: T::Origin {
        fn deposit_event() = default;

        #[weight = 10_000 + T::DbWeight::get().reads_writes(1,1)]
        pub fn as_multi(
            origin,
            threshold: u16,
            other_signatories: Vec<T::AccountId>,
            call_hash: T::Hash,
        ) -> DispatchResult {
            let who = ensure_signed(origin)?;
            let signatories = Self::ensure_multisig(&who, &other_signatories, threshold)?;
            
            // 检查是否达到阈值
            if signatories.len() >= threshold as usize {
                // 执行调用逻辑
                Self::deposit_event(RawEvent::MultisigExecuted(who, call_hash, Ok(())));
            } else {
                // 存储待批准的多重签名
                Multisigs::<T>::insert(&who, &call_hash, Multisig {
                    when: system::Module::<T>::block_number(),
                    who: signatories,
                    threshold,
                    deposit: T::Currency::minimum_balance(),
                    length: 0,
                });
                Self::deposit_event(RawEvent::MultisigCreated(who, other_signatories, threshold, call_hash));
            }
            Ok(())
        }

        #[weight = 10_000]
        pub fn approve_as_multi(
            origin,
            threshold: u16,
            other_signatories: Vec<T::AccountId>,
            call_hash: T::Hash,
        ) -> DispatchResult {
            let who = ensure_signed(origin)?;
            let signatories = Self::ensure_multisig(&who, &other_signatories, threshold)?;
            
            // 更新存储中的批准状态
            Multisigs::<T>::mutate(&who, &call_hash, |maybe_multisig| {
                if let Some(multisig) = maybe_multisig {
                    if !multisig.who.contains(&who) {
                        multisig.who.push(who.clone());
                    }
                }
            });
            
            Self::deposit_event(RawEvent::MultisigApproved(who, call_hash));
            Ok(())
        }

        #[weight = 10_000]
        pub fn cancel_as_multi(
            origin,
            threshold: u16,
            other_signatories: Vec<T::AccountId>,
            call_hash: T::Hash,
        ) -> DispatchResult {
            let who = ensure_signed(origin)?;
            let _ = Self::ensure_multisig(&who, &other_signatories, threshold)?;
            
            // 从存储中移除多重签名
            Multisigs::<T>::remove(&who, &call_hash);
            
            Self::deposit_event(RawEvent::MultisigCancelled(who, call_hash));
            Ok(())
        }
    }
}

impl<T: Config> Module<T> {
    // 验证多重签名参数
    fn ensure_multisig(
        who: &T::AccountId,
        others: &[T::AccountId],
        threshold: u16,
    ) -> Result<Vec<T::AccountId>, DispatchError> {
        // 验证阈值有效性
        if threshold == 0 || threshold > others.len() as u16 + 1 {
            return Err(Error::<T>::InvalidThreshold.into());
        }
        
        // 检查重复账户
        let mut signatories = others.to_vec();
        signatories.push(who.clone());
        signatories.sort();
        signatories.dedup();
        
        if signatories.len() != others.len() + 1 {
            return Err(Error::<T>::DuplicateSignatories.into());
        }
        
        Ok(signatories)
    }
}

1 回复

Rust多重签名插件库pallet-multisig的使用:实现安全多方交易与权限管理的Substrate模块

以下是基于提供内容的完整示例demo,展示如何使用pallet-multisig实现一个完整的多重签名流程:

// 导入必要的依赖
use frame_support::{dispatch::DispatchResult, traits::Currency};
use frame_system::Config as SystemConfig;
use sp_core::sr25519::Public;
use sp_runtime::{traits::StaticLookup, AccountId32};

// 假设我们有以下账户
let alice: AccountId32 = AccountId32::new([1u8; 32]);
let bob: AccountId32 = AccountId32::new([2u8; 32]); 
let charlie: AccountId32 = AccountId32::new([3u8; 32]);
let recipient: AccountId32 = AccountId32::new([4u8; 32]);
let amount: u128 = 100_000_000;

// 1. 创建多签账户 (2-of-3)
let signatories = vec![alice.clone(), bob.clone(), charlie.clone()];
let threshold = 2;

// 2. 创建一个转账提案
let call = Call::Balances(BalancesCall::transfer {
    dest: recipient.clone(),
    value: amount,
});

// 3. Alice发起提案(不设置时间限制)
Multisig::as_multi(
    RuntimeOrigin::signed(alice.clone()),
    threshold,
    vec![bob.clone(), charlie.clone()], // 其他签名者
    None, // 不设置时间限制
    Box::new(call.clone()),
);

// 获取提案哈希(实际应用中从事件或存储中获取)
let call_hash = blake2_256(&call.encode());

// 4. Bob审批提案
Multisig::approve_as_multi(
    RuntimeOrigin::signed(bob.clone()),
    threshold,
    vec![alice.clone(), charlie.clone()], // 其他签名者
    None, // 时间点
    call_hash,
);

// 5. 检查是否达到阈值(此时已满足2-of-3)
if let Some((_, approvals)) = Multisig::multisigs((multi_account(), call_hash)) {
    if approvals.len() >= threshold as usize {
        // 6. Charlie执行交易(任何参与者都可以执行)
        Multisig::as_multi_threshold_1(
            RuntimeOrigin::signed(charlie.clone()),
            vec![alice.clone(), bob.clone()],
            Box::new(call),
        );
    }
}

// 辅助函数:生成多签账户地址
fn multi_account() -> AccountId32 {
    let signatories = vec![
        AccountId32::new([1u8; 32]),
        AccountId32::new([2u8; 32]),
        AccountId32::new([3u8; 32]),
    ];
    let threshold = 2;
    let encoded = (threshold, signatories).encode();
    let hash = blake2_256(&encoded);
    AccountId32::new(hash)
}

// 带时间限制的提案示例
fn time_limited_proposal() {
    let lifetime = 24 * 60 * 60; // 24小时(秒数)
    let call = Call::System(SystemCall::remark { remark: vec![1, 2, 3] });
    
    Multisig::as_multi(
        RuntimeOrigin::signed(alice.clone()),
        2,
        vec![bob.clone(), charlie.clone()],
        Some(frame_system::Pallet::<T>::block_number() + lifetime.into()),
        Box::new(call),
    );
}

// 取消提案示例
fn cancel_proposal() {
    let call = Call::Balances(BalancesCall::transfer {
        dest: recipient.clone(),
        value: amount,
    });
    let call_hash = blake2_256(&call.encode());
    
    Multisig::cancel_as_multi(
        RuntimeOrigin::signed(alice.clone()),
        2,
        vec![bob.clone(), charlie.clone()],
        call_hash,
    );
}

这个完整示例展示了:

  1. 创建多签账户组(2-of-3)
  2. 发起转账提案
  3. 其他签名者审批提案
  4. 达到阈值后执行交易
  5. 包含带时间限制的提案和取消提案的示例

实际使用时需要注意:

  • 确保账户有足够存款支付多签操作费用
  • 妥善保管各个签名者的私钥
  • 合理设置阈值平衡安全性和便利性
  • 监控提案状态防止过期
回到顶部