1 准备工作
首先在本地将私有链运行起来:
sudo nodeos -e -p eosio --plugin eosio::wallet_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin
私有链默认存储的位置在~/.local/share/eosio/nodeos路径下面。
然后新建一个账户acctoken以便运行eosio.token智能合约。
cleos create account eosio acctoken EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
ps:创建账户命令的格式是:cleos create account creator name OwnerKey ActiveKey
-
创建账户用create account命令
-
是由账户eosio创建的
-
新创建的账户名为acctoken
-
这里OwnerKey和ActiveKey都设置为EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
2 发布基础智能合约eosio.bios
eosio.bios是这个合约是EOS很多基本action的基础系统,所以要保证这个合约的有效执行。这个合约可以让你能够直接控制资源分配,并且有权限访问API。在公链上,这个合约将管理已募集和待募集token,以储备带宽给CPU、内存以及网络活动使用。
这个默认合约eosio.bios可以在EOS源码位置contracts/eosio.bios找到。可以通过cleos来指定这个合约执行:
~/eos$ cleos set contract eosio build/contracts/eosio.bios -p eosio
Reading WAST/WASM from build/contracts/eosio.bios/eosio.bios.wast...
Assembling WASM...
Publishing contract...
executed transaction: 36736dabac246732ef389fb5dd47099887854e25178a320b0e288324b5c87a9c 3288 bytes 2200576 cycles
# eosio <= eosio::setcode {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001581060037f7e7f0060057f7e7e7e7e...
# eosio <= eosio::setabi {"account":"eosio","abi":{"types":[],"structs":[{"name":"set_account_limits","base":"","fields":[{"n...
ps:发布合约命令的格式是:cleos set contract creator contractpath
- 发布合约用set contract
- 是由账户eosio创建的
- 合约位置在build/contracts/eosio.bios路径下
- -p eosio:发布合约这个action是由eosio进行签名的
3 在EOS上发行代币
EOS上面发行代币非常简单,就是先发行eosio.token智能合约,然后依据这个智能合约再发行自己的代币。
发布eos.token智能合约
使用以下命令发布eosio.token智能合约:
~/eos$ cleos set contract acctoken build/contracts/eosio.token -p acctoken
Reading WAST/WASM from build/contracts/eosio.token/eosio.token.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 913da06943c5d00e9adcf6a84b33857d0e7a9507168b7fa907d21b5806cb8235 8192 bytes 1474 us
# eosio <= eosio::setcode {"account":"acctoken","vmtype":0,"vmversion":0,"code":"0061736d01000000017e1560037f7e7f0060057f7e7e7...
# eosio <= eosio::setabi {"account":"acctoken","abi":"0e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d65050...
warning: transaction executed locally, but may not be confirmed by the network yet ]
发行代币
就像以太坊token那样,我们在EOS上可以更加方便的创建一个基于EOS的代币。首先,去token合约中的头文件eosio.token.hpp,查看一下token相关的接口都有哪些,其中有一个create函数,我们正是将要使用这个函数来创建token,所以我们可以留意一下它的参数都包括哪些。
class token : public contract {
public:
token( account_name self ):contract(self){}
void create( account_name issuer,
asset maximum_supply);
void issue( account_name to, asset quantity, string memo );
void transfer( account_name from,
account_name to,
asset quantity,
string memo );
使用下列命令来调用create函数创建代币:
~/eos$ cleos push action acctoken create '["eosio","1000000000.0000 EOS"]' -p acctoken
executed transaction: 932ab81c295f54259a21992911c4aca1c1d118902341d28d9acad5d47c9f3f9f 208 bytes 12981 us
# acctoken <= acctoken::create {"issuer":"eosio","maximum_supply":"1000000000.0000 EOS"}
warning: transaction executed locally, but may not be confirmed by the network yet ]
- 调用智能合约的格式是cleos push action contract_publisher function_name parameter
- 这里调用了create函数来创建代币,带了俩个参数eosio和1000000000.0000 EOS
- 代币发行账户是eosio,代币最大发行10亿,单位是EOS
ps:账户和合约的关系是:
- 可以使用某个账户作为合约发布者,那么该账户就拥有了此合约的操作权,后续对该合约的操作不必再写合约名字,直接使用该账户加上合约内部函数即可。
- 一个账户可以发布多次不同的合约,但是以最后一次为有效,因为作为合约code的hash是只有一个,每次部署新的合约会覆盖原有的。
代币发放
我们已经有了EOS代币,我们现在将代币发行100个给inita账户,通过调用issue函数:
~/eos$ cleos push action acctoken issue '["inita","100.0000 EOS","memo"]' -p eosio
executed transaction: 66cc35d5c3102f36f7eb6eda8f786b6a2e997e5723e94614fbe6d0b3f9150941 216 bytes 2091 us
# acctoken <= acctoken::issue {"to":"inita","quantity":"100.0000 EOS","memo":"memo"}
# acctoken <= acctoken::transfer {"from":"eosio","to":"inita","quantity":"100.0000 EOS","memo":"memo"}
# eosio <= acctoken::transfer {"from":"eosio","to":"inita","quantity":"100.0000 EOS","memo":"memo"}
# inita <= acctoken::transfer {"from":"eosio","to":"inita","quantity":"100.0000 EOS","memo":"memo"}
warning: transaction executed locally, but may not be confirmed by the network yet ]
查询inita账户余额:
~/eos$ cleos get currency balance acctoken inita EOS
100.0000 EOS
转账
调用transfer函数来进行转账
~/eos$ cleos push action acctoken transfer '["inita","initb","25.0000 EOS","memo"]' -p inita
executed transaction: 3b24a573903e689c66d990fe420232ac0cbb88375195406f87f2ab13ed4f1eb5 224 bytes 895 us
# acctoken <= acctoken::transfer {"from":"inita","to":"initb","quantity":"25.0000 EOS","memo":"memo"}
# inita <= acctoken::transfer {"from":"inita","to":"initb","quantity":"25.0000 EOS","memo":"memo"}
# initb <= acctoken::transfer {"from":"inita","to":"initb","quantity":"25.0000 EOS","memo":"memo"}
warning: transaction executed locally, but may not be confirmed by the network yet ]
lzj@lzj-VirtualBox:~/eos$ cleos get currency balance acctoken inita
75.0000 EOS
lzj@lzj-VirtualBox:~/eos$ cleos get currency balance acctoken initb
25.0000 EOS
4 创建自己的Hello World智能合约
合约代码
我们先创建一个名为hello的文件夹,然后新建一个hello.cpp
#include <eosiolib/eosio.hpp>
using namespace eosio;
class hello : public eosio::contract {
public:
using contract::contract;
/// @abi action
void hi( account_name user ) {
print( "Hello, ", name{user} );
}
};
EOSIO_ABI( hello, (hi) )
第1行引用了eosio标准库,eosio标准库定义了eos开发需要的一些基本数据结构、函数以及常用的宏。
第2行指定名字空间eosio,eosio标准库中定义的开发接口都在名字空间eosio中。
第4行定义了一个合约类,该类从contract类派生,contract类是在eosio标准库中被定义。
第8行注释使用了@abi,这个注释将被eosio编译工具eosiocpp使用,eosiocpp工具可以根据@abi注释来生成abi文件。
第9~11行,是该合约的方法函数,也被称为action,执行合约时需要指定方法以及参数,最终在合约的方法函数中被执行。在这里例子中,该方法只做了一件事情,调用eosio标准库接口打印hello, world。
第14行是一个宏,该宏定义了eos合约入口的标准写法
编译合约
使用命令先生成wast文件
$ sudo eosiocpp -o hello.wast hello.cpp
然后继续生成abi文件:
hello$ sudo eosiocpp -g hello.abi hello.cpp
2018-12-03T08:31:31.770 thread-0 abi_generator.hpp:68 ricardian_contracts ] Warning, no ricardian clauses found for hello
2018-12-03T08:31:31.771 thread-0 abi_generator.hpp:75 ricardian_contracts ] Warning, no ricardian contract found for hi
Generated hello.abi ...
发布hello合约
创建了一个账户acchello
~/eos$ cleos create account eosio acchello EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
executed transaction: be27102f4789b9c1f65db7166e883c822e4e7d6be25f490b22dd4ff0006955d1 288 bytes 561 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"acchello","owner":{"threshold":1,"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVP...
warning: transaction executed locally, but may not be confirmed by the network yet ]
然后使用acchello发布智能合约:
~/eos$ cleos set contract acchello contracts/hello/ -p acchello
Reading WAST/WASM from contracts/hello/hello.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: b2dd4a0c48d1e2ddee1b432bdf7f9a5ff122ed01a50aaa23e27c817978bf5348 1896 bytes 515 us
# eosio <= eosio::setcode {"account":"acchello","vmtype":0,"vmversion":0,"code":"0061736d01000000013b0c60027f7e006000017e60027...
# eosio <= eosio::setabi {"account":"acchello","abi":"0e656f73696f3a3a6162692f312e30000102686900010475736572046e616d650100000...
warning: transaction executed locally, but may not be confirmed by the network yet ]
调用hello智能合约
:~/eos$ cleos push action acchello hi '["bush"]' -p acchello
executed transaction: 139a54fea8bf3dc20dc622f0c7494325ea4e0bc3decbd8c8b7a2e24b2b378f6b 192 bytes 356 us
# acchello <= acchello::hi {"user":"bush"}
>> Hello, bush
warning: transaction executed locally, but may not be confirmed by the network yet ]
ps:有人会反应执行了hi函数不在控制台打印字符串,原因是需要在nodeos启动参数里加上--contracts-console参数
加入权限
目前我们的hello合约是不限制hi参数的,也就是说其实我们是没有“bush”这个签名人的,也就是说这个参数中无论是否传入账户名,都可以输出。我们期望智能合约hi函数的参数必须是有效账户名,同时只有该账户拥有当前action的签名权。所以,我们要修改hello.cpp文件。
/// @abi action
void hi( account_name user ) {
require_auth(user);// 只有该user账户有权签名当前action
print( "Hello, ", name{user} );
}
重新编译并发布智能合约,再传入非有效账户名时,或者用其他账户签名的时候就会报错:
~/eos$ cleos push action acchello hi '["bush"]' -p acchello
Error 3090004: Missing required authority
~/eos$ cleos push action acchello hi '["acchello"]' -p acchello
executed transaction: bc0d46e8bed951733967a78dfd198578eec8bb4087c621480f79fe8dd6c1f73d 192 bytes 553 us
# acchello <= acchello::hi {"user":"acchello"}
>> Hello, acchello
warning: transaction executed locally, but may not be confirmed by the network yet ]