以几个实例演绎开发过程。
=================================实例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))