1. 程式人生 > >EOS智慧合約訪問資料庫

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
}