EOSIO Dawn 3.0 智能合约
EOSIO 智能合约介绍
必须的背景知识
C / C++ 经验
基于 EOSIO 的区块链使用 WebAssembly (WASM) 执行用户生成的应用程序和代码。WASM 是一项新兴的网络标准,得到了谷歌,微软,苹果等公司的广泛支持。目前,用于构建编译为 WASM 的应用程序的最成熟工具链是使用 C/C++ 编译器的 clang/llvm 。
其他第三方开发的工具链包括:Rust,Python 和 Solidity。虽然这些其他语言看起来可能更简单,但它们的性能可能会影响你可以构建的应用程序的规模。我们预计 C++ 将成为开发高性能和安全智能合约的最佳语言,并计划在可预见的将来使用 C++。
Linux / Mac OS 经验
EOSIO 软件支持以下环境:
- Amazon 2017.09 and higher
- Centos 7
- Fedora 25 and higher (Fedora 27 recommended)
- Mint 18
- Ubuntu 16.04 (Ubuntu 16.10 recommended)
- MacOS Darwin 10.12 and higher (MacOS 10.13.x recommended)
命令行知识
与 EOSIO 一起提供的各种工具,要求你具有基本的命令行知识才能与之交互。
EOSIO 智能合约基础知识
交互模型
EOSIO 智能合约以动作(actions)和共享内存数据库访问的形式彼此交互,
例如,合约可以读取其他合约数据库的状态,只要它包含在具有异步事务的读取范围内即可。
异步通信可能会导致资源限制算法会处理的垃圾邮件(spam)。
在合约中可以定义两种通信模式:
内联。内联保证与当前交易一起执行或展开; 无论成功或失败,都不会通知任何通知。内联与原有交易拥有相同的作用范围和权限。
延期。延期交互将由出块人酌情决定如何执行; 可以传递交互结果或者可以简单地超时。延期交互可以有不同的作用范围,并带有发送它们的合约指定的权限。
动作 vs 交易
一个动作表示单个操作,而一个交易是一个或多个动作的集合。合约和账户以动作的形式进行交流。动作可以单独发送,也可以组合的形式发送,如果它们打算作为一个整体来执行。
1 个动作的交易.
1 | { |
多动作交易, 这些动作会同时成功或失败.
1 | { |
动作名称限制
动作类型实际上是 base32 编码的 64 位整数 。这意味着对于前 12 个字符它们仅限于字符 a-z,1-5 和 ‘.’ 。如果有第 13 个字符,则它仅限于前 16 个字符(’.’和 a-p)。
交易确认
接收交易哈希并不意味着交易已被确认,它只意味着节点认为没有错误并接受了它,这也意味着其他出块人很可能会接受它。
通过确认,你应该在交易历史中看到包含确认交易所属区块的交易。
智能合约文件
为了简单起见 ,我们创建了一个名为 eosiocpp 的工具,可以用来启动一个新的合约。eosiocpp 也将为你创建 3 个智能合约文件,并提供基本框架。
1 | $ eosiocpp -n ${contract} |
上面的命令将新建一个空项目,项目目录下有 3 个文件:
1 | ${contract}.abi ${contract}.hpp ${contract}.cpp |
hpp
${contract}.hpp
是被 .cpp
引用的,包含变量,常量和函数定义的头文件。
cpp
${contract}.cpp
文件是包含智能合约功能函数的源文件。
如果你使用 eosiocpp
工具生成 .cpp
文件,生成的 .cpp 文件与下面的相似:
1 |
|
在这个例子中,你可以看到有两个函数, init
和 apply
。
他们所做的只是记录动作,不做其他检查。只要出块人允许,任何人都可以随时提供任何操作。在没有任何所需的签名的情况下,合约将按照消耗的带宽收费。
init
init
函数只会在初始部署时执行一次。用来初始化智能合约的变量,例如,代币合约的代币发行量。
apply
apply
是动作处理器,它监听所有传入的动作并根据函数内的逻辑作出反应。该 apply 函数需要两个输入参数,code
和 action
。
代码过滤器
为了应对特定的动作,apply
函数按以下方式编写。你也可以通过省略代码过滤器来编写对通用动作的响应。
1 | if (code == N(${contract_name}) { |
你也可以在代码块中定义对各个操作的响应。
动作过滤器
为了响应某个特定动作,apply
函数按照以下方式编写。这通常与代码过滤器结合使用。
1 | if (action == N(${action_name}) { |
wast
任何要部署到 EOSIO 区块链的程序都必须编译为 WASM 格式。这是区块链接受的唯一格式。
准备好 CPP 文件后,可以使用 eosiocpp
工具将其编译为 WASM(.wast)的文本版本。
1 | $ eosiocpp -o ${contract}.wast ${contract}.cpp |
abi
应用程序二进制接口(ABI)是一种基于 JSON 的描述,介绍如何将用户动作在 JSON 和二进制表达之间转换。ABI 还介绍了如何将数据库状态转换为 JSON 或从 JSON 转换数据库状态。通过 ABI 描述了智能合约,开发人员和用户就可以通过 JSON 无缝地与你的合约进行交互。
ABI 文件可以通过使用 eosiocpp
工具从 .hpp
文件生成:
1 | $ eosiocpp -g ${contract}.abi ${contract}.hpp |
以下是框架合约 ABI 的示例:
1 | { |
你会注意到这个 ABI 定义了一个 transfer
类型的动作 transfer
。这告诉 EOSIO,当 ${account}->transfer
被看到时,交易的负载(payload)类型是 transfer
。动作类型 transfer
在 structs
数组中被定义,structs
数组对象中,name
属性的值为 transfer
。
1 | ... |
该 ABI 有好几个字段,包括 from
, to
和 quantity
。
这些字段有相应的类型 account_name
, 和 uint64
。account_name
是一个内置的类型使用 uint64
来表示 base32 字符串。
要详细了解可用的内置类型,请点击此处 。
1 | { |
在上面的 types
数组中,我们为已存在类型定义了一个别名列表。在这里,我们定义 name
为 account_name
的一个别名。
调试智能合约
为了能够调试你的智能合约,你需要设置本地 nodeos 节点。这个本地 nodeos 节点可以作为独立的私人测试网或作为公共测试网(或官方测试网)的扩展来运行。
当你首次创建智能合约时,建议先在私人测试网上测试并调试你的智能合约,因为你完全控制了整个区块链。这使你可以拥有无 限量你所需要的 eos,你可以随时重置区块链状态。当准备发布到生产环境时,可以通过将本地节点连接到公共测试网(或官方测试网)来在公共测试网(或官方测试网)上进行调试,以便你可以在本地节点中看到测试网的日志。
下面的教程,将在私人测试网上进行调试。
如果你尚未设置自己的本地节点,请按照 启动指南 进行操作。默认情况下,除非你按照 Testnet 指南 中所述修改 config.ini 文件以便与公共 testnet(或官方 testnet)节点连接,否则你的本地节点将仅运行在私有测试网络中。
方法
用于调试智能合约的主要方法是 穴居人调试(Caveman Debugging),我们利用打印功能来检查变量的值并检查合约的流程。在智能合约中打印可以通过打印 API (C 和 C++ ) 完成。C++ API 是 C API 的封装器,因此大多数情况下我们只会使用 C++ API。
打印
打印 C API 支持你可以打印的以下数据类型:
- prints - 一个带 null 终止符的字符数组(字符串)
- prints_l - 给定大小的任何字符数组(字符串)
- printi - 64 位无符号整数
- printi128 - 128 位无符号整数
- printd - 编码为 64 位无符号整数的浮点类型
- printn - 编码为 64 位无符号整数的 base32 字符串
- printhex - 给出二进制数据及其大小的十六进制
打印 C++ API 通过重写 print() 函数来封装一些上述 C API,因此用户不需要确定他需要使用哪种特定的打印功能。
打印 C++ API 支持:
- 一个带 null 终止符的字符数组(字符串)
- 整数(128 位无符号,64 位无符号,32 位无符号,有符号,无符号)
- 编码为 64 位无符号整数的 base32 字符串
- 具有 print() 方法的结构体
例子
我们来写一个新的合约作为调试的例子
- debug.hpp
1 |
|
- debug.cpp
1 |
|
- debug.hpp
1 | { |
让我们部署它并发送一条消息给它。假设你已经创建 debug
帐户并在将私钥导入你的钱包中。
1 | $ eosiocpp -o debug.wast debug.cpp |
当你检查你的本地 nodeos
节点日志时,你将在发送上述消息后看到以下行。
1 | Code is debug |
这里,你可以确认你的消息正在进入正确的控制流程并且金额已正确更新。你可能会看到上述消息至少 2 次,这很正常,
因为每个交易在验证、块生成和块应用阶段都会被执行。