2 个稳定版本

1.0.1 2023年8月15日

#35 in #ink

Apache-2.0

47KB
515

HookPoints 模块

许可: Apache 2.0   Rust   Polkadot

HookPoints 模块提供了一个灵活的框架,用于与 Substrate 运行时中注册的回调进行交互。用户可以注册全局回调和特定回调,提供了一种强大的机制来调用来自不同开发人员或协议的扩展。

概念概述

钩点借鉴了 web2 时代,当时像 Magento、WordPress 和 Shopify 这样的平台因可扩展的插件系统而蓬勃发展。同样,Substrate 提供了一个专业的区块链核心,强调 Unix 原则“正确地做一件事”。钩点在此基础上构建,引入了一个插件系统,增强了核心功能,并允许添加新的创新。

使用 ink! 和 pallet_contracts,钩点实现了 Substrate 模块和 ink 模块之间的无缝集成。它们抽象掉了字节处理和编码等挑战,为开发者提供了一个高级接口。这连接了 Substrate 和 ink 环境,简化了开发。

此外,还有一个 CLI 工具可用于钩点。它可以自动化 Substrate 和 ink 的代码生成,处理测试和其他基本任务。这减少了样板代码,让开发者专注于核心逻辑,并简化了区块链开发过程。

功能

  • 全局回调:允许用户设置一个默认的回调,可以全局触发。

  • 特定回调:通过指定在特定条件下触发哪个回调来覆盖默认设置。此功能可以实现更细致的控制,并可用于集成不同的扩展或协议。

  • 灵活编码:该模块旨在直接使用字节,允许用户以他们首选的方式对数据进行编码后再发送。

  • 运行时基准测试:内置对运行时基准测试的支持,以确保性能和效率。

先决条件

确保您的运行时中包含 pallet_contracts,因为 HookPoints 依赖于它与合约交互。

安装 CLI 工具

> cargo install hookpoints-cli

安装

要将HookPoints组件添加到您的运行时中,请在Cargo.toml中将它作为依赖项添加,然后将其包含在运行时的组件列表中。

impl pallet_hookpoints::Config for Runtime { 
    // your event of choice 
    type RuntimeEvent = RuntimeEvent;
    // max length of a callback, this is typically ContractName::function_name 
    type MaxLengthId = ConstU32<64>;
    // runtime weights, if in doubt use ours 
    type WeightInfo = pallet_hookpoints::weights::SubstrateWeight<Runtime>;
}

使用方法

切换到Substrate根目录并运行

hookpoints-cli configure

这将引导您完成创建hook points的设置过程。您为项目命名(这将成为ink!开发者需要实现的特质的名称)。

之后,您可以交互式地创建hook points

  • 添加一个名称,这是要调用的ink函数
  • 按名称和类型添加参数
  • 添加一个返回值和默认值(默认值将用于生成样板代码,可以是原始值或定义的参数之一)。

例如,在GenesisDao中,我们到处都使用hook points。

例如,我们想要有一个hook point,让DAO所有者可以更改其代币持有者的投票权,以实现替代投票模型、添加归属钱包等。因此,我们有一个名为on_vote的hook point,添加了参数voter作为AccountId,以及voting_power作为Balance。我们返回更改后的voting_power作为余额,默认情况下只是未更改的voting_power

CLI将在您的根目录中创建一个hookpoints.json文件。

运行

hookpoints-cli generate

然后CLI将生成

  • 一个ink!特质,其中包含所有回调函数进入ink!宇宙
  • 一个ink!样板合同,带有所有回调和默认值、完整的单元测试和功能e2e测试。
  • 一个ink!测试合同,您可以用它来测试substrate代码中的集成
  • 在配置了钩子的每个组件中,都有一个抽象化的hooks.rs文件,其中包含简单函数,您可以在substrate内部调用。无需字节争夺,无需解码/编码,无需与ink/substrate连接交互。它只需工作。

转到您想实现hook point的组件,只需使用提供的hooks.rs中的函数即可。

在我们的示例中,这将是

let new_voting_power = on_vote(dao_owner, original_caller_of_the_extrinsic, voter, voting_power);

就是这样。

外设

用户可以注册一个全局回调,这将成为任何调用除非为该特定交互定义了特定回调之外的默认交互点。

这通常是由“拥有”您应用程序一部分的人执行的,他想要改变其行为。在上述示例中是DAO。

注册全局回调

用户还可以定义全局回调,当未定义特定回调时,将用作默认回调。

assert_ok!(HookPoints::register_global_callback(origin, contract_address));

注册特定回调

用户还可以定义特定回调,当触发时,将优先于全局回调。

assert_ok!(HookPoints::register_specific_callback(
    RuntimeOrigin::signed(ALICE).into(),
    contract_address,
    selector_id_as_stringd
));

ID可以在使用hookpoints-cli(强烈推荐)时作为标签在contract.json中找到,其中ChoosenName:choosen_hookpoint_name

测试

以下是一个典型测试可能的样子

#[test]
fn execute_callback() {
    new_test_ext().execute_with(|| {
        let creator = AccountId32::new([1u8; 32]);
        
        // your compiled wasm ink contract!
        let contract_path = "test_contract.wasm";

        let contract_deployment = HookPoints::prepare_deployment("new", creator.clone(), std::fs::read(contract_path).unwrap(), vec![])
            .add_init_arg(42u128);

        let contract_address = HookPoints::install(contract_deployment)
            .expect("Contract installation should be successful");

        // Register the contract for callbacks
        HookPoints::register_global_callback(RuntimeOrigin::signed(creator.clone()), contract_address.clone()).unwrap();

        // Create a HookPoint for the "multiply" function
        let hookpoint = HookPoints::create("multiply", creator.clone(), creator.clone())
            .add_arg(2u128);

        // Execute the "multiply" function using the HookPoint
        let result: Result<u128, _> = HookPoints::execute(hookpoint);

        // Ensure the result is Ok and equals to 84 (since 42 * 2 = 84)
        assert_eq!(result.unwrap(), 84);
    });
}

这使用了来自./contract文件夹的我们的测试合同。

许可

根据Apache License,Version 2.0许可。您只能在本许可的范围内使用此组件。

依赖项

20–35MB
~574K SLoC