EOS系列二:EOS智能合约开发学习心得

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_33656634/article/details/83860279

以几个实例演绎开发过程。

=================================实例1====================================

一、建立合约目录,编写合约c++代码

cd /home/game/contracts
mkdir hello
cd hello
touch hello.cpp

编写c++

#include <eosiolib/eosio.hpp> //包含 EOSIO C and C++ API
#include <eosiolib/print.hpp>

using namespace eosio; //使用eosio命名空间

class hello : public contract { //继承自eosio::contract
  public:
      using contract::contract;//public引用基类成员contract(可能在基类中contract是保护或者私有的成员变量或函数)

      [[eosio::action]] //eosio.cdt中EOSIO-cpp编译自动生成ABI文件所用的属性标识
      void hi( name user ) { //参数name类型
         print( "Hello, ", name{user}); //eosio::print(),参数中实例化一个name变量??
      }
};
EOSIO_DISPATCH( hello, (hi)) //合约部署的宏定义,如有需要可以自定义其具体内容

二、编译.wasm文件并生成abi文件

eosio-cpp -o hello.wasm hello.cpp --abigen

三、合约部署

1、为合约创建部署帐号,合约的部署是部署到某个帐号。

cleos wallet keys

获取当前钱包公私钥。返回内容中公钥如:EOS59pZ8Wu75WDGeSD5cvEvJMfEia436rn4miLDQz6G9sHBJpS3f8。

创建部署帐号hello:

cleos create account eosio hello EOS59pZ8Wu75WDGeSD5cvEvJMfEia436rn4miLDQz6G9sHBJpS3f8 -p eosio@active

2、向区块链广播提交.wasm合约。

cleos set contract hello /home/game/contracts/hello -p hello@active

3、合约部署之后,调用合约功能测试。

cleos push action hello hi '["bob"]' -p bob@active
cleos push action hello hi '["bob"]' -p alice@active

测试结果正常(留意shell返回内容)。

四、修改合约c++代码、重新编译、重新部署。

1、修改c++代码,例如增加校验:

void hi( name user ) {
   require_auth( user );
   print( "Hello, ", name{user} );
}

2、重新编译:

$ eosio-cpp -o hello.wasm hello.cpp --abigen

3、重新部署(注还是原来的部署帐号):

$ cleos set contract hello /home/game/contracts/hello -p hello@active

4、再测

$ cleos push action hello hi '["bob"]' -p alice@active

这次返回不成功了。

$ cleos push action hello hi '["alice"]' -p alice@active

这个返回成功。

=================================实例2 系统自带合约====================================

一、获取github实例代码

$ cd /home/game/contracts
$ git clone https://github.com/EOSIO/eosio.contracts --branch v1.4.0 --single-branch
$ cd eosio.contracts/eosio.token

二、创建部署帐号eosio.token

$ cleos create account eosio eosio.token EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

三、编译合约代码

$ eosio-cpp -I include -o eosio.token.wasm src/eosio.token.cpp --abigen

四、部署合约

$ cleos set contract eosio.token /home/game/contracts/eosio.contracts/eosio.token --abi eosio.token.abi -p eosio.token@active

五、调用合约功能

1、create token功能(实际上是调用合约c++代码中create函数,并传递了相应的参数)。

$ cleos push action eosio.token create '[ "eosio", "1000000000.0000 SYS"]' -p eosio.token@active

也可用这样的命令:

cleos push action eosio.token create '{"issuer":"eosio", "maximum_supply":"1000000000.0000 SYS"}' -p eosio.token@active

2、Issue Tokens功能

$ cleos push action eosio.token issue '[ "alice", "100.0000 SYS", "memo" ]' -p eosio@active

可以加上-d -j选项( indicate "don't broadcast" and "return transaction as json," ):

$ cleos push action eosio.token issue '["alice", "100.0000 SYS", "memo"]' -p eosio@active -d -j

3、Transfer Tokens功能

$ cleos push action eosio.token transfer '[ "alice", "bob", "25.0000 SYS", "m" ]' -p alice@active

4、查询

$ cleos get currency balance eosio.token bob SYS
25.00 SYS
$ cleos get currency balance eosio.token alice SYS
75.00 SYS

=================================实例3 数据存储合约====================================

一、建立目录环境

$ cd /home/game/contracts
$ mkdir addressbook
$ cd addressbook
$ touch addressbook.cpp

二、合约代码:

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

using namespace eosio;

class [[eosio::contract]] addressbook : public eosio::contract {

public:
  using contract::contract;
  
 //构造函数
  addressbook(name receiver, name code,  datastream<const char*> ds): contract(receiver, code, ds) {}

//upsert函数
  [[eosio::action]]
  void upsert(name user, 
              std::string first_name, 
              std::string last_name, 
              std::string street, 
              std::string city, 
              std::string state) {
    require_auth( user );//权限校验(提交的user必须为提交者自己)
    address_index addresses(_code, _code.value);//多索引表变量
    auto iterator = addresses.find(user.value);
    if( iterator == addresses.end() )
    {//表中无此user,插入
      addresses.emplace(user, [&]( auto& row ) {
       row.key = user;
       row.first_name = first_name;
       row.last_name = last_name;
       row.street = street;
       row.city = city;
       row.state = state;
      });
    }
    else {//表中有此user,更新
      std::string changes;
      addresses.modify(iterator, user, [&]( auto& row ) {
        row.key = user;
        row.first_name = first_name;
        row.last_name = last_name;
        row.street = street;
        row.city = city;
        row.state = state;
      });
    }
  }

  [[eosio::action]]
  void erase(name user) {
    // require_auth(user);//校验,确保只能删除自己的信息

    address_index addresses(_self, _code.value);

    auto iterator = addresses.find(user.value);
    eosio_assert(iterator != addresses.end(), "Record does not exist");
    addresses.erase(iterator);
  }

private:
//定义数据结构表
  struct [[eosio::table]] person {
    name key;
    std::string first_name;
    std::string last_name;
    std::string street;
    std::string city;
    std::string state;
    uint64_t primary_key() const { return key.value; }//为多索引表游标定义primary_key方法
  };
  typedef eosio::multi_index<"people"_n, person> address_index;//多索引表类型

};

EOSIO_DISPATCH( addressbook, (upsert)(erase))

      
    
    

三、编译、创建部署帐号、部署

$ eosio-cpp -o addressbook.wasm addressbook.cpp --abigen

$ cleos create account eosio addressbook EOS59pZ8Wu75WDGeSD5cvEvJMfEia436rn4miLDQz6G9sHBJpS3f8 -p eosio@active

$ cleos set contract addressbook /home/game/contracts/addressbook -p addressbook@active

四、合约的使用测试

$ cleos push action addressbook upsert '["alice", "alice", "liddell", "123 drink me way", "wonderland", "amsterdam"]' -p alice@active

$ cleos push action addressbook upsert '["bob", "bob", "is a loser", "doesnt exist", "somewhere", "someplace"]' -p alice@active

$ cleos get table addressbook addressbook people --lower alice --limit 1

$ cleos push action addressbook erase '["alice"]' -p alice@active

$ cleos get table addressbook addressbook people --lower alice --limit 1

=================================实例4 在实例3的基础上增加内联actions================================

一、修改合约代码(有备注的部分)

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

using namespace eosio;

class [[eosio::contract]] addressbook : public eosio::contract {

public:
  using contract::contract;
  
  addressbook(name receiver, name code,  datastream<const char*> ds): contract(receiver, code, ds) {}

  [[eosio::action]]
  void upsert(name user, std::string first_name, std::string last_name, std::string street, std::string city, std::string state) {
    require_auth( user );

    address_index addresses(_code, _code.value);

    auto iterator = addresses.find(user.value);
    if( iterator == addresses.end() )
    {
      addresses.emplace(user, [&]( auto& row ) {
       row.key = user;
       row.first_name = first_name;
       row.last_name = last_name;
       row.street = street;
       row.city = city;
       row.state = state;
      });
//调用内联action辅助器函数
      send_summary(user, "successfully emplaced record to addressbook");
    }
    else {
      std::string changes;
      addresses.modify(iterator, user, [&]( auto& row ) {
        row.key = user;
        row.first_name = first_name;
        row.last_name = last_name;
        row.street = street;
        row.city = city;
        row.state = state;
      });
//调用内联action辅助器函数
      send_summary(user, "successfully modified record to addressbook");
    }
  }

  [[eosio::action]]
  void erase(name user) {
    require_auth(user);
    address_index addresses(_self, _code.value);
    auto iterator = addresses.find(user.value);
    eosio_assert(iterator != addresses.end(), "Record does not exist");
    addresses.erase(iterator);
//调用内联action辅助器函数
    send_summary(user, "successfully erased record from addressbook");
  }

//notify action
  [[eosio::action]]
  void notify(name user, std::string msg) {
    require_auth(get_self());
    require_recipient(user);
  }

private:
  
  struct [[eosio::table]] person {
    name key;
    std::string first_name;
    std::string last_name;
    std::string street;
    std::string city;
    std::string state;
    uint64_t primary_key() const { return key.value; }
  };

//notify action的包装函数
  void send_summary(name user, std::string message) {
//实例化一个action,并send
    action(
      permission_level{get_self(),"active"_n},//实例一个匿名的授权等级类型的变量作为实参
      get_self(),//获取合约部署帐号权限
      "notify"_n,//对应notify action
      std::make_tuple(user, name{user}.to_string() + message)//data,拼装消息数据字符串
    ).send();
  };

  typedef eosio::multi_index<"people"_n, person> address_index;

};

EOSIO_DISPATCH( addressbook, (upsert)(notify)(erase) ) //宏定义中增加notify的action

二、为eosio.code增加授权

$ cleos set account permission addressbook active '{"threshold": 1,"keys": [{"key": "EOS59pZ8Wu75WDGeSD5cvEvJMfEia436rn4miLDQz6G9sHBJpS3f8","weight": 1}], "accounts": [{"permission":{"actor":"addressbook","permission":"eosio.code"},"weight":1}]}' -p addressbook@owner

三、重新编译合约、部署合约(无需重新创建部署帐号)

$ cd /home/game/contracts/addressbook
$ eosio-cpp -o addressbook.wasm addressbook.cpp --abigen

$ cleos set contract addressbook /home/game/contracts/addressbook

四、合约使用

$ cleos push action addressbook upsert '["alice", "alice", "liddell", "123 drink me way", "wonderland", "amsterdam"]' -p alice@active

$ cleos get actions alice

从命令返回的结果看,留意与之前的不同。

==============================实例5 在实例4的基础上内联actions触发外部合约=============================

一、新建合约目录,将在其中新建作为addressbook合约的外部合约。

$ cd /home/game/contracts
$ mkdir abcounter
$ touch abcounter.cpp

二、abcounter.cpp合约代码

#include <eosiolib/eosio.hpp>

using namespace eosio;

class [[eosio::contract]] abcounter : public eosio::contract {
  public:
    using contract::contract;

    abcounter(name receiver, name code,  datastream<const char*> ds): contract(receiver, code, ds) {}

    [[eosio::action]]
    void count(name user, std::string type) {
      require_auth( name("addressbook"));//指定addressbook合约的部署帐号,也就是只能由这个帐号调用
      count_index counts(name(_code), _code.value);
      auto iterator = counts.find(user.value);
      
      if (iterator == counts.end()) {
        counts.emplace("addressbook"_n, [&]( auto& row ) {
          row.key = user;
          row.emplaced = (type == "emplace") ? 1 : 0;
          row.modified = (type == "modify") ? 1 : 0;
          row.erased = (type == "erase") ? 1 : 0;
        });
      }
      else {
        counts.modify(iterator, "addressbook"_n, [&]( auto& row ) {
          if(type == "emplace") { row.emplaced += 1; }
          if(type == "modify") { row.modified += 1; }
          if(type == "erase") { row.erased += 1; }
        });
      }
    }

  private:
    struct [[eosio::table]] counter {
      name key;
      uint64_t emplaced;
      uint64_t modified;
      uint64_t erased;
      uint64_t primary_key() const { return key.value; }
    };

    using count_index = eosio::multi_index<"counts"_n, counter>;
};

EOSIO_DISPATCH( abcounter, (count));

三、创建部署帐号、编译合约、部署合约

$ cleos create account eosio abcounter EOS59pZ8Wu75WDGeSD5cvEvJMfEia436rn4miLDQz6G9sHBJpS3f8

$ eosio-cpp -o abcounter.wasm abcounter.cpp --abigen

$ cleos set contract abcounter /home/game/contracts/abcounter

四、修改addressbook合约,增加inline action发送到abcounter合约的功能代码

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

using namespace eosio;


class [[eosio::contract]] addressbook : public eosio::contract {

public:
  using contract::contract;
  
  addressbook(name receiver, name code,  datastream<const char*> ds): contract(receiver, code, ds) {}

  [[eosio::action]]
  void upsert(name user, std::string first_name, std::string last_name, std::string street, std::string city, std::string state) {
    require_auth( user );

    address_index addresses(_code, _code.value);

    auto iterator = addresses.find(user.value);
    if( iterator == addresses.end() )
    {
      addresses.emplace(user, [&]( auto& row ) {
       row.key = user;
       row.first_name = first_name;
       row.last_name = last_name;
       row.street = street;
       row.city = city;
       row.state = state;
       send_summary(user, "successfully emplaced record to addressbook");
       increment_counter(user, "emplace");//触发inline action发送到外部合约
      });
    }
    else {
      std::string changes;
      addresses.modify(iterator, user, [&]( auto& row ) {
        row.key = user;
        row.first_name = first_name;
        row.last_name = last_name;
        row.street = street;
        row.city = city;
        row.state = state;
        send_summary(user, "successfully modified record in addressbook.");
        increment_counter(user, "modify");//触发inline action发送到外部合约
      });
    }
  }

  [[eosio::action]]
  void erase(name user) {
    require_auth(user);
    address_index addresses(_self, _code.value);
    auto iterator = addresses.find(user.value);
    eosio_assert(iterator != addresses.end(), "Record does not exist");
    addresses.erase(iterator);
    send_summary(user, "successfully erased record from addressbook");
    increment_counter(user, "erase");//触发inline action发送到外部合约
  }

  [[eosio::action]]
  void notify(name user, std::string msg) {
    require_auth(get_self());
    require_recipient(user);
  }

private:
  
  struct [[eosio::table]] person {
    name key;
    std::string first_name;
    std::string last_name;
    std::string street;
    std::string city;
    std::string state;
    uint64_t primary_key() const { return key.value; }
  };

  void send_summary(name user, std::string message) {
    action(
      permission_level{get_self(),"active"_n},
      get_self(),
      "notify"_n,
      std::make_tuple(user, name{user}.to_string() + message)
    ).send();
  };

  void increment_counter(name user, std::string type) {
    //定义action变量
    action counter = action(
      permission_level{get_self(),"active"_n},
      "abcounter"_n,//外部合约的部署帐号abcounter
      "count"_n,//外部合约中的count action
      std::make_tuple(user, type)
    );

    counter.send();//发送
  }

  typedef eosio::multi_index<"people"_n, person> address_index;

};

EOSIO_DISPATCH( addressbook, (upsert)(notify)(erase))

五、重新编译addressbook合约、重新部署

$ eosio-cpp -o addressbook.wasm addressbook.cpp

$ cleos set contract addressbook /home/game/contracts/addressbook

六、测试使用

$ cleos push action addressbook upsert '["alice", "alice", "liddell", "123 drink me way", "wonderland", "amsterdam"]' -p alice@active

...从结果可以看到已经触发

$ cleos get table abcounter abcounter counts --lower alice --limit 1

$ cleos push action addressbook upsert '["alice", "alice", "liddell", "1 there we go", "wonderland", "amsterdam"]' -p alice@active

$ cleos push action addressbook erase '["alice"]' -p alice@active

$ cleos push action abcounter count '["alice", "erase"]' -p alice@active

$ cleos get table abcounter abcounter counts --lower alice --limit 

七、优化addressbook触发外部合约的处理机制,条件满足才触发外部合约,不满足不触发

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

using namespace eosio;


class [[eosio::contract]] addressbook : public eosio::contract {

public:
  using contract::contract;
  
  addressbook(name receiver, name code,  datastream<const char*> ds): contract(receiver, code, ds) {}

  [[eosio::action]]
  void upsert(name user, std::string first_name, std::string last_name, std::string street, std::string city, std::string state) {
    require_auth(user);

    address_index addresses(_code, _code.value);

    auto iterator = addresses.find(user.value);
    if( iterator == addresses.end() )
    {
      addresses.emplace(user, [&]( auto& row ){
       row.key = user;
       row.first_name = first_name;
       row.last_name = last_name;
       row.street = street;
       row.city = city;
       row.state = state;
       send_summary(user, " successfully emplaced record to addressbook");
       increment_counter(user, "emplace");
      });
    }
    else {
      std::string changes;
      addresses.modify(iterator, user, [&]( auto& row ) {
        
        if(row.first_name != first_name) {
          row.first_name = first_name;
          changes += "first name ";
        }
        
        if(row.last_name != last_name) {
          row.last_name = last_name;
          changes += "last name ";
        }

        if(row.street != street) {
          row.street = street;
          changes += "street ";
        }
        
        if(row.city != city) {
          row.city = city;
          changes += "city ";
        }
        
        if(row.state != state) {
          row.state = state;
          changes += "state ";
        }
      });

//根据条件是否满足情况决定是否发送外部合约的action
      if(changes.length() > 0) {
        send_summary(user, "successfully modified record in addressbook. Fields changed: " + changes);
        increment_counter(user, "modify");
      } else {
        send_summary(user, "called upsert, but request resulted in no changes.");
      }
    }
  }

  [[eosio::action]]
  void erase(name user) {
    require_auth(user);
    address_index addresses(_code, _code.value);
    auto iterator = addresses.find(user.value);
    eosio_assert(iterator != addresses.end(), "Record does not exist");
    addresses.erase(iterator);
    send_summary(user, " successfully erased record from addressbook");
    increment_counter(user, "erase");
  }

  [[eosio::action]]
  void notify(name user, std::string msg) {
    require_auth(get_self());
    require_recipient(user);
  }

private:
  
  struct [[eosio::table]] person {
    name key;
    std::string first_name;
    std::string last_name;
    std::string street;
    std::string city;
    std::string state;
    uint64_t primary_key() const { return key.value; }
  };

  void send_summary(name user, std::string message) {
    action(
      permission_level{get_self(),"active"_n},
      get_self(),
      "notify"_n,
      std::make_tuple(user, name{user}.to_string() + message)
    ).send();
  };

  void increment_counter(name user, std::string type) {
    
    action counter = action(
      permission_level{get_self(),"active"_n},
      "abcounter"_n,
      "count"_n,
      std::make_tuple(user, type)
    );

    counter.send();
  }

  typedef eosio::multi_index<"people"_n, person> address_index;
};

EOSIO_DISPATCH( addressbook, (upsert)(notify)(erase))

猜你喜欢

转载自blog.csdn.net/weixin_33656634/article/details/83860279