Rust代理管理库pallet-proxy的使用,实现高效账户权限委托与链上操作代理

Rust代理管理库pallet-proxy的使用,实现高效账户权限委托与链上操作代理

Proxy Module

这是一个允许账户授予其他账户权限的模块,可以从其签名来源处派发特定类型的调用。

被授予权限的账户可能需要在执行前一定时间提前宣布他们希望执行的操作。在这种情况下,目标账户可能会拒绝该宣布,从而否决执行。

概述

pallet-proxy模块提供了以下主要功能:

  • 账户可以委托权限给其他账户
  • 代理账户可以代表原始账户执行特定类型的操作
  • 支持延迟执行机制,允许原始账户审查和否决代理操作
  • 可配置不同的代理类型和权限级别

接口

可调度函数

许可证: Apache-2.0

完整示例代码

use frame_support::{assert_ok, traits::Currency};
use sp_core::H256;
use sp_runtime::{
    traits::{BlakeTwo256, IdentityLookup},
    BuildStorage,
};

type Block = frame_system::mocking::MockBlock<Test>;

// 配置测试环境
frame_support::construct_runtime!(
    pub enum Test {
        System: frame_system,
        Balances: pallet_balances,
        Proxy: pallet_proxy,
    }
);

impl frame_system::Config for Test {
    type BaseCallFilter = frame_support::traits::Everything;
    type BlockWeights = ();
    type BlockLength = ();
    type DbWeight = ();
    type RuntimeOrigin = RuntimeOrigin;
    type RuntimeCall = RuntimeCall;
    type Nonce = u64;
    type Hash = H256;
    type Hashing = BlakeTwo256;
    type AccountId = u64;
    type Lookup = IdentityLookup<Self::AccountId>;
    type Block = Block;
    type RuntimeEvent = RuntimeEvent;
    type BlockHashCount = ();
    type Version = ();
    type PalletInfo = PalletInfo;
    type AccountData = pallet_balances::AccountData<u64>;
    type OnNewAccount = ();
    type OnKilledAccount = ();
    type SystemWeightInfo = ();
    type SS58Prefix = ();
    type OnSetCode = ();
    type MaxConsumers = frame_support::traits::ConstU32<16>;
}

impl pallet_balances::Config for Test {
    type Balance = u64;
    type DustRemoval = ();
    type RuntimeEvent = RuntimeEvent;
    type ExistentialDeposit = ();
    type AccountStore = System;
    type WeightInfo = ();
    type MaxLocks = ();
    type MaxReserves = ();
    type ReserveIdentifier = [u8; 8];
    type HoldIdentifier = ();
    type FreezeIdentifier = ();
    type MaxHolds = ();
    type MaxFreezes = ();
}

impl pallet_proxy::Config for Test {
    type RuntimeEvent = RuntimeEvent;
    type RuntimeCall = RuntimeCall;
    type Currency = Balances;
    type ProxyType = ();
    type ProxyDepositBase = ();
    type ProxyDepositFactor = ();
    type MaxProxies = ();
    type WeightInfo = ();
    type MaxPending = ();
    type CallHasher = BlakeTwo256;
    type AnnouncementDepositBase = ();
    type AnnouncementDepositFactor = ();
    type MaxPending = ();
}

// 测试用例
#[test]
fn proxy_works() {
    new_test_ext().execute_with(|| {
        // 创建账户
        let alice = 1;
        let bob = 2;
        let charlie = 3;
        
        // 给Alice一些余额
        Balances::make_free_balance_be(&alice, 100);
        
        // Alice添加Bob作为她的代理
        assert_ok!(Proxy::add_proxy(
            RuntimeOrigin::signed(alice),
            bob,
            (),
            0
        ));
        
        // Bob现在可以作为Alice的代理执行操作
        let call = Box::new(RuntimeCall::Balances(pallet_balances::Call::transfer {
            dest: charlie,
            value: 10,
        }));
        
        assert_ok!(Proxy::proxy(
            RuntimeOrigin::signed(bob),
            alice,
            None,
            call
        ));
        
        // 检查转账是否成功
        assert_eq!(Balances::free_balance(&charlie), 10);
    });
}

#[test]
fn delayed_proxy_works() {
    new_test_ext().execute_with(|| {
        // 创建账户
        let alice = 1;
        let bob = 2;
        let charlie = 3;
        
        // 给Alice一些余额
        Balances::make_free_balance_be(&alice, 100);
        
        // Alice添加Bob作为她的代理
        assert_ok!(Proxy::add_proxy(
            RuntimeOrigin::signed(alice),
            bob,
            (),
            0
        ));
        
        // Bob宣布一个代理操作
        let call = Box::new(RuntimeCall::Balances(pallet_balances::Call::transfer {
            dest: charlie,
            value极链上治理等。

1 回复

以下是根据提供内容整理的完整示例demo,展示了pallet-proxy的完整使用流程:

//! 完整演示pallet-proxy的使用:从创建代理到通过代理执行操作

use frame_support::{dispatch::DispatchResult, traits::Currency};
use frame_system::RawOrigin;
use pallet_proxy::{Call as ProxyCall, ProxyType};
use sp_core::crypto::AccountId32;
use sp_runtime::traits::StaticLookup;

// 类型别名简化
type AccountId = AccountId32;
type Balance = u128;
type RuntimeCall = crate::Call;

/// 演示完整的代理流程
pub fn demo_proxy_workflow() -> DispatchResult {
    // 1. 准备账户
    let primary_account: AccountId = AccountId32::new([1u8; 32]);
    let proxy_account: AccountId = AccountId32::new([2u8; 32]);
    let recipient_account: AccountId = AccountId32::new([3u8; 32]);
    
    // 2. 主账户添加代理 (NonTransfer权限)
    let add_proxy = ProxyCall::add_proxy {
        delegate: proxy_account.clone(),
        proxy_type: ProxyType::NonTransfer,
        delay: 0,
    };
    RuntimeCall::Proxy(add_proxy)
        .dispatch(RawOrigin::Signed(primary_account.clone()).into())?;

    // 3. 通过代理执行转账操作 (会失败,因为代理类型是NonTransfer)
    let transfer_call = RuntimeCall::Balances(pallet_balances::Call::transfer {
        dest: recipient_account.clone(),
        value: 100u128,
    });
    
    let proxy_transfer = ProxyCall::proxy {
        real: primary_account.clone(),
        force_proxy_type: None,
        call: Box::new(transfer_call),
    };
    let _ = RuntimeCall::Proxy(proxy_transfer)
        .dispatch(RawOrigin::Signed(proxy_account.clone()).into());

    // 4. 主账户添加Any权限的代理
    let add_any_proxy = ProxyCall::add_proxy {
        delegate: proxy_account.clone(),
        proxy_type: ProxyType::Any,
        delay: 0,
    };
    RuntimeCall::Proxy(add_any_proxy)
        .dispatch(RawOrigin::Signed(primary_account.clone()).into())?;

    // 5. 再次通过代理执行转账操作 (这次会成功)
    let transfer_call = RuntimeCall::Balances(pallet_balances::Call::transfer {
        dest: recipient_account,
        value: 100u128,
    });
    
    let proxy_transfer = ProxyCall::proxy {
        real: primary_account.clone(),
        force_proxy_type: None,
        call: Box::new(transfer_call),
    };
    RuntimeCall::Proxy(proxy_transfer)
        .dispatch(RawOrigin::Signed(proxy_account.clone()).into())?;

    // 6. 创建匿名代理
    let create_pure = ProxyCall::create_pure {
        proxy_type: ProxyType::Governance,
        delay: 10,
        index: 0,
    };
    RuntimeCall::Proxy(create_pure)
        .dispatch(RawOrigin::Signed(primary_account.clone()).into())?;

    // 7. 移除代理
    let remove_proxy = ProxyCall::remove_proxy {
        delegate: proxy_account,
        proxy_type: ProxyType::Any,
        delay: 0,
    };
    RuntimeCall::Proxy(remove_proxy)
        .dispatch(RawOrigin::Signed(primary_account).into())?;

    Ok(())
}

/// 演示质押代理场景
pub fn demo_staking_proxy() -> DispatchResult {
    let primary_account: AccountId = AccountId32::new([1u8; 32]);
    let staking_manager: AccountId = AccountId32::new([4u8; 32]);
    
    // 1. 添加质押代理
    let add_proxy = ProxyCall::add_proxy {
        delegate: staking_manager.clone(),
        proxy_type: ProxyType::Staking,
        delay: 0,
    };
    RuntimeCall::Proxy(add_proxy)
        .dispatch(RawOrigin::Signed(primary_account.clone()).into())?;

    // 2. 通过代理执行质押操作
    let bond_call = RuntimeCall::Staking(pallet_staking::Call::bond {
        value: 1000u128,
        payee: pallet_staking::RewardDestination::Staked,
    });
    
    let proxy_call = ProxyCall::proxy {
        real: primary_account.clone(),
        force_proxy_type: Some(ProxyType::Staking),
        call: Box::new(bond_call),
    };
    RuntimeCall::Proxy(proxy_call)
        .dispatch(RawOrigin::Signed(staking_manager.clone()).into())?;

    // 3. 带延迟的代理调用示例
    let add_delayed_proxy = ProxyCall::add_proxy {
        delegate: staking_manager.clone(),
        proxy_type: ProxyType::Staking,
        delay: 5, // 5个区块的延迟
    };
    RuntimeCall::Proxy(add_delayed_proxy)
        .dispatch(RawOrigin::Signed(primary_account.clone()).into())?;

    let bond_call = RuntimeCall::Staking(pallet_staking::Call::bond {
        value: 500u128,
        payee: pallet_staking::RewardDestination::Staked,
    });
    
    let delayed_proxy_call = ProxyCall::proxy {
        real: primary_account.clone(),
        force_proxy_type: Some(ProxyType::Staking),
        call: Box::new(bond_call),
    };
    RuntimeCall::Proxy(delayed_proxy_call)
        .dispatch(RawOrigin::Signed(staking_manager).into())?;

    // 4. 清理代理
    let remove_proxy = ProxyCall::remove_proxy {
        delegate: staking_manager,
        proxy_type: ProxyType::Staking,
        delay: 0,
    };
    RuntimeCall::Proxy(remove_proxy)
        .dispatch(RawOrigin::Signed(primary_account).into())?;

    Ok(())
}

// 辅助函数:计算纯代理账户地址
fn compute_pure_proxy(
    original: &AccountId,
    proxy_type: ProxyType,
    index: u16,
    maybe_when: Option<u64>,
) -> AccountId {
    // 实际实现应使用pallet_proxy提供的算法
    // 这里简化为示例
    let mut bytes = original.0;
    bytes[0] = bytes[0].wrapping_add(proxy_type as u8);
    bytes[1] = bytes[1].wrapping_add(index as u8);
    if let Some(when) = maybe_when {
        bytes[2] = bytes[2].wrapping_add(when as u8);
    }
    AccountId32::from(bytes)
}

这个完整示例包含以下功能演示:

  1. 基本代理流程:

    • 添加不同类型的代理
    • 通过代理执行不同权限级别的操作
    • 权限验证(NonTransfer代理不能执行转账)
    • 移除代理
  2. 质押代理专用场景:

    • 设置质押专用代理
    • 通过代理执行质押操作
    • 带延迟执行的代理调用
  3. 辅助功能:

    • 纯代理账户计算(简化版)
    • 错误处理(示例中省略了部分错误处理细节)

注意:实际使用时需要根据具体链的Runtime配置调整类型和调用方式。

回到顶部