1. 程式人生 > >eos原始碼賞析(十三):EOS智慧合約資料持久化儲存(上)

eos原始碼賞析(十三):EOS智慧合約資料持久化儲存(上)

前面的文章(eos原始碼賞析(十):EOS智慧合約入門之區塊上鍊)中提到了fork_db,區塊生產之後會將區塊的狀態資訊等儲存在fork_db中,但是當這個動作完成之後,fork_db中的內容就會變化,用來儲存下一個區塊的狀態資訊,並不能實現對歷史區塊資訊的儲存。對於區塊鏈來說,一定要有一個持久化資料儲存機制方能保證記錄eosio鏈上所有區塊資訊,並提供查詢介面,不然區塊生產的意義就已經不存在了。我們結合一個智慧合約來談談eosio中如何實現區塊資訊的持久化儲存,文章共分為上下兩篇

上篇主要包括以下內容:

  • Multi-Index的官方說明

  • 智慧合約中Multi-Index的使用

  • 智慧合約中資料增、刪、改、查

下篇主要包含以下內容

  • Multi-Index和ChainBase之間的互動

  • boost::multi-index的介紹及使用

一、Multi-Index的官方說明

關於Multi-Index的說明參見:https://developers.eos.io/eosio-cpp/docs/db-api

圖1 資料持久化儲存的必要性

上圖為官方給出的示例圖,說明了我們為什麼需要持久化的資料儲存

Actionsperform the work of EOSIO contracts. Actions operate within an environmentknown as the action context. As illustrated in the Action "Apply"Context Diagram, an action context provides several things necessary for theexecution of the action. One of those things is the action\'s working memory.This is where the action maintains its working state. Before processing anaction, EOSIO sets up a clean working memory for the action. Variables thatmight have been set when another action executed are not available within thenew action\'s context. The only way to pass state among actions is to persist itto and retrieve it from the EOSIO database.

簡單的說就是在每次action執行之前,eosio都會為其分配一塊記憶體,當另一個action執行的時候上一個action中的狀態資訊就不存在了,為了解決這個問題,只有持久化資料儲存,而後從資料庫中進行資料的查詢。

為此,eosio中引入了Multi-Index,Multi-Index改寫了boost中的Multi-Index,在eos中Multi-Index為eosio的資料庫提供了c++的介面,它可以儲存任意資料型別。類似於傳統的資料庫,每一張表都有一個表名,而在eosio中每一個Multi-Index就是一張表,與傳統資料庫不同的是他沒有行只有列,或者說一行N列,每一行都儲存一個物件,有過eos開發經驗的或許都知道,我們在Multi-Index中儲存的大多是結構體變數,每個結構體變數中會存在有多個成員變數,也就是說Multi-Index變相的幫我們儲存了很多資料。如我們上篇文章中所講到的狼人遊戲中結構體變數的儲存:

圖2 狼人遊戲中Multi-Index的使用

傳統資料庫大都只有唯一主鍵,而Multi-Index支援多主鍵索引,通過訪問對映底層儲存資料上不同型別的索引所直接獲取到的並不是儲存底層的資料實體,而是儲存實體的一個檢視。和其他區塊鏈技術的基礎架構對比,eosio永續性服務的一個關鍵區別是它的多索引迭代器。eosio多索引允許智慧合約開發者保留各種不同主鍵型別排序的物件集合,這些主鍵型別可以從物件內的資料派生。

二、智慧合約中Multi-Index的使用

注:本文中程式碼右滑可以檢視更多,或者找我要。

為了更方便的說明智慧合約中Multi-Inde的使用,我們來編寫一個智慧合約,其中使用Multi-Index來儲存《天龍八部》中各位英雄豪傑的相關資訊。該合約的標頭檔案如下:

#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

using namespace std;
using namespace eosio;

class tianlongbabu : public eosio::contract
{
private:
        account_name _this_contract;
        // @abi table heros
        struct heros
        {
                uint64_t heroid;          //天龍英雄的id
                string heroname;          //天龍英雄的名字
                string herodes;           //天龍英雄的個人描述
                string heroborn;          //天龍英雄的出生地
                uint32_t heroforceidx;    //天龍英雄的武力值
                uint32_t heroinsideidx;   //天龍英雄的內力值

                uint64_t primary_key() const{return heroid;}
                string get_heroname() const{return heroname;}
                string get_herodes() const{return herodes;}
                string get_heroborn() const{return heroborn;}
                uint32_t get_heroforceidx() const{return heroforceidx;}
                uint32_t get_heroinsideidx() const{return heroinsideidx;}

                EOSLIB_SERIALIZE(heros,(heroid)(heroname)(herodes)(heroborn)(heroforceidx)(heroinsideidx))
        };
        typedef eosio::multi_index<N(heros),heros> heros_table;
        heros_table ht;
public:
        tianlongbabu(account_name self):
        contract(self),
        _this_contract(self),
        ht(self,self)
        {
                //建構函式
        }
        // @abi action
//      void create(account_name heroname,string strheroname,string strherodes,string strheroborn,const uint32_t heroforceidx,const uint32_t heroinsideidx);
        // @abi action
        void create(account_name heroname,uint32_t heroforceidx,uint32_t heroinsideidx,string herodes,string heroborn);
        void deletebyid(uint64_t heroid);
        void deletebyforce(uint64_t forceindex);
        void modifytitle(account_name heroname,uint64_t heroid,string strherodes);
        void selectbyid(uint64_t heroid);
};

tlbb.hpp 程式碼(右滑可以檢視更多)

在類tianlongbabu中定義了結構體heros其中包含了天龍英雄的id、天龍英雄的名字、天龍英雄的出生地、天龍英雄的描述、天龍英雄的武力值、天龍英雄的內力值等相關變數。在使用EOSLIB_SERIALIZE對其序列化之後,使用Multi-Index並宣告一個變數ht,我們接下來的操作都在圍繞這個ht進行。此後,還聲明瞭幾個函式create用來建立天龍八部中的英雄,deletebyid用來根據英雄id來刪除表中的英雄資料,deletebyforce用來根據英雄的武力值遍歷刪除小於某個武力值的英雄資料,modifytitle根據英雄的id去修改該英雄的描述,selectbyid根據英雄的id去查詢英雄的相關資料。

三、智慧合約中資料增、刪、改、查

我們知道,在傳統資料庫中對資料的操作包含有增、刪、改、查,而在Multi-Index中也不例外,Multi-Index使用emplace、erase、modify、get等實現了對資料的各種操作,在看程式碼之前,我們先建立一些賬號,並部署相關合約:

cleos create account eosio tlbb.code {pub-key} {pub-key}
cleos create account eosio xiaofeng {pub-key} {pub-key}
cleos create account eosio xuzhu {pub-key} {pub-key}
cleos create account eosio duanyu {pub-key} {pub-key}
cleos create account eosio murongfu {pub-key} {pub-key}
[email protected]:~/eos/contracts/tlbb$ eosiocpp -o tlbb.wast tlbb.cpp 
[email protected]:~/eos/contracts/tlbb$ eosiocpp -g tlbb.abi tlbb.cpp 
[email protected]:~/eos/contracts/tlbb$ cleos set contract tlbb.code /home/ubuntu/eos/contracts/tlbb -p tlbb.code

賬號建立部署合約

下面我們結合程式碼一個個來看:

void tianlongbabu::create(account_name heroname,uint32_t heroforceidx,uint32_t heroinsideidx,string herodes,string heroborn)
{
        print(name{heroname},"出生在:",heroborn,",現在是",herodes,",他的武力值:",heroforceidx,",他的內力值:",heroinsideidx);
        uint64_t heroindex = 0;
        ht.emplace(_this_contract,[&](auto &p)
        {
                p.heroid = ht.available_primary_key();   //主鍵自增
                heroindex = p.heroid;
                p.herodes        = herodes;
                p.heroborn       = heroborn;
                p.heroforceidx   = heroforceidx;
                p.heroinsideidx  = heroinsideidx;

        });
        heros result = ht.get(heroindex);
        print("-該英雄的編號是:",result.heroid);
}

create建立英雄

在create中我們將push action中傳入的引數使用emplace寫入到表ht中,此處的available_primary_key()可以實現主鍵的自增,其餘的操作類似,類似於一個結構體變數作為一行多列的資料儲存。在寫入完成之後我們使用簡單的例子查詢到該英雄的id。然後我們執行以下action(記得nodeos執行的時候加入--contracts-console引數或者在config.ini中加入相關配置,這樣方便檢視合約執行的結果,在終端可以打印出來),這樣我們就完成了四個英雄的建立,蕭峰、虛竹、段譽、慕容復,並將這四個英雄的相關資料寫入到了ht中:

[email protected]:~/eos/contracts/tlbb$ cleos push action tlbb.code create \'["xiaofeng","1000","500","丐幫幫主","

遼國"]\' -p xiaofeng
executed transaction: ca536e0e13182f2ea6dc5e681c4aa3d81940d760c7435cba948bd9d56cc82460  128 bytes  1106 us
#     tlbb.code <= tlbb.code::create            

{"heroname":"xiaofeng","heroforceidx":1000,"heroinsideidx":500,"herodes":"丐幫幫主","heroborn":"...
>> xiaofeng出生在:遼國,現在是丐幫幫主,他的武力值:1000,他的內力值:500-該英雄的編號是:14

[email protected]:~/eos/contracts/tlbb$ cleos push action tlbb.code create \'["xuzhu","600","600","靈鷲宮宮主","中

原"]\' -p xuzhu
executed transaction: 2a93021425148e8c1ff5679566d758318ab7201ee4fb6bf06b5e536de7cc8f84  136 bytes  1121 us
#     tlbb.code <= tlbb.code::create            

{"heroname":"xuzhu","heroforceidx":600,"heroinsideidx":600,"herodes":"靈鷲宮宮主","heroborn":"?.
>> xuzhu出生在:中原,現在是靈鷲宮宮主,他的武力值:600,他的內力值:600-該英雄的編號是:15

[email protected]:~/eos/contracts/tlbb$ cleos push action tlbb.code create \'["duanyu","400","800","大理國皇帝","

大.理"] -p duanyu
executed transaction: 3b134c346cddb1cc0ef8eb9fefba41c3d82d1dda4019a189fa209e60157e65ac  136 bytes  1248 us
#     tlbb.code <= tlbb.code::create            

{"heroname":"xuzhu","heroforceidx":400,"heroinsideidx":800,"herodes":"大理國皇帝","heroborn":"?.
>> duanyu出生在:大理,現在是大理國皇帝,他的武力值:400,他的內力值:800-該英雄的編號是:16
[email protected]:~/eos/contracts/tlbb$ cleos push action tlbb.code create \'["murongfu","400","100","燕國皇 室後

代","姑蘇"]\' -p murongfu
executed transaction: 564d9acedd8e4dbbcb590ebcbdb2ef4c4151f35e8d611767065750d3f9852b5f  136 bytes  1303 us
#     tlbb.code <= tlbb.code::create            

{"heroname":"murongfu","heroforceidx":400,"heroinsideidx":100,"herodes":"燕國皇室後代","herobo...
>> murongfu出生在:姑蘇,現在是燕國皇室後代,他的武力值:400,他的內力值:100-該英雄的編號是:17

相信很多朋友都不太喜歡慕容復,因為其陰險狡詐、不自量力且貪生怕死,賣友求榮,最不能讓我忍受的是包不同的死,可憐一世家臣,卻落得如此下場,那麼我們就根據慕容復的id把慕容復從英雄庫中刪除吧,根據id刪除英雄資料的程式碼如下:

void tianlongbabu::deletebyid(uint64_t heroid)
{
        auto findhero = ht.find(heroid);
        eosio_assert(findhero != ht.end(), "您要刪除的英雄不存在");
        ht.erase(findhero);
        print("編號為:",heroid,"的英雄被刪除了");
}

而後我們執行以下action來刪除慕容復:

[email protected]:~/eos/contracts/tlbb$ cleos push action tlbb.code deletebyid \'["17"]\' -p tlbb.code
executed transaction: 970814ddd5c351878690ee53357fb82ad9c8b7da4aa395fd388918ba2ebf7fe3  104 bytes  821 us
#     tlbb.code <= tlbb.code::deletebyid        {"heroid":17}
>> 編號為:17的英雄被刪除了

虛竹的人生可以說是跌宕起伏的,從出生被偷走做了十幾年的小和尚到成為靈鷲宮宮主、逍遙派掌門,再到少林寺一役認出自己的爹媽,不就爹媽慘死,也是可憐,我們可以嘗試著修改下虛竹的相關資訊,讓他還是老老實實的當個小和尚,Multi-Index中修改資料內容的程式碼如下:

void tianlongbabu::modifytitle(account_name heroname,uint64_t heroid,string strherodes)
{
        require_auth(heroname);
        eosio_assert(!strherodes.empty(),"您輸入的字元不能為空");
        auto findhero = ht.find(heroid);
        eosio_assert(findhero != ht.end(),"您要修改的英雄不存在");
        ht.modify(findhero,heroname,[&](auto &p)
        {
                p.herodes = strherodes;
        });
        print(name{heroname},"現在是",strherodes);
}

在這裡我們使用require_auth獲取了使用者的active許可權來保證該英雄的資料不能被別人修改,然後執行以下action來修改虛竹的資料:

[email protected]:~/eos/contracts/tlbb$ cleos push action tlbb.code modifytitle \'["xuzhu","15","少林寺一個小和

尚"]\' -p [email protected]
executed transaction: d5b44a7f42ab1a499eb8b7bcdd69f26af591a9cf7cb277b48d6a36ceded55645  136 bytes  804 us
#     tlbb.code <= tlbb.code::modifytitle       {"heroname":"xuzhu","heroid":15,"strherodes":"少林寺一個小和尚"}
>> xuzhu現在是少林寺一個小和尚

說完了增刪改,其實上面已經有了資料查詢的過程,下面我們在通過一個簡單的例子,查詢下虛竹的資訊是不是修改成功了,程式碼如下:

void tianlongbabu::selectbyid(uint64_t heroid)
{
        auto gethero = ht.get(heroid);
        print("編號:",heroid,"出生在:",gethero.heroborn,"現在是:",gethero.herodes);
}

然後執行以下action:

[email protected]:~/eos/contracts/tlbb$ cleos push action tlbb.code selectbyid \'["15"]\' -p tlbb.code
executed transaction: 91715b3ae749425c70b0b93a2a2de6fdac6cef2f031de825480ab806d8096cee  104 bytes  694 us
#     tlbb.code <= tlbb.code::selectbyid        {"heroid":15}
>> 編號:15出生在:中原現在是:少林寺一個小和尚
[email protected]:~/eos/contracts/tlbb$ cleos push action tlbb.code selectbyid \'["14"]\' -p tlbb.code
executed transaction: 50bd5837e1301ae903d2475550a6a3a8502de8c7debcd34288dcb7c3f434a2a7  104 bytes  956 us
#     tlbb.code <= tlbb.code::selectbyid        {"heroid":14}
>> 編號:14出生在:遼國現在是:丐幫幫主

分別查詢了虛竹和蕭峰的相關英雄資訊。

本文主要介紹了Multi-Index在智慧合約中的應用,其中包含區塊鏈資料持久儲存的必要性、Multi-Index的一些簡單介紹、Multi-Index在只能合約中應用,智慧合約中資料的增、刪、改、查等操作。

我們知道,Multi-Index為多索引,那麼如何使用這個多索引呢,我們能否根據別的引數去查詢相關的英雄資訊,在emplace、erase、modify、get等背後又是和資料庫chainbase之間是如何互動的呢?我們下篇文章中繼續討論。

如果你覺得我的文章對你有一定的幫助,請點選文章末尾的喜歡該作者。

如果你對eos開發感興趣,歡迎關注本公眾號,一起學習eos開發。 

                                                                                          微信公眾號

         有任何疑問或者指教請新增本人個人微信,當然有對eos開發感興趣或者金庸粉的也可以新增一起交流,備註eos開發或金庸。

                                                                                                個人微訊號

相關推薦

eos原始碼賞析十三EOS智慧合約資料持久化儲存

前面的文章(eos原始碼賞析(十):EOS智慧合約入門之區塊上鍊)中提到了fork_db,區塊生產之後會將區塊的狀態資訊等儲存在fork_db中,但是當這個動作完成之後,fork_db中的內容就會變化,用來儲存下一個區塊的狀態資訊,並不能實現對歷史區塊資訊的儲存。對於區塊鏈來

EOS學習EOSIO智慧合約基礎

概述一個EOSIO智慧合約由一組活動(action)和型別定義組成。活動定義和實現了合約的行為方式,型別定義了所需的內容和結構。EOSIO的活動操作模式是基於訊息的通訊架構。我們可以通過cleos客戶端通過發訊息給nodeos或者使用EOSIO“send”方法(也就是“eos

eos原始碼賞析十三默克爾樹在EOS中的應用

前面文章中在分析push_transactioneos原始碼賞析(二十):EOS智慧合約之push_transaction的天龍八“步”以及區塊簽名eos原始碼賞析(二十一):EOS智慧合約之區塊簽名的天龍八“步”的時候都提到了默克爾樹,受限於篇幅未做具體分析。

eos原始碼賞析二十EOS智慧合約之push_transaction的天龍八“步”

很久沒談《天龍八部》了。 eosio整個系統中,transaction佔據著十分重要的位置。我們在區塊鏈上的任何有效操作,都代表著有transaction被執行了。在執行的過程中,push_transaction是不可以被忽略的。例如我們建立賬戶的時候,會通過p

eos原始碼賞析二十一EOS智慧合約之區塊簽名的天龍八“步”

在上篇文章中我們提到了,由使用者操作會產生各種事務,事務的鏈上執行是由push_transaction來完成的,我們簡單的劃分了下,具體可參考eos原始碼賞析(二十):EOS智慧合約之push_transaction的天龍八“步” 。我們知道,在區塊生產或者打包

eos原始碼賞析二十二EOS交易狀態何時才是不可逆的

作為本文的讀者,可能大部分都進行過eos代幣的轉賬操作。我們平時的交易過程中,能體驗到【立馬到賬】的感覺,這也是eos被越來越多的人認可的重要原因。然而在區塊鏈系統中一筆交易是否完成,有一個很重要的因素就是該筆交易在鏈上是否是可逆的。今天我們就來談談一筆交易在什

eos原始碼賞析EOS智慧合約入門之區塊打包和廣播機制

首先感謝群裡的大佬中山狼、linx、阿泥豆等各位給予的指導。 在上篇文章中我們寫到了eos中區塊產生的呼叫流程,其主要過程是從外掛中的producer_pligin去產生區塊,而實際產生區塊的過程卻是在chain中的controller.cpp中實現的。通過以前的文章我們知

eos原始碼賞析EOS智慧合約入門之共識機制初探

        從丐幫幫主及丐幫長老的選舉說起。        金庸小說中塑造了眾多丐幫幫主的形象,如汪劍通、蕭峰、洪七公、黃蓉、魯有腳、耶律齊、史火龍等。這些幫主在未當選幫主之前大都是丐幫中普普通通的一員,後來經過投票選舉---也就是吐唾沫的方式當上了幫主。這和現在eos的

eos原始碼賞析eos程式碼主分支架構

   在上篇文章中我們完成了在Ubuntu作業系統編譯eos的程式碼,我們會發現在原有的路徑下會多出一個名為build的資料夾,那麼這個資料夾裡面有什麼內容呢?這些東西是用來幹什麼的呢?古有庖丁解牛,現我們也將一一的分解eos程式碼主分支,看看這些程式碼都包含了什麼內容,恰如我們看書都會先翻一下序文和目錄一樣

eos原始碼賞析EOS智慧合約入門之區塊

或許我們還都記得美國隊長的勇敢、神武,為了捍衛自由和保衛人民而擁有的堅不可摧的盾牌,但我們還記得那個瘦弱到不堪一擊的史蒂夫.羅傑斯麼?血清的注射讓他變成了很多人心目中的英雄。那麼我們又可曾想過,美國隊長還會變成以前的那個因瘦弱的身軀而一直被人嘲笑的史蒂夫.羅傑斯麼,或許真的已

EOS 原始碼賞析EOS程式碼主分支架構

EOS 主目錄資料夾 : 第一部分:CMakeModules主要是cmake編譯所需要使用的一些配置資訊。 cotire.cmake 是加快編譯速度的cmake檔案 doxygen.cmake 可以將程式碼中的

百度前端技術學院第三十一到第三十三我是精明的小賣家

var regionGroup = document.getElementById("region-radio-wrapper"); var productGroup = document.getElementById("product-radio-wrapper");

Java進階學習第二十三Spring框架代理模式、AOP程式設計、jdbc支援

一、共性問題 1、伺服器啟動報錯,什麼原因? ① jar包缺少或者jar包衝突 ◆ 先檢查專案中是否缺少jar包引用 ◆ 伺服器:檢查jar包有沒有釋出到伺服器下;使用者庫jar包,需要手動釋出到tomcat(每次新建專案) ◆ 重新發布專案 ② 配置檔案錯誤(

設計模式):單例模式 JVM類載入機制 JDK原始碼學習筆記——Enum列舉使用及原理 Java併發):雙重檢驗鎖定DCL Java併發):Java記憶體模型 Java併發):Java記憶體模型 Java併發):雙重檢驗鎖定DCL JDK原始碼學習筆記——Enum列舉使用及原理

單例模式是一種常用的軟體設計模式,其定義是單例物件的類只能允許一個例項存在。 單例模式一般體現在類宣告中,單例的類負責建立自己的物件,同時確保只有單個物件被建立。這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。 適用場合: 需要頻繁的進行建立和銷燬的物件; 建立物

springCloud3微服務的註冊與發現Eureka

springcloud 微服務的註冊與發現 eureka 一、簡介服務消費者需要一個強大的服務發現機制,服務消費者使用這種機制獲取服務提供者的網絡信息。即使服務提供者的信息發生變化,服務消費者也無須修改配置。服務提供者、服務消費者、服務發現組件三者之間的關系大致如下: 1.各個微服務在啟動時,將自

JVM高級特性與實踐對象存活判定算法引用 與 回收

添加 引用計數器 程序計數器 正文 bmc 進入 block 結構 內存 關於垃圾回收器GC(Garbage Collection),多數人意味它是Java語言的伴生產物。事實上,GC的歷史遠比Java悠遠,於1960年誕生在MIT的Lisp是第一門真正使用內存動態分配和垃

python開發第三篇python基本數據類型列表,元組,字典

python開發 .com mage es2017 列表 基本 images 數據類型 切片 ##########列表:list########## 1.索引: 結果:eirc 2.切片 python開發(第三篇):python基本數據類型(列表,元組,字典)

C#可擴展編程之MEF學習筆記MEF簡介及簡單的Demo

com ring this exec hosting code .cn 引用 展開 在文章開始之前,首先簡單介紹一下什麽是MEF,MEF,全稱Managed Extensibility Framework(托管可擴展框架)。單從名字我們不難發現:MEF是專門致力於解決擴展性

跟開濤學SpringMVC4.1Controller接口控制器詳解1

詳解 shu fix gmv 控制器 input abstract pre pdf http://www.importnew.com/19397.html http://blog.csdn.net/u014607184/article/details/5207453

Android項目實戰三十九Android集成Unity3D項目圖文詳解

jar包沖突 scree pmap module 項目實戰 技術 詳細 應用端 原來 原文:Android項目實戰(三十九):Android集成Unity3D項目(圖文詳解)  需求:   Unity3D 一般用於做遊戲 而且是跨平臺的。原本設計是Android 應用端A