前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >heco生态链流动性挖矿dapp系统开发部署(成熟技术)

heco生态链流动性挖矿dapp系统开发部署(成熟技术)

原创
作者头像
开发v_hkkf5566
发布2022-10-24 14:33:33
5270
发布2022-10-24 14:33:33
举报
文章被收录于专栏:技术开发分享

火币生态链 Huobi ECO Chain(HECO)是一个去中心化高效节能公链,也是火币开放平台推出的首个产品,在支撑高性能交易得基础上,实现智能合约的兼容。

Heco是火币开放平台的公链基础设施,未来将成为承载用户、资产和应用的基础平台。

概述

RISC-V service 为 Huobi Chain 提供了一个支持 RISC-V 指令集的虚拟机服务。 用户可以通过该服务自行部署和运行合约,实现强大的自定义功能。

一个 RISC-V 合约的本质是一个 Linux 的 ELF 可执行文件,使用虚拟机运行该合约等同于 Linux 环境下在单核 CPU 下运行这个可执行文件。

理论上任何提供了 RISC-V 后端的语言均可以用来开发合约。就生成代码的体积和质量(运行过程中 cycle 的消耗)而言,目前最成熟的工具是 riscv-gcc。

本文将以 C 语言开发的一个 ERC20 Token 和 Bank 合约为例,为你展示如何编写、部署、调用、测试一个 RISC-V 合约。

Echo 合约示例

我们首先来看一个简单的 echo 合约:

#include <pvm.h>

int main() {

    char args[100] = {0};

    uint64_t args_len = 0;

    pvm_load_args(args, &args_len);

    pvm_ret(args, args_len);

return 0;

}

该合约的作用是将参数的内容原样返回。

将该 C 代码通过 riscv-gcc 编译生成的二进制文件即为我们的合约。

运行合约时,从 main 函数开始。当 main 函数返回值为 0 时,认为合约执行成功,否则合约执行失败。

注意,这个合约中我们引入了 pvm.h,使用了其中的 pvm_load_args 和 pvm_ret 函数。 pvm.h 这个文件中包含了我们与链交互所需要的所有函数。这些函数是通过系统调用实现的,我们将在下节进行详细讲解。

pvm

pvm 支持合约向 ckb-vm 外部发起调用的系统函数集合。在 ckb-vm 内部,你无法直接向链发起任何调用,包括获取 Block,Receipt 这样常用的方法。 所有的对链请求,都必须经过 ckb-vm 的 ecall 指令集完成。pvm 替开发者封装了所有对链请求的逻辑,导出为 C 函数,供合约调用

系统调用

由于 CKB-VM 只是一个 RISC-V 指令集解释器。要实现合约的复杂逻辑,必然要与链进行交互,如解析参数,返回结果,获取链/交易上下文,操作合约状态等。因此我们在 risc-v service 中用系统调用实现了这些交互功能。

下面是 pvm_load_args 函数的实现。调用该函数时,虚拟机会根据寄存器的状态,调用虚拟机外部对应功能的实现函数,并将实现结果通过寄存器和内存写回虚拟机。例如下面的 pvm_load_args 调用时,会将交易中的合约调用参数写到虚拟机内部的内存,然后将内存起始地址和参数长度写到对应的寄存器。

static inline long

__internal_syscall(long n, long _a0, long _a1, long _a2, long _a3, long _a4, long _a5)

{

    register long a0 asm("a0") = _a0;

    register long a1 asm("a1") = _a1;

    register long a2 asm("a2") = _a2;

    register long a3 asm("a3") = _a3;

    register long a4 asm("a4") = _a4;

    register long a5 asm("a5") = _a5;

    register long syscall_id asm("a7") = n;

    asm volatile ("scall": "+r"(a0) : "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(syscall_id));

    return a0;

}

#define syscall(n, a, b, c, d, e, f) \

    __internal_syscall(n, (long)(a), (long)(b), (long)(c), (long)(d), (long)(e), (long)(f))

int pvm_load_args(uint8_t *data, uint64_t *len)

{

    return syscall(SYSCODE_LOAD_ARGS, data, len, 0, 0, 0, 0);

}

RISC-V service 中提供的系统调用有 4 类:

debug 工具。pvm_debug 提供了虚拟机内部的 debug 工具,用户可以打印一段任意的 bytes,方便合约进行调试

入参出参。pvm_load_args 和 pvm_ret 分别提供了合约的入参和出参功能,用户通过前者获取合约调用参数,通过后者返回合约执行结果。合约的入参和出参均为任意的 bytes

获取交易上下文。例如通过 pvm_caller 获取调用该函数的地址,pvm_block_height 获取当前块高度。

链操作:

pvm_set_storage 和 pvm_get_storage 可以用来操作合约的状态空间。每个合约拥有独立的地址,在该地址下拥有独立的状态空间,可以把这个状态空间理解成一个 kv 数据库,用户可以在里面保存任意的数据。合约只能访问和修改自己状态空间内的数据。可以把这个状态空间类比理解成以太坊的 contract storage。

pvm_contract_call 可以用来调用其它 RISC-V 合约,pvm_service_call 可以用来调用 huobi-chain 的其它 build-in service。

所有的系统调用函数都在 pvm.h 文件中,里面有详细的函数文档,读者可以自行查阅。

ERC20 和 Bank 合约代码

理解了系统调用后,我们来看一个真实的 ERC20 合约和 Bank 合约的例子。

我们将源码放到了 GitHub 上,读者可以将示例代码下载到本地进行查看和交互。

$ git clone https://github.com/nervosnetwork/riscv-contract-tutorials.git

$ cd riscv-contract-tutorials/bank

ERC20 合约 是一个符合 ERC20 标准的 token 合约。本合约仅作为说明合约功能之用,读者如有发行自定义资产的需求,建议使用 huobi-chain 的原生资产模块,asset service。

Bank 合约实现了存钱、取钱、查余额等功能,展示了合约如何与其它合约相互操作(例如存钱时需要调用 ERC20 合约的 transfer_from 功能),该合约本身并没有什么现实意义,但可以很容易扩展成一个类似 EtherDelta 这样的 DEX 合约。

合约说明:

序列化:由于合约的入参出参均为任意的 bytes,对于功能复杂的合约,我们可能需要引入一些序列化方法。在上述的代码中,我们使用的是 JSON 格式,因为 JSON 使用广泛,无 schema 限制,且可读性较好。用户也可以根据自己的需求(速度、可读性、大小),选择适合的序列化方案,如 rlp,protobuf,thrift,msgpack 等。

函数分发:合约执行的统一入口为 main 函数,用户如想在一个合约中实现许多不同的功能,可以自行在 main 函数中进行函数分发。上述示例中,即是通过 method 字段的内容来路由到不同的函数进行处理。

除了系统合约外,我们还用到了很多其他的 C 语言库来帮助开发,具体内容可以参见 deps 文件夹。

编译

我们使用 riscv-gcc 来将 C 源码编译成二进制文件。由于 riscv-gcc 工具编译较为复杂,我们提供了打包好的 docker 镜像供读者使用。编译示例如下:

读者可以在 bank 文件夹中运行:

$ make bin_docker

命令来使用 docker 进行编译,在 bin 文件夹下得到的两个二进制文件即为我们的合约。

交互

RISC-V service 提供了两种 exec 和 call 两个交互接口。前者为写接口,可以操作链上数据,需要通过发交易,打包执行,后者为查询接口,可以通过链的 query 功能直接调用。

示例(继续使用刚才的 client):

# 查询接口

> await client.queryService({ serviceName: 'riscv', method: 'call', payload: JSON.stringify({ address, args: JSON.stringify({method: 'total_supply'}) })})

{ isError: false, ret: '"1000000000"' }

# 发交易

> payload = JSON.stringify({ address, args: JSON.stringify({method: 'transfer', recipient: '0000000000000000000000000000000000000000', amount: 100})})

> tx = await client.composeTransaction({ method: 'exec', payload, serviceName: 'riscv' })

> txHash = await client.sendTransaction(account.signTransaction(tx))

> receipt = await client.getReceipt(txHash)

{ txHash:

   '9f18a395972012817c68611e86e182af72f33964ec86c629c8727a9ec1a79daa',

  height: '0000000000000386',

  cyclesUsed: '0000000000072306',

  events: [],

  stateRoot:

   '7f95c8a6d338fbd64de764cdf1870cc60696c4e69af1656de279d7cded6026ed',

  response:

   { serviceName: 'riscv',

     method: 'exec',

     response:{

             code:'0x00',

             succeed_data:'',

             error_message:''

     }

   }

 }

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档