简介
本章将深入源码,为大家分析eos的系统合约eosio.token的实现细节。eosio.token是eos的发币合约,这个合约主要实现了EOS代币的创建、发行、转账等功能。
主要合约方法
eosio.token系统合约的源码在eos/contracts/eosio.token中,eos项目方独立开了github,负责eosio.token合约的更新和维护。
eosio.token合约,在eosio.token.hpp头文件中,主要定义了以下三个合约方法:
- create:负责创建资产
- issue:负责发行资产
- transfer:负责资产转账
eosio.token.hpp头文件中定义的主要方法
下面,将为大家一一介绍各个方法的功能实现细节
create方法
create方法的实现在eosio.token.cpp中,需要传入两个参数完成币种创建:
- issuer:资产发行账户
- maximum_supply:最大发行量
create方法实现细节
ceate方法调用实例如下,eosio.token合约部署在eosio.token账户,资产发行账户为eosio,最大发行量10亿,币种精度4,币种符号EOS
cleos push action eosio.token create '[ "eosio", "1000000000.0000 EOS"]' -p eosio.token
深入源码来看,create方法通过以下步骤完成了资产的创建:
- 获取eosio.token合约部署账户的授权
- 判断币种符号 (sysmol name) 是否合法,币种符号必须是大写字母,长度小于8位
- 判断发行量是否越界且是否为正数,支持的最大发行量 amount <= 2^62-1
- 查询statstable表,判断代币符号是否存在,eosio.token支持发行多资产
- 建表,将代币符号、发行总量、发行方存入数据库
以上步骤,主要逻辑详见:eos/contracts/eosiolib/symbol.hpp、eos/contracts/eosiolib/asset.hpp、eos/contracts/eosiolib/multi_index.hpp
issue方法
create方法的实现在eosio.token.cpp中,需要传入两个参数完成币种的发行:
- to:发行金额打入的账户
- quantity:发行金额
- memo:交易备注
issue方法实现细节
issue方法调用实例如下,资产发行账户eosio授权,将10亿EOS打入eosio账户中,备注是test issue
cleos push action eosio.token issue '[ "eosio", "1000000000.0000 EOS", "test issue"]' -p eosio
深入源码来看,issue方法通过以下步骤完成了资产的发行:
- 前置检查:查表判断币种是否存在、memo最大长度不能超过256字节
- 查表获取币种信息:发行账户、最大发行量、币种精度、币种符号
- 获取发行账户的授权
- 前置检查:发行金额为正数且不超过最大发行量、币种精度校验
- 更新表,增加发行资产的余额
- 如果to账户和发行账户不一致,调用内联合约转账
以上步骤,主要逻辑详见:eos/contracts/eosiolib/symbol.hpp、eos/contracts/eosiolib/asset.hpp、eos/contracts/eosiolib/multi_index.hpp、eos/contracts/eosiolib/currency.hpp
这里特别要说明最后一个步骤,内联合约的调用。实现逻辑在eos/contracts/eosiolib/currency.hpp
issue方法最后调用的inline合约方法 - eosio.token的transfer方法
eos智能合约之间,通过action通信,共享数据库存储。一个智能合约,可以异步的读取另一个智能合约的数据库状态。智能合约有两种通信状态:
- Inline 内联合约。指的是在一个已有的交易里调用其他智能合约的方法。如果当前交易失败了,内联合约的执行也不受影响,内联合约执行失败或成功并没有通知,但执行成功会上链
- Deffered 延迟合约。指的是不是立即执行,而是在未来的某个时间计划去执行的合约。当前,可以构造一个交易去发送延迟合约交易,也可以发送一个交易去取消延迟合约。延迟合约也不能保证执行成功,执行失败或者成功并没有通知,但执行成功会上链
在issue方法中,当发现issuer账户和to账户不一致时,会调用lnline合约,触发eosio.token合约的transfer方法,将发行的金额转入to账户。当我们执行issue方法后,会发现除了产生issue的action后,还会有transfer的action:
inline合约方法调用成功后会上链
transfer方法
transfer方法的实现在eosio.token.cpp中,需要传入四个参数完成币种的转账:
- from:出币账户
- to:入金账户
- quantity:转账金额
- memo:交易备注
transfer方法的实现细节
深入源码来看,issue方法通过以下步骤完成了资产的转账:
- 前置检查:判断是否转账给自己、判断to账户是否存在
- 获得from账户的授权
- 获取币种信息:发行账户、最大发行量、币种精度、币种符号
- 分别通知from、to账户合约调用结果
- 前置检查:转账金额为正且不超过最大发行量、币种精度正确、memo长度不大于256
- 加减from和to地址账户余额
以上步骤,主要逻辑详见:eos/contracts/eosiolib/symbol.hpp、eos/contracts/eosiolib/asset.hpp、eos/contracts/eosiolib/multi_index.hpp、eos/contracts/eosiolib/currency.hpp
这里特别要说明第四个步骤,通知from、to账户合约调用结果。实现逻辑在eos/contracts/eosiolib/action.hpp、eos/libraries/chain/apply_context.cpp、eos/libraries/chain/transaction_context.cpp
eosio.token的transfer方法调用require_recipient
将相关账户放入_notified数组中
封装好trace通知内容,并执行通知
trace通知内容封装过程
首先,eosio智能合约的执行结果,会分别通知from、to、eosio.token。我们发送转账交易,会发现有三条actions,通知所有涉及的账户,这是eos系统合约设计的通知机制。大家在执行cleos get actions获取交易结果时,可以通过receiver过滤出发给自己账户的通知。
transfer方法调用成功后,发送给所有相关账户的通知上链
receipt的通知详情,调用cleos get actions bp1 -j 获取:
后记
本章为大家介绍了eosio.token合约的实现细节,从源码层面为大家分析了eosio.token如何实现资产的创建、发行、转账的。
目前,基于eos发行资产的项目方,都会参考eosio.token的实现,去实现自己的发币合约。但是,由于eos是一个刚刚完成主网上线的新项目,有许多隐藏的bug有待发现。例如,我们最后讲到的transfer方法的receipt通知机制,由于实现过于复杂,前段时间就被慢雾发现了有严重的安全漏洞,会使交易所的提币账户ram被耗尽,详细issue。
因此,大家在编写自己的发币合约时,务必要检查好相关逻辑,以免被恶意攻击造成资产的损失。eos社区目前还没有出合约代码的标准规范,但我相信不久的将来,一定会有详细的规范出台。
接下来的文章,将继续从源码层面为大家讲解eos的系统合约实现细节,尽请期待!