Home | 简体中文 | 繁体中文 | 杂文 | 知乎专栏 | Github | OSChina 博客 | 云社区 | 云栖社区 | Facebook | Linkedin | 视频教程 | 打赏(Donations) | About
知乎专栏多维度架构微信号 netkiller-ebook | QQ群:128659835 请注明“读者”

6.7. 智能合约数据库操作 CURD

为了方便调试合约

找到config.ini中的配置项contracts-console = false 改为 true

		
[root@netkiller ~]# vim ~/.local/share/eosio/nodeos/config/config.ini

# print contract's output to console (eosio::chain_plugin)
contracts-console = true
		
	

这样 eosio::print() 输出的内容才会显示在控制台上。

6.7.1. 创建一个新项目

		
eosiocpp -n project		
		
		

6.7.2. 创建结构体

例如我们需要这样一个数据结构

		
{
	id,
	description,
	completed
}		
		
		

结构体定义如下

		
struct todo {
	uint64_t id;
	std::string description;
	uint64_t completed;

	uint64_t primary_key() const { return id; }
	EOSLIB_SERIALIZE(todo, (id)(description)(completed))
};		
		
		

primary_key() 相当与数据中的主键。

定义一个表

		
typedef eosio::multi_index<N(todos), todo> todo_table;
todo_table todos;	
		
		

6.7.3. 插入数据操作

		
void create(account_name author, const uint32_t id, const std::string& description) {
	todos.emplace(author, [&](auto& new_todo) {
		new_todo.id  = id;
		new_todo.description = description;
		new_todo.completed = 0;
	});		
		
		

6.7.4. 修改数据操作

		
void complete(account_name author, const uint32_t id) {
      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");
    }		
		
		

6.7.5. 删除数据操作

		
void destroy(account_name author, const uint32_t id) {
	auto todo_lookup = todos.find(id);
	todos.erase(todo_lookup);

	eosio::print("todo#", id, " destroyed");
}		
		
		

6.7.6. 完整的合约例子

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

namespace eosio {
using std::string;
  class netkiller : public contract {
     public:
           netkiller( account_name self ):contract(self){}

           void create(account_name author, string title, string content);
           void change(account_name author, uint64_t post_id, string title, string content);
           void remove(account_name author, uint64_t post_id);
           void find(uint64_t post_id, account_name author);

     private:

           struct da {
                 uint64_t     post_id;
                 account_name poster;
                 string       title;
                 string       content;

                 uint64_t primary_key()const { return post_id; }
                 account_name get_poster() const { return poster; }

                 EOSLIB_SERIALIZE(da, (post_id)(poster)(title)(content))
           };
           typedef eosio::multi_index<N(data), da, indexed_by<N(byposter), const_mem_fun<da, account_name, &da::get_poster>> > article;
  };
}
		
		
		
		
#include "netkiller.hpp"

namespace eosio {

    void netkiller::create(account_name author, string title, string content)
    {
        require_auth( author );
        article datable( _self, _self);
        datable.emplace(author, [&]( da & d){
            d.title = title;
            d.content = content;
            d.post_id = datable.available_primary_key();
            d.poster = author;
        });
    }

    void netkiller::change(account_name author, uint64_t post_id, string title, string content)
    {
        require_auth(author);
        article datable( _self, author);
        auto post = datable.find(post_id);
        eosio_assert(post->poster == author, "netkiller");
        datable.modify(post, author, [&](auto& p){
            if (title != "")
                p.title = title;
            if (content != "")
                p.content = content;
        });
    }

    void netkiller::remove(account_name author, uint64_t post_id)
    {
        require_auth(author);
        article datable( _self, author);
        auto post = datable.find(post_id);
        eosio::print(post->title.c_str());

        eosio_assert(post->poster == author, "The author is invlide");
        datable.erase(post);
    }

    void netkiller::find(uint64_t post_id, account_name author){
        article datable(_self, _self);
        auto post_da = datable.find( post_id);
        eosio::print("Post_id: ", post_da->post_id, "  Post_Tile: ", post_da->title.c_str(), " Content: ", post_da->content.c_str());

        auto poster_index = datable.template get_index<N(byposter)>();
        auto pos = poster_index.find( author );

        for (; pos != poster_index.end(); pos++)
        {
            eosio::print("content:", pos->content.c_str(), " post_id:", pos->post_id, " title:", pos->title.c_str());
        }
    }

}
EOSIO_ABI(eosio::netkiller, (create)(change)(remove)(find))

		
		
		
		

6.7.6.1. 编译

			
eosiocpp -o cms.wast cms.cpp	
eosiocpp -g cms.abi cms.cpp
			
			

6.7.6.2. 启动EOS私链开发环境

		
nodeos -e -p eosio --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin --plugin eosio::wallet_api_plugin		
		
			

6.7.6.3. 创建合约账号

这里我们创建一个账号,用这个账号部署合约,该账号是合约所有者。

创建秘钥对

				
[root@netkiller ~]# cleos wallet unlock		
				
[root@netkiller ~]# cleos create key
Private key: 5HxCWNbTEADKbvdRBgeENXhxReHMQbVuPL5mumDqGCzmkPo5yy3
Public key: EOS7WEcxxHcmM7w7DHB56N6qQ2toMrdudYjeDTZb6LtL9g77MXzR4
				
			

导入私钥

				
[root@netkiller ~]# cleos wallet import 5HxCWNbTEADKbvdRBgeENXhxReHMQbVuPL5mumDqGCzmkPo5yy3
imported private key for: EOS7WEcxxHcmM7w7DHB56N6qQ2toMrdudYjeDTZb6LtL9g77MXzR4		
	
[root@netkiller ~]# cleos wallet keys | grep EOS7WEcxxHcmM7w7DHB56N6qQ2toMrdudYjeDTZb6LtL9g77MXzR4
  "EOS7WEcxxHcmM7w7DHB56N6qQ2toMrdudYjeDTZb6LtL9g77MXzR4",	
				
			

创建账号 neo

				
[root@netkiller ~]# cleos create account eosio contract.cms EOS7WEcxxHcmM7w7DHB56N6qQ2toMrdudYjeDTZb6LtL9g77MXzR4 EOS7WEcxxHcmM7w7DHB56N6qQ2toMrdudYjeDTZb6LtL9g77MXzR4
executed transaction: f04ba09f633dffbf97321c6d2e021192082383908fa690dc40032cd98a1bfd89  200 bytes  390 us
#         eosio <= eosio::newaccount            "0000000000ea305590af0119999b274501000000010003588ecdc868696f500c7782dbf0da3b298830e67ea9b810469819d...
warning: transaction executed locally, but may not be confirmed by the network yet
				
			

contract.art 就是我们合约账号,我们使用 contract 前缀来区分他是合约账号。

6.7.6.4. 部署合约

		
[root@netkiller eos]# cleos wallet unlock
password: Unlocked: default

[root@netkiller eos]# cleos set contract contract.cms cms
Reading WAST/WASM from cms/cms.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 8a72e29389e170807daaf41e9c9e70ac4eff2f2f129ca22ef55ca9443768dedf  7176 bytes  1311 us
#         eosio <= eosio::setcode               "80250219999b27450000c3770061736d0100000001b2011b60037f7e7e0060027f7e0060057f7e7e7f7f0060047f7e7f7f0...
#         eosio <= eosio::setabi                "80250219999b27459d020e656f73696f3a3a6162692f312e30000506637265617465000306617574686f72046e616d65057...
warning: transaction executed locally, but may not be confirmed by the network yet
		
			

6.7.6.5. 创建

		
cleos push action contract.cms create '{"author":"contract.cms","title":"hello","content":"helloworld!!!"}' -p contract.cms

[root@netkiller eos]# cleos push action contract.art create '{"author":"contract.art","title":"hello","content":"helloworld!!!"}' -p contract.art
executed transaction: b6cab608fb4e7fa17a7f893848f3516e1bfd231769ad7d7226b0a099f309a771  120 bytes  899 us
#  contract.art <= contract.art::create         {"author":"contract.cms","title":"hello","content":"helloworld!!!"}
warning: transaction executed locally, but may not be confirmed by the network yet
		
			
			
[root@netkiller eos]# cleos push action contract.cms create '{"author":"neo","title":"hello","content":"helloworld!!!"}' -p neo
executed transaction: 90cb81b11386514b450e1a609f0e1e2633f6a4e40d453c127811e5cd33b46a5a  120 bytes  755 us
#  contract.art <= contract.art::create         {"author":"neo","title":"hello","content":"helloworld!!!"}
warning: transaction executed locally, but may not be confirmed by the network yet		
			
			

下面我们来查询一下刚刚插入的数据:

		

		
			

6.7.6.6. 查找

find

			
[root@netkiller eos]# cleos push action contract.cms find '{"id":0}' -p contract.cms
executed transaction: b3cba4d001fcb49a88926be208fa7bee59d557b0ed8a2bd12e65bdd3ff69c61e  104 bytes  486 us
#  contract.cms <= contract.cms::find           {"id":0}
>> id: 0 Tile: hello Content: helloworld!!!			
			
			

query

			
[root@netkiller eos]# cleos push action contract.cms query '{"author":"contract.cms", "id":0}' -p contract.cms 
executed transaction: c81a0d21634f4942f7d65dd49efcf5cf7cd739a049968b5f9b0eaad7de4c688c  112 bytes  583 us
#  contract.cms <= contract.cms::query          {"author":"contract.cms","id":0}
>> Post_id: 0  Post_Tile: hello Content: helloworld!!!content:helloworld!!! id:0 title:hello
			
			

6.7.6.7. 修改

修改表中的数据

			
[root@netkiller eos]# cleos push action contract.cms change '{"author":"contract.cms","id":0,"title":"word","content":"china"}' -p contract.cms
executed transaction: 073205e4e3e30699a81394ee622aa84d568958919801b364d809ff34f4ca8412  120 bytes  553 us
#  contract.cms <= contract.cms::change         {"author":"contract.cms","id":0,"title":"word","content":"china"}
			
			

检查数据修改情况

			
[root@netkiller eos]# cleos push action contract.cms find '{"id":0}' -p contract.cms
executed transaction: 300fe2c93cf2cfebcdd0524e0629a96b3011b0592be2119d001f38807c1c378b  104 bytes  498 us
#  contract.cms <= contract.cms::find           {"id":0}
>> id: 0 Tile: word Content: china			
			
			

6.7.6.8. 删除

			
[root@netkiller eos]# cleos push action contract.cms remove '{"author":"contract.cms","id":0}' -p contract.cms
executed transaction: eeee8ff799c58d5e3a246ccec8d80c47599ad947d4581611d9a668abee53c0b5  112 bytes  770 us
#  contract.cms <= contract.cms::remove         {"author":"contract.cms","id":0}
>> word		
			
			

检查被删除的数据,提示 Error 3070002: Runtime Error Processing WASM 表示找不到该记录。

			
[root@netkiller eos]# cleos push action contract.cms find '{"id":0}' -p contract.cms
Error 3070002: Runtime Error Processing WASM			
			
			

6.7.7. 序列主键

		
#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        date;
        uint32_t        odometer;

        auto primary_key() const { return pkey; }

        account_name get_customer() const { return customer; }

        EOSLIB_SERIALIZE(service_rec, (pkey)(customer)(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) {
        
        service_table_type service_table(current_receiver(), 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.date = 2000;
            s_rec.odometer = 100;
        });
        
        print("Hello, ", name{customer});
        service_rec result = service_table.get(pkeyf);
        print("_", result.pkey);
        print("_", result.customer);
        print("_", result.date);
        print("_", result.odometer);
    }

};

EOSIO_ABI(vehicle, (exec))