EOS Asia
本教程原文作者为EOS Asia,亚洲最具技术实力和最国际化的EOS超级节点竞选者。EOS Asia 同时也是EOS Gems和Traffic Exchange Token这两个项目背后的开发者。
本文由 DappReview 获得 EOS Asia 授权进行翻译并发表。
本篇是EOS智能合约系列第二弹,该系列教程旨在帮助开发者从0到1快速上手如何在EOS生态下开发DApp。如果有任何希望深入讨论的主题,欢迎留言给我们!如果你还不知道怎么在EOS下部署智能合约,请先阅读系列第一弹《[技术教程]EOS智能合约开发:第一节——准备发车》
在大部分的应用场景中,开发者都需要通过智能合约与区块链上“永久保存”的数据进行交互。本次教程中,我们会一起通过 To-do List(待办事项)这个实例,来教会你如何实现与数据交互的标准操作(CRUD - Create, Retrieve, Update, Delete)
深入了解 Boost.MultiIndex
由于EOS的智能合约基于C++,我们需要利用Boost.MultiIndex Containers这个库。下面是该库的说明:
Boost多索引容器库提供了名为multi_index_container 的类模板,可以用于建造拥有一个或多个索引(indices) 的容器,不同的索引具有不同的排序和访问语义。这些索引都提供了类似于STL容器的接口,因此使用起来也非常相似。在一组元素之上维护多个索引的想法来自 于关系数据库,并且考虑到简单的set和map无法满足多索引表中的复杂数据结构的规范。
让我们把上述的一些概念拆分讲解一下,并与开发者所熟知的传统数据库概念做类比:
容器(Containers)
包含很多元素的类(table/表)
元素(Elements)
数据对象(rows in a table/表中的行)
接口(Interface)
容器读取元素的方法(query/查询)
在EOS智能合约中,可以使用来定义多索引容器。如果我们读一读使用了这个特性的一些合约例子,比如这个“骰子合约”:
https://github.com/EOSIO/eos/blob/master/contracts/dice/dice.cpp
你会发现很难真正搞明白到底是哪一部分是在处理区块链上的数据。不过别担心,我们会带你理解它,很快你就能自己实现一个有存储功能的智能合约。
我们将通过开发一个To-do List (待办事项表)小DApp来理解上述的内容。从功能上,要能勾掉已经完成的事情,添加新事项,以及删除不需要的事项。在这个例子中,我们将用作为容器名,作为元素结构。
从初始化第一个容器为开始,首先,我们向传入两个模板参数。第一个参数是我们的容器名,第二个参数是定义元素的数据结构。来给我们的todo模型创建一个小例子,如下:
struct todo { uint64_t id; uint64_t primary_key() const { return id; } EOSLIB_SERIALIZE(todo, (id))};typedef eosio::multi_index todo_table;todo_table todos;
简单有效!我们简单地定义了一个64位无符号整型的ID,并通过primary_key来访问它。把多索引定义成typedef,暂时还不需要把它实例化。目前为止,这个todo模型里面还没有什么东西,下面来添加一些参数:
struct todo { uint64_t id; std::string description; uint64_t completed; EOSLIB_SERIALIZE(todo, (id)(description)(completed))};typedef eosio::multi_index todo_table;todo_table todos;
现在我们更近了一步,加入了待办事项的描述参数-(比如 “完成小说撰写”)和状态参数-(用来记录一个事项在当前是否完成了)。
为了方便自动生成我们的ABI(Application Binary Interface),我们在容器定义前面加一行注释来帮助生成器:
那么在注释里的 是什么意思呢,它是我们的查询索引。默认情况下,我们需要一种在容器里查询元素的方法,而我们的前64位(64位类型下,基本上是first key)就可以用来干这件事。一般情况下都用 ;对于first key,也可以用 类型,因为在底层其实 类型也是一个 类型。参考如下:
https://github.com/EOSIO/eos/blob/2f2c8c7e3811caca178a7553192c8fe59a22576d/contracts/eosiolib/types.h#L22
此时我们应该有了一个功能简单的容器, 代码看起来是这样的:
// @abi table todos i64struct todo { uint64_t id; std::string description; uint64_t completed; uint64_t primary_key() const { return id; } EOSLIB_SERIALIZE(todo, (id)(description)(completed))};typedef eosio::multi_index todo_table;todo_table todos;
使用你的新容器
现在已经有了一个定义好的容器,我们可以使用它里面的元素。在智能合约里,将通过不同的函数与这些元素进行交互。
对于链上的永久性储存有四种基本函数:创建(Create),检索 (Retrieve),更新 (Update),删除 (Delete)。 在这个例子里,我们不需要考虑检索,因为检索是由前端读取合约来处理的而不用函数。对其他的三个,我们将分别创建函数。
创建(Create)- 创建事项
添加一个待办事项进入列表
可以用 完成
// @abi actionvoid create(account_name author, const uint32_t id, const std::string& description) { todos.emplace(author, [&](auto& new_todo) { new_todo.id = id; new_todo.description = description; new_todo.completed = 0; }); eosio::print("todo#", id, " created");}
一个重要细节是我们把author作为一个参数也传入了。在 方法中第一个参量是必须的。
更新(Update)- 完成事项
创建一个完成事项的函数可以通过更新参数completed 的状态来实现:
// @abi actionvoid complete(account_name author, const uint32_t id) { auto todo_lookup = todos.find(id); eosio_assert(todo_lookup != todos.end(), "Todo does not exist"); todos.modify(todo_lookup, author, [&](auto& modifiable_todo) { modifiable_todo.completed = 1; }); eosio::print("todo#", id, " marked as complete");}
删除(Delete)- 删除事项
这是一个内部调用的智能合约,不用太担心安全性和权限问题。 我们专心搞清楚删除函数如何最简化地实行就可以了:
// @abi actionvoid destroy(account_name author, const uint32_t id) { auto todo_lookup = todos.find(id); todos.erase(todo_lookup); eosio::print("todo#", id, " destroyed");}
部署,测试,与前端打通
在上一篇教程中,我们用一个简单的ping/pong实例讲述了如何将一个EOS的智能合约与网页前端连接起来。现在我们有了几个与区块链上的永久性数据交互的函数,下面可以为这个待办事项制作一个前端了。
部署
部署合约的过程比较直观,就下面这几步:
1. 建立合约ABI和WASM:
2. 建立账户/钱包:
cleos create account eosio todo.user EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4cleos set contract todo.user ../todo -p todo.user
3. 测试合约也很简单:
$ cleos push action todo create '["todo", 1, "hello world"]' -p todo.userexecuted transaction: bc5bfbd1e07f6e3361d894c26d4822edcdc2e42420bdd38b46a4fe55538affcf 248 bytes 107520 cycles# todo > todo created
4. 获取数据:
在前端测试
在这里我们就节省读者的时间,不在文章中深究React.js的代码了,不过我强烈推荐大家去看下这个例子的代码仓库,里面有前端部分的全部代码:
https://github.com/eosasia/eos-todo
如果你希望我们去更深入地探讨一些关于浏览器前端和EOS智能合约交互的内容,不论是用React、Vue、Angular还是原生Javascript,欢迎留言或者在电报群里告诉我们。
电报群:https://steemit.com/@eosasia
长按下图二维码,关注“EOS Asia”
领取专属 10元无门槛券
私享最新 技术干货