Appreciation of EOS source code: persistent storage of EOS smart contract data

Previous articles( Appreciation of EOS source code (10): blockchain of EOS smart contract introduction )Fork is mentioned in_ DB, after block production, the status information of the block will be stored in fork_db, but when this action is completed, fork_ The content in DB will change to store the status information of the next block, which can not save the historical block information. For a blockchain, it is necessary to have a persistent data storage mechanism to ensure that all block information on the eosio chain is recorded and a query interface is provided, otherwise the significance of block production will no longer exist. Let's talk about how to realize the persistent storage of block information in eosio combined with a smart contract. The article is divided into two parts

The first part mainly includes the following contents:

  • Official description of multi index
  • Use of multi index in smart contract
  • Data addition, deletion, modification and query in smart contract

The next part mainly includes the following contents

  • Interaction between multi index and ChainBase
  • Introduction and use of boost:: multi index

1, Official description of multi index

For the description of multi index, see: https://developers.eos.io/eosio-cpp/docs/db-api

Figure 1 necessity of data persistent storage

The above figure is an official example diagram, which illustrates why we need persistent data storage

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. In short, eosio allocates a piece of memory for each action before it is executed. When another action is executed, the state information in the previous action does not exist. In order to solve this problem, only persistent data storage is available, and then data query is performed from the database.

Therefore, multi index is introduced into eosio, which rewrites the multi index in boost. In eos, multi index provides a c + + interface for eosio database, which can store any data type. Similar to the traditional database, each table has a table name. In eosio, each multi index is a table. Different from the traditional database, it has no rows, only columns, or one row with N columns. Each row stores an object. Those with eos development experience may know that most of the variables stored in the multi index are structural variables, There will be multiple member variables in each structure variable, that is, multi index helps us store a lot of data in a disguised form. As we mentioned in the previous article, the storage of structure variables in werewolf Games:

Figure 2 use of multi index in werewolf game

Most traditional databases only have a unique primary key, while multi index supports multi primary key indexes. By accessing different types of indexes on the underlying storage data, what is directly obtained is not the underlying data entity, but a view of the storage entity. Compared with the infrastructure of other blockchain technologies, a key difference of eosio persistence service is its multi index iterator. Eosio multi index allows smart contract developers to retain a collection of objects sorted by different primary key types, which can be derived from the data in the object.

2, Use of multi index in smart contract

Note: in this article, you can see more by right sliding the code, or find me.

In order to more conveniently explain the use of multi index in smart contract, we will write a smart contract, in which multi index is used to store the relevant information of heroes in Tianlong Babu. The header file of the contract is as follows:

#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 of dragon hero
                string heroname;          //The name of the Dragon hero
                string herodes;           //Personal description of Tianlong hero
                string heroborn;          //Birthplace of dragon hero
                uint32_t heroforceidx;    //Force value of dragon hero
                uint32_t heroinsideidx;   //Internal power of dragon hero

                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)
        {
                //Constructor
        }
        // @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 code (right slide to see more)

The structure heros is defined in the class tianlongbabu, which includes the id of the Tianlong hero, the name of the Tianlong hero, the birthplace of the Tianlong hero, the description of the Tianlong hero, the force value of the Tianlong hero, the internal force value of the Tianlong hero and other related variables. When using eoslib_ After serialize serializes it, use multi index and declare a variable ht. Our next operations are centered on this ht. After that, several functions are also declared. Create is used to create heroes in the eight dragons. deletebyid is used to delete hero data in the table according to the hero id. deletebyforce is used to traverse and delete hero data less than a certain force value according to the hero's force value. modifytitle modifies the hero's description according to the hero's id, selectbyid searches the hero's related data according to the hero's id.

3, Data addition, deletion, modification and query in smart contract

We know that data operations in traditional databases include adding, deleting, modifying and querying, and multi index is no exception. Multi index uses empty, erase, modify and get to realize various operations on data. Before looking at the code, we first create some account numbers and deploy relevant contracts:

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}
ubuntu@VM-16-6-ubuntu:~/eos/contracts/tlbb$ eosiocpp -o tlbb.wast tlbb.cpp 
ubuntu@VM-16-6-ubuntu:~/eos/contracts/tlbb$ eosiocpp -g tlbb.abi tlbb.cpp 
ubuntu@VM-16-6-ubuntu:~/eos/contracts/tlbb$ cleos set contract tlbb.code /home/ubuntu/eos/contracts/tlbb -p tlbb.code

Account creation deployment contract

Let's look at the code one by one:

void tianlongbabu::create(account_name heroname,uint32_t heroforceidx,uint32_t heroinsideidx,string herodes,string heroborn)
{
        print(name{heroname},"Born in:",heroborn,",Now it is",herodes,",His force value:",heroforceidx,",His internal power value:",heroinsideidx);
        uint64_t heroindex = 0;
        ht.emplace(_this_contract,[&](auto &p)
        {
                p.heroid = ht.available_primary_key();   //Primary key auto increment
                heroindex = p.heroid;
                p.herodes        = herodes;
                p.heroborn       = heroborn;
                p.heroforceidx   = heroforceidx;
                p.heroinsideidx  = heroinsideidx;

        });
        heros result = ht.get(heroindex);
        print("-The hero's number is:",result.heroid);
}

Create create hero

In create, we use empty to write the parameters passed in the push action into the table ht, where available_primary_key() can realize the self increment of the primary key. Other operations are similar, similar to a structure variable as a row and multiple columns of data storage. After writing, we use a simple example to query the id of the hero. Then we execute the following actions (remember to add the -- contracts console parameter or add relevant configurations in config.ini when nodeos is running, so that it is convenient to view the contract execution results and print them on the terminal). In this way, we have completed the creation of four heroes, Xiao Feng, Xu Zhu, Duan Yu and Mu Rongfu, The relevant data of these four heroes are written into ht:

ubuntu@VM-16-6-ubuntu:~/eos/contracts/tlbb$ cleos push action tlbb.code create \'["xiaofeng","1000","500","Beggars' sect leader","

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

{"heroname":"xiaofeng","heroforceidx":1000,"heroinsideidx":500,"herodes":"Beggars' sect leader","heroborn":"...
>> xiaofeng Born in:Liao ,Now he is the leader of the beggars' sect,His force value:1000,His internal power value:500-The hero's number is:14

ubuntu@VM-16-6-ubuntu:~/eos/contracts/tlbb$ cleos push action tlbb.code create \'["xuzhu","600","600","Lingjiu palace leader","in

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

{"heroname":"xuzhu","heroforceidx":600,"heroinsideidx":600,"herodes":"Lingjiu palace leader","heroborn":"?.
>> xuzhu Born in:central plains,Now he is the leader of the spirit vulture palace,His force value:600,His internal power value:600-The hero's number is:15

ubuntu@VM-16-6-ubuntu:~/eos/contracts/tlbb$ cleos push action tlbb.code create \'["duanyu","400","800","Emperor of Dali","

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

{"heroname":"xuzhu","heroforceidx":400,"heroinsideidx":800,"herodes":"Emperor of Dali","heroborn":"?.
>> duanyu Born in:Dali,Now he is the emperor of Dali,His force value:400,His internal power value:800-The hero's number is:16
ubuntu@VM-16-6-ubuntu:~/eos/contracts/tlbb$ cleos push action tlbb.code create \'["murongfu","400","100","Royal Queen of Yan state

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

{"heroname":"murongfu","heroforceidx":400,"heroinsideidx":100,"herodes":"Descendants of Yan royal family","herobo...
>> murongfu Born in:Gusu,Now he is the descendant of the royal family of Yan state,His force value:400,His internal power value:100-The hero's number is:17

I believe many friends don't like Murong Fu very much because he is insidious, cunning, overestimating himself, afraid of life and death, and selling friends for prosperity. What I can't stand most is Bao Di's death. Poor family officials ended up like this. Then we will delete Murong Fu from the hero database according to Murong Fu's id. the code for deleting hero data according to id is as follows:

void tianlongbabu::deletebyid(uint64_t heroid)
{
        auto findhero = ht.find(heroid);
        eosio_assert(findhero != ht.end(), "The hero you want to delete does not exist");
        ht.erase(findhero);
        print("No:",heroid,"Your hero has been deleted");
}

Then we execute the following action s to delete Murong Fu:

ubuntu@VM-16-6-ubuntu:~/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}
>> No:17 Your hero has been deleted

Xuzhu's life can be said to be ups and downs. From being stolen from birth and being a little monk for more than ten years to becoming the leader of lingjiu palace and the leader of Xiaoyao sect, and then recognizing his parents in the battle of Shaolin Temple, his parents died miserably, but also pitiful. We can try to modify the relevant information of xuzhu so that he can still be a little monk, The code for modifying data content in multi index is as follows:

void tianlongbabu::modifytitle(account_name heroname,uint64_t heroid,string strherodes)
{
        require_auth(heroname);
        eosio_assert(!strherodes.empty(),"The character you entered cannot be empty");
        auto findhero = ht.find(heroid);
        eosio_assert(findhero != ht.end(),"The hero you want to modify does not exist");
        ht.modify(findhero,heroname,[&](auto &p)
        {
                p.herodes = strherodes;
        });
        print(name{heroname},"Now it is",strherodes);
}

Here we use require_auth obtains the user's active permission to ensure that the hero's data cannot be modified by others, and then executes the following action s to modify the virtual bamboo's data:

ubuntu@VM-16-6-ubuntu:~/eos/contracts/tlbb$ cleos push action tlbb.code modifytitle \'["xuzhu","15","Shaolin Temple a small and

still"]\' -p xuzhu@active
executed transaction: d5b44a7f42ab1a499eb8b7bcdd69f26af591a9cf7cb277b48d6a36ceded55645  136 bytes  804 us
#     TLBB. Code < = TLBB. Code:: modifytitle {"heroname": "xuzhu", "heroid": 15, "STRODES": "a little monk in Shaolin Temple"}
>> xuzhu Now he is a little monk in Shaolin Temple

After adding, deleting and modifying, in fact, there is a data query process above. Let's query whether the information of Phyllostachys pubescens has been successfully modified through a simple example. The code is as follows:

void tianlongbabu::selectbyid(uint64_t heroid)
{
        auto gethero = ht.get(heroid);
        print("number:",heroid,"Born in:",gethero.heroborn,"Now it is:",gethero.herodes);
}

Then execute the following action s:

ubuntu@VM-16-6-ubuntu:~/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}
>> number:15 Born in:Central Plains is now:A little monk in Shaolin Temple
ubuntu@VM-16-6-ubuntu:~/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}
>> number:14 Born in:Liao is now:Beggars' sect leader

The relevant hero information of xuzhu and Xiao Feng was queried respectively.

This paper mainly introduces the application of multi index in smart contract, including the necessity of persistent storage of blockchain data, some brief introduction of multi index, the application of multi index in smart contract, and the operation of data addition, deletion, modification and query in smart contract.

We know that multi index is a multi index, so how to use this multi index? Can we query relevant hero information according to other parameters, and how do we interact with the database chainbase behind empty, erase, modify, get, etc? We will continue our discussion in the next article.

Posted on Tue, 23 Nov 2021 00:59:32 -0500 by embsupafly