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,
);
}
这个完整示例展示了:
- 创建多签账户组(2-of-3)
- 发起转账提案
- 其他签名者审批提案
- 达到阈值后执行交易
- 包含带时间限制的提案和取消提案的示例
实际使用时需要注意:
- 确保账户有足够存款支付多签操作费用
- 妥善保管各个签名者的私钥
- 合理设置阈值平衡安全性和便利性
- 监控提案状态防止过期