原文連接:醒者呆的博客園,www.cnblogs.com/Evsward/p/m…html
智能合約操做鏈數據庫是很常見的應用場景。EOS提供了專門的工具來作這件事(至關於Ethereum的leveldb),專業術語叫作持久化API,本文將完整嚴密地介紹這個工具以及對它的使用測試。git
關鍵字:EOS,智能合約,鏈數據庫,eosio::multi_index,constructor,emplace,erase,find。github
首先來看EOS中智能合約涉及到持久化的場景需求。一個action在執行時會有上下文變量出現,包括事務機制的處理,這些內容會應用鏈上分配的內存資源,而若是沒有持久化技術,執行超過做用域時就會丟失掉這些上下文數據。所以要使用持久化技術將關鍵內容記錄在鏈數據庫中,任什麼時候候使用都不受影響。持久化技術應該包括:數據庫
這是模仿boost::multi_index開發的一套庫。它使用C++編寫,提供了合約與數據庫的交互持久化接口。api
Multi-Index Iterators:不一樣於其餘key-value數據庫,multi_index提供了不一樣類型的key對應的值也是可迭代的複雜集合類型。
複製代碼
建立表bash
使用Multi-Index表dom
通常來說,對數據庫的操做無外乎增刪改查,函數
下面咱們經過一個智能合約操做底層數據庫的實例,來演示對持久化api,multi_index的使用。過程當中,也會展現相應api的定義。工具
首先,建立一個service表,用來建立服務記錄報告,它包含的字段有:學習
#include <eosiolib/eosio.hpp>
using namespace eosio;
class vehicle : public eosio::contract {
public:
/// @abi table
struct service_rec {
uint64_t pkey;
account_name customer;
uint32_t service_date;
uint32_t odometer;
auto primary_key() const { return pkey; }
account_name get_customer() const { return customer; }
EOSLIB_SERIALIZE(service_rec, (pkey)(customer)(service_date)(odometer))
};
typedef multi_index<N(service), service_rec> service_table_type;
using contract::contract;
/// @abi action
void exec(account_name owner, account_name customer) {
print("Hello, ", name{customer});
service_table_type service_table(current_receiver(), owner);// 構造器,第一個參數是code表明表的擁有者,目前是current_receiver(),也可使用contract基類的構造器初始化時的帳戶名_self,第二個參數是scope,在代碼層次範圍的標識符,這裏就使用傳入的owner帳戶。
uint64_t pkeyf;// 主鍵
service_table.emplace(owner, [&](auto &s_rec) {
s_rec.pkey = service_table.available_primary_key();// 主鍵自增
pkeyf = s_rec.pkey;
print(pkeyf);// 打印主鍵內容
s_rec.customer = customer;
s_rec.service_date = 2000;
s_rec.odometer = 1000;
});
service_rec result = service_table.get(pkeyf);
print("_", result.pkey);
print("_", result.customer);
print("_", result.service_date);
print("_", result.odometer);
}
};
EOSIO_ABI(vehicle, (exec))
複製代碼
使用eosiocpp工具生成abi文件和wast文件。 eosiocpp -g vehicle.abi vehicle.cpp | eosiocpp -o vehicle.wasm vehicle.cpp
而後在終端中部署該合約, cleos set contract one work/CLionProjects/github.com/eos/contracts/vehicle
調用合約的exec方法,並輸出結果:
liuwenbin@liuwenbin-H81M-DS2:~$ cleos push action one exec '["one","two"]' -p one
executed transaction: 3a45eaeb06732ad0c53ba7b157003e1c503f74ed447029d82cecbe12926cc9a9 112 bytes 365 us
# one <= one::exec {"owner":"one","customer":"two"}
>> Hello, two13_13_14927180964919508992_2000_1000
warning: transaction executed locally, but may not be confirmed by the network yet
複製代碼
經過輸出結果,能夠知道主鍵爲13,customer帳戶名被轉爲無符號32位整型數據14927180964919508992,服務時間爲2000,里程錶爲1000。咱們已經成功將數據存入了multi_index並取了出來。
刪除的話能夠經過service_table.erase(result);來刪除掉對應記錄。
find涉及二級索引,迭代器等操做,end判斷等multi_index的api操做沒有給出具體實例,將來在其餘合約使用時會直接說明。
複製代碼
爲了更好的熟悉multi_index的機制,咱們再演練一個簡單的例子:維護一個todolist的數據庫表。直接上代碼以下:
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
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<N(todos), todo> 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))
複製代碼
這裏加入了對數據的增刪改查功能。而後咱們使用 eosiocpp -o todolist.wast todolist.cpp && eosiocpp -g todolist.abi todolist.cpp
建立對應的abi和wast文件。接下來建立用戶todo.user,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos create account eosio todo.user EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
executed transaction: 7d7191e2935c6fd0024f571e228ee11d51f4f44a64ed2ce977326fb27679f0c6 200 bytes 174 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"todo.user","owner":{"threshold":1,"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnV...
warning: transaction executed locally, but may not be confirmed by the network yet
複製代碼
部署合約,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos set contract todo.user work/VSCode-Projects/eos/contracts/todolist -p todo.user
Reading WAST/WASM from work/VSCode-Projects/eos/contracts/todolist/todolist.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 03befa58d6a54970db708beaa0520179277b01addf1ec647a76a9b3f6459ff57 5128 bytes 2203 us
# eosio <= eosio::setcode {"account":"todo.user","vmtype":0,"vmversion":0,"code":"0061736d01000000016e1260047f7e7f7f0060037f7e...
# eosio <= eosio::setabi {"account":"todo.user","abi":{"types":[],"structs":[{"name":"todo","base":"","fields":[{"name":"id",...
warning: transaction executed locally, but may not be confirmed by the network yet
複製代碼
建立新條目,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos push action todo.user create '["todo.user",1,"hello, world"]' -p todo.user
executed transaction: 54d9825971370a242fa51fa7cc587a6478dd7852039c263d91058c6b1163a4bc 120 bytes 336 us
# todo.user <= todo.user::create {"author":"todo.user","id":1,"description":"hello, world"}
>> todo#1 created
複製代碼
查詢,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos get table todo.user todo.user todos
{
"rows": [{
"id": 1,
"description": "hello, world",
"completed": 0
}
],
"more": false
}
複製代碼
完成事項,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos push action todo.user complete '["todo.user",1]' -p todo.user
executed transaction: 11423637cb321969961b3ce0305c93ba7f95a83b6d82c1a4e31a08c569f2dcaa 104 bytes 312 us
# todo.user <= todo.user::complete {"author":"todo.user","id":1}
>> todo#1 marked as complete
warning: transaction executed locally, but may not be confirmed by the network yet
複製代碼
再次查詢,completed字段已置爲1,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos get table todo.user todo.user todos
{
"rows": [{
"id": 1,
"description": "hello, world",
"completed": 1
}
],
"more": false
}
複製代碼
建立多條記錄,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos push action todo.user create '["todo.user",2,"eos funk"]' -p todo.user
executed transaction: 35f417a4d7438e6ea9ffd837e3c261fca0cb3926b534dc6603fdb38f94d5cd77 120 bytes 329 us
# todo.user <= todo.user::create {"author":"todo.user","id":2,"description":"eos funk"}
>> todo#2 created
warning: transaction executed locally, but may not be confirmed by the network yet
liuwenbin@liuwenbin-H81M-DS2:~$ cleos push action todo.user create '["todo.user",10,"go to bank"]' -p todo.user
executed transaction: cd4cd2c85500e93a79eb5c3b3852a0a96a9fb220696c63f2683b473d58a6ca34 120 bytes 311 us
# todo.user <= todo.user::create {"author":"todo.user","id":10,"description":"go to bank"}
>> todo#10 created
warning: transaction executed locally, but may not be confirmed by the network yet
複製代碼
查詢,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos get table todo.user todo.user todos
{
"rows": [{
"id": 1,
"description": "hello, world",
"completed": 1
},{
"id": 2,
"description": "eos funk",
"completed": 0
},{
"id": 10,
"description": "go to bank",
"completed": 0
}
],
"more": false
}
複製代碼
刪除一條數據,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos push action todo.user destroy '["todo.user",2]' -p todo.user
executed transaction: c87e37f4521e0ce2a865acbeb63c0f3a0681b9b5a0a6c5db8cd53584d6d13dca 104 bytes 2198 us
# todo.user <= todo.user::destroy {"author":"todo.user","id":2}
>> todo#2 destroyed
warning: transaction executed locally, but may not be confirmed by the network yet
複製代碼
再次查詢,
liuwenbin@liuwenbin-H81M-DS2:~$ cleos get table todo.user todo.user todos
{
"rows": [{
"id": 1,
"description": "hello, world",
"completed": 1
},{
"id": 10,
"description": "go to bank",
"completed": 0
}
],
"more": false
}
複製代碼
這是一個完整的,經過multi_index進行curd的一個例子(第一個例子是官方文檔給出的,其中內容有bug)。
經過本篇文章的學習,咱們掌握瞭如何在EOS中使用智能合約調用multi_index實現數據的持久化。
圓方圓學院聚集大批區塊鏈名師,打造精品的區塊鏈技術課程。 在各大平臺都長期有優質免費公開課,歡迎報名收看。
公開課地址:ke.qq.com/course/3451…