EOS的智能合约可以将数据永久存储在区块链数据库中。智能合约通过multi-index接口来与数据库打交道。通过eosio::multi_index智能合约能够写入、读取和修改eosio数据库的数据。eosio::multi_index在概念上和传统数据库的“表(table)”类似,数据“行(rows)”是独立的对象(通常是class对象或struct对象),数据“列(columns)”是对象的成员属性(class或struct的成员属性)。multi_index支持主键(primary key),但必须是唯一的无符号64位整型(uint64_t)。multi_index按主键排序时,使用升序。
一个完整的multi-index表定义如下:
//@abi table address i64
struct address {
uint64_t account;
string name;
uint64_t phone;
uint64_t liked;
//定义address表的主键
uint64_t primary_key() const { return account; }
//定义extractor,二级索引是phone
uint64_t get_phone() const {return phone; }
//序列化
EOSLIB_SERIALIZE(address, (account)(name)(phone)(liked))
}
//第一个参数是表名(即address),第二个参数是表对象的类型(address)
typedef multi_index address_table_type;
// 构造函数,有两个参数uint64_t类型参数:code, scope
// code: 拥有这张multi_index表的账户,该账户拥有对合约数据的读写权限
// scope: code层级内的范围标识符
address_table_type addresses(_self, _self);
const_iterator emplace( unit64_t payer, Lambda&& constructor )
payer:为新对象使用的存储付费的账户
constructor:lambda函数,可以让新创建的对象就地初始化
void erase( const object_type& obj )
obj:待更新对象的引用
void modify( const object_type &obj, uint64_t payer, Lambda&& updater )
obj:待更新对象的引用
payer:为新对象使用的存储付费的账户
updater:用于更新目标对象的lambda函数
const object_type& get( uint64_t primary ) const
primary:主键值
返回主键对应的对象引用
const object_type& find( uint64_t primary ) const
primary:主键值
返回主键对应的对象引用
这个例子用来维护一个todolist的数据库表。直接上代码如下:
#include
#include
class todolist : public eosio::contract
{
public:
using eosio::contract::contract;
// @abi table todos i64
struct todo
{
uint64_t id; // 待办事项主键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;
// @abi action
void create(account_name author, const uint32_t id, const std::string &description)
{
todo_table todos(_self, author);
todos.emplace(author, [&](auto &new_todo) {
new_todo.id = id;
new_todo.description = description;
new_todo.completed = 0;
});
eosio::print("todo#", id, " created");
}
// @abi action
void complete(account_name author, const uint32_t id)
{
todo_table todos(_self, author);
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");
}
// @abi action
void destroy(account_name author, const uint32_t id)
{
todo_table todos(_self, author);
auto todo_lookup = todos.find(id);
todos.erase(todo_lookup);
eosio::print("todo#", id, " destroyed");
}
};
EOSIO_ABI(todolist, (create)(complete)(destroy))
编译该智能合约。
~/eos_contract/multi_index$ sudo eosiocpp -o multi_index.wast multi_index.cpp
lzj@lzj-VirtualBox:~/eos_contract/multi_index$ sudo eosiocpp -g multi_index.abi multi_index.cpp
2018-12-04T07:52:20.770 thread-0 abi_generator.hpp:68 ricardian_contracts ] Warning, no ricardian clauses found for todolist
2018-12-04T07:52:20.770 thread-0 abi_generator.hpp:75 ricardian_contracts ] Warning, no ricardian contract found for create
2018-12-04T07:52:20.770 thread-0 abi_generator.hpp:75 ricardian_contracts ] Warning, no ricardian contract found for complete
2018-12-04T07:52:20.771 thread-0 abi_generator.hpp:75 ricardian_contracts ] Warning, no ricardian contract found for destroy
Generated multi_index.abi ...
接下来创建用来发布智能合约的账户accmulti:
~/eos_contract$ cleos create account eosio accmulti EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
executed transaction: 47238b1a8489fff221bd72a7f8cf21e8294fc75bdc2da46acbbd1197f7ec8565 288 bytes 1172 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"accmulti","owner":{"threshold":1,"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVP...
warning: transaction executed locally, but may not be confirmed by the network yet ]
发布智能合约:
~/eos_contract$ cleos set contract accmulti multi_index -p accmulti
Reading WAST/WASM from multi_index/multi_index.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: f1127afc36606c40fad5880ac590ff8ef594126c6a652c9a6c5e569e105f63ec 5232 bytes 921 us
# eosio <= eosio::setcode {"account":"accmulti","vmtype":0,"vmversion":0,"code":"0061736d01000000016e1260047f7e7f7f0060037f7e7...
# eosio <= eosio::setabi {"account":"accmulti","abi":"0e656f73696f3a3a6162692f312e30000404746f646f00030269640675696e7436340b6...
warning: transaction executed locally, but may not be confirmed by the network yet ]
创建新条目:
~/eos_contract$ cleos push action accmulti create '["accmulti","1","play basketball"]' -p accmulti
executed transaction: e18fd3d35a61d6b5889f697d7333cae40d2dc1fe5eacbc8dc4a4f2566fbf8add 208 bytes 535 us
# accmulti <= accmulti::create {"author":"accmulti","id":1,"description":"play basketball"}
>> todo#1 created
warning: transaction executed locally, but may not be confirmed by the network yet ]
查询数据表:
~/eos_contract$ cleos get table accmulti accmulti todos
{
"rows": [{
"id": 1,
"description": "play basketball",
"completed": 0
}
],
"more": false
}
这里completed是0,现在通过调用complete函数让其完成:
~/eos_contract$ cleos push action accmulti complete '["accmulti",1]' -p accmulti
executed transaction: 5101e086a02244465211f1a36192d677d9fa96230bc46212c34548c7a6921918 192 bytes 501 us
# accmulti <= accmulti::complete {"author":"accmulti","id":1}
>> todo#1 marked as complete
warning: transaction executed locally, but may not be confirmed by the network yet ]
再次查询,completed字段已经变为1:
~/eos_contract$ cleos get table accmulti accmulti todos
{
"rows": [{
"id": 1,
"description": "play basketball",
"completed": 1
}
],
"more": false
}
再添加俩条记录:
~/eos_contract$ cleos push action accmulti create '["accmulti",2,"go shopping"]' -p accmulti
executed transaction: 16ef3ed34ad3b492ef264de6ed8d8f0bfa6e88a0f31862d6b6762dc80a76c410 208 bytes 1096 us
# accmulti <= accmulti::create {"author":"accmulti","id":2,"description":"go shopping"}
>> todo#2 created
warning: transaction executed locally, but may not be confirmed by the network yet ]
lzj@lzj-VirtualBox:~/eos_contract$ cleos push action accmulti create '["accmulti",3,"go to swim"]' -p accmulti
executed transaction: 01002f611c889eacf837b9c88625e974553288ec211bc7aa6e7ee02ce4f64a4b 208 bytes 1885 us
# accmulti <= accmulti::create {"author":"accmulti","id":3,"description":"go to swim"}
>> todo#3 created
warning: transaction executed locally, but may not be confirmed by the network yet ]
查询:
~/eos_contract$ cleos get table accmulti accmulti todos
{
"rows": [{
"id": 1,
"description": "play basketball",
"completed": 1
},{
"id": 2,
"description": "go shopping",
"completed": 0
},{
"id": 3,
"description": "go to swim",
"completed": 0
}
],
"more": false
}
执行删除操作:
lzj@lzj-VirtualBox:~/eos_contract$ cleos push action accmulti destroy '["accmulti",2]' -p accmulti
executed transaction: 0fdf687a5f4363a90b33c8ab1c08a3ea9448fa6df23d73a23844575009bd5329 192 bytes 489 us
# accmulti <= accmulti::destroy {"author":"accmulti","id":2}
>> todo#2 destroyed
warning: transaction executed locally, but may not be confirmed by the network yet ]
查询,可以看到键位2的数据项已经被删除了:
~/eos_contract$ cleos get table accmulti accmulti todos
{
"rows": [{
"id": 1,
"description": "play basketball",
"completed": 1
},{
"id": 3,
"description": "go to swim",
"completed": 0
}
],
"more": false
}