EOS智慧合約訪問資料庫
1 multi-index介紹
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表
- 使用C++類(class)或結構體(struct)定義物件
- 在class或struct中,定義一個const成員函式:primary_key(),返回uint64_t型別的主鍵值
- 確定二級索引(最多支援16個),二級索引不侷限於uint64_t,它支援更多型別
- 為每個二級索引定義extractor,即一個函式,用於從Multi-Index表的函式中獲取鍵,這個函式會被indexed_by(後面會講)用到
一個完整的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)) }
例項化multi-index表
//第一個引數是表名(即address),第二個引數是表物件的型別(address)
typedef multi_index<N(address), address> address_table_type;
// 建構函式,有兩個引數uint64_t型別引數:code, scope
// code: 擁有這張multi_index表的賬戶,該賬戶擁有對合約資料的讀寫許可權
// scope: code層級內的範圍識別符號
address_table_type addresses(_self, _self);
使用multi-index表
- 新增物件到表中用emplace
const_iterator emplace( unit64_t payer, Lambda&& constructor )
payer:為新物件使用的儲存付費的賬戶
constructor:lambda函式,可以讓新建立的物件就地初始化
- 從表中刪除物件用erace
void erase( const object_type& obj )
obj:待更新物件的引用
- modify修改現有物件
void modify( const object_type &obj, uint64_t payer, Lambda&& updater )
obj:待更新物件的引用
payer:為新物件使用的儲存付費的賬戶
updater:用於更新目標物件的lambda函式
- get查詢表中物件
const object_type& get( uint64_t primary ) const
primary:主鍵值
返回主鍵對應的物件引用
- find查詢表中物件
const object_type& find( uint64_t primary ) const
primary:主鍵值
返回主鍵對應的物件引用
2 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))
編譯該智慧合約。
~/eos_contract/multi_index$ sudo eosiocpp -o multi_index.wast multi_index.cpp
[email protected]:~/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 ]
[email protected]:~/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
}
執行刪除操作:
[email protected]:~/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
}