1. 概述
官方关于智能合约介绍的文档:
https://developers.eos.io/eosio-cpp/docs/introduction
Hello World智能合约文档:
https://developers.eos.io/eosio-cpp/docs/hello-world
基于EOSIO
的区块链使用WebAssembly(WASM)执行用户生成的应用程序和代码。WASM
是一种新兴的Web
标准,得到了Google
,Microsoft
,Apple
和其他公司的广泛支持。目前,用于构建编译为WASM
的应用程序的最成熟的工具链是clang / llvm及其C / C ++
编译器。为获得最佳兼容性,建议使用EOSIO
工具链。
第三方开发的其他工具链包括:Rust
,Python
和Solidity
。虽然这些其他语言可能看起来更简单,但它们的性能可能会影响可以构建的应用程序的规模。我们希望C ++
将成为开发高性能和安全智能合约的最佳语言,并计划在可预见的未来使用C ++
。
在使用C++
编写完成合约代码后,通过EOSIO
软件中提供的eosiocpp
工具,将C++
代码编译生成WASM
(wasm
的文本格式是后缀是wast
)文件和abi
文件,再利用cleos
工具(将代码部署到链上,也就是存到区块数据中。
2. EOS智能合约与以太坊智能合约的区别
以下内容节选自全面理解EOS——4.测试智能合约与EOS发币
1.名称不同
在EOS中具有账号的概念,智能合约名也就是账号名。而以太坊中的合约是一个个不同的地址。
2.升级方式不同
以太坊的合约不可升级,一旦部署之后,代码不可修改。如果需要修改,只能在一个新的地址上重新部署。而EOS的智能合约和账号绑定后,账号可直接升级智能合约的代码,其实就相当于向链上重新上传了代码。
3.资源消耗不同
以太坊智能合约的执行需要消耗gas,也就是每个步骤都有手续费,手续费不够就不会继续执行,之前的操作也会被回滚,而且手续费也不退。而EOS的智能合约不需要首先费,但是部署合约要消耗RAM
,传送信息和执行合约需要消耗抵押而得的CPU
和网络带宽
。
3.文件结构
eosiocpp工具可以初始化一份新的合约,简化我们的工作。
使用eosiocpp
创建一份智能合约:
yuyangdeMacBook-Pro:eos yuyang$ eosiocpp -n hello
此命令会在当前目录创建一个名为hello
的文件夹,里面包含三个文件:
hello.abi
abi文件
hello.cpp
包含合同函数实现的源文件
hello.hpp
头文件:包含变量,常量,和cpp文件
的函数引用
如果.cpp
文件是使用eosiocpp
工具生成的,则生成的.cpp
文件将类似于以下内容:
#include
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) )
可能官方文档没更新,我这里的.cpp
文件的内容如下:
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include
/**
* The init() and apply() methods must have C calling convention so that the blockchain can lookup and
* call these methods.
*/
extern "C" {
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
eosio::print( "Hello World: ", eosio::name(code), "->", eosio::name(action), "\n" );
}
} // extern "C"
4.Hello World智能合约
4.1 创建账号
创建名为hello.code
的账号用于部署合约:
打开并解锁钱包:
yuyangdeMacBook-Pro:cleos yuyang$ cleos wallet open
Opened: default
yuyangdeMacBook-Pro:cleos yuyang$ cleos wallet list
Wallets:
[
"default"
]
yuyangdeMacBook-Pro:cleos yuyang$ cleos wallet unlock --password PW5KQwfoDJDUUTTEdZzTpSHYfmvSWxL23jefVHLi6UL74Uw9em6WA
Unlocked: default
yuyangdeMacBook-Pro:cleos yuyang$ cleos wallet list
Wallets:
[
"default *"
]
生成密钥对:
yuyangdeMacBook-Pro:cleos yuyang$ cleos create key --to-console
Private key: 5JixXRHHAnxL68LnyvSBTKszZiZgHFAQctHVVYAFp4fwijFE8SZ
Public key: EOS64rVEsvzXnTiGSoYaMockxcvxdj7eM57anUoW1eNcSgxmijXTK
yuyangdeMacBook-Pro:cleos yuyang$ cleos create key --to-console
Private key: 5KhJsLuwzQ8ShdrTVnvgmXZT4J1iGHF16YAyyGFPxeMNVgWVyZF
Public key: EOS8JLJVwbQv9Dp4nnLg8Bso4RvXgsY3MeNBTU5LfLcD8WgKL5jUT
导入密钥对到钱包:
yuyangdeMacBook-Pro:cleos yuyang$ cleos wallet import --private-key 5JixXRHHAnxL68LnyvSBTKszZiZgHFAQctHVVYAFp4fwijFE8SZ
imported private key for: EOS64rVEsvzXnTiGSoYaMockxcvxdj7eM57anUoW1eNcSgxmijXTK
yuyangdeMacBook-Pro:cleos yuyang$ cleos wallet import --private-key 5KhJsLuwzQ8ShdrTVnvgmXZT4J1iGHF16YAyyGFPxeMNVgWVyZF
imported private key for: EOS8JLJVwbQv9Dp4nnLg8Bso4RvXgsY3MeNBTU5LfLcD8WgKL5jUT
创建账号:
yuyangdeMacBook-Pro:cleos yuyang$ cleos create account eosio hello.code EOS64rVEsvzXnTiGSoYaMockxcvxdj7eM57anUoW1eNcSgxmijXTK EOS8JLJVwbQv9Dp4nnLg8Bso4RvXgsY3MeNBTU5LfLcD8WgKL5jUT
executed transaction: 7904dc63327d21b01b097f80ec6272a4ca656b3120efe5e9f39738cd5b5c263f 200 bytes 3394 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"hello.code","owner":{"threshold":1,"keys":[{"key":"EOS64rVEsvzXnTiGSoYaMo...
2018-08-29T03:35:56.708 thread-0 main.cpp:455 print_result warning: transaction executed locally, but may not be confirmed by the network yet
查看账号:
yuyangdeMacBook-Pro:cleos yuyang$ cleos get account hello.code
permissions:
owner 1: 1 EOS64rVEsvzXnTiGSoYaMockxcvxdj7eM57anUoW1eNcSgxmijXTK
active 1: 1 EOS8JLJVwbQv9Dp4nnLg8Bso4RvXgsY3MeNBTU5LfLcD8WgKL5jUT
memory:
quota: unlimited used: 2.66 KiB
net bandwidth:
used: unlimited
available: unlimited
limit: unlimited
cpu bandwidth:
used: unlimited
available: unlimited
limit: unlimited
4.2 编写代码并生成wast和abi文件
使用下面代码覆盖hello.cpp
中的内容:
#include
#include
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) )
EOSIO_ABI中定义了对外可调用的函数hi方法,在hi方法中打印了调用者的账户名
生成.wast
文件
yuyangdeMacBook-Pro:eos yuyang$ cd hello
yuyangdeMacBook-Pro:hello yuyang$ eosiocpp -o hello.wast hello.cpp
多了hello.wasm
和hello.wast
文件
将刚才创建的hello文件夹
中的hello.abi
文件删除,我们要重新生成abi文件
yuyangdeMacBook-Pro:hello yuyang$ eosiocpp -g hello.abi hello.cpp
2018-08-29T03:45:49.132 thread-0 abi_generator.hpp:68 ricardian_contracts ] Warning, no ricardian clauses found for hello
2018-08-29T03:45:49.133 thread-0 abi_generator.hpp:75 ricardian_contracts ] Warning, no ricardian contract found for hi
Generated hello.abi ...
ABI
应用程序二进制接口(ABI)是一个基于JSON的描述,介绍如何在JSON和二进制表示之间转换用户操作。ABI还描述了如何将数据库状态转换为JSON或从JSON转换。通过ABI描述合同后,开发人员和用户将能够通过JSON无缝地与您的合同进行交互。
4.3 部署合约
yuyangdeMacBook-Pro:cleos yuyang$ cleos set contract hello.code ../../../hello -p hello.code@active
Reading WASM from ../../../hello/hello.wasm...
Publishing contract...
executed transaction: 14fc91fcdb35775b42a98e71267959aae508d0858406610da7b59520f20aebd3 1800 bytes 5738 us
# eosio <= eosio::setcode {"account":"hello.code","vmtype":0,"vmversion":0,"code":"0061736d01000000013b0c60027f7e006000017e600...
# eosio <= eosio::setabi {"account":"hello.code","abi":"0e656f73696f3a3a6162692f312e30000102686900010475736572046e616d6501000...
2018-08-29T06:22:51.809 thread-0 main.cpp:455 print_result warning: transaction executed locally, but may not be confirmed by the network yet
4.4 合约交互
在执行调用命令之前,我们先简单地了解EOS中的一个概念:transaction
和action
。
qction
表示单个操作,而transaction
是一个或多个action
的集合。action
是合约和账户之间进行通信的方式。action
可以单独执行,或者组合起来作为一个整体执行。EOS
中的action
就相当于以太坊中的transaction
。
调用合约hi
方法:
yuyangdeMacBook-Pro:cleos yuyang$ cleos push action hello.code hi '["user"]' -p user@active
executed transaction: 9f109157ea7ea19bd069b6befa28c707cd5a40777c8827a9e474c926f5d9d0e0 104 bytes 2159 us
# hello.code <= hello.code::hi {"user":"user"}
2018-08-29T06:43:16.925 thread-0 main.cpp:455 print_result warning: transaction executed locally, but may not be confirmed by the network yet
如果看不到合约执行时的打印内容。可以重启nodeos
,并且加上--contracts-console
参数启动,以查看合约的输出内容:
yuyangdeMacBook-Pro:nodeos yuyang$ nodeos --contracts-console
或者编辑配置文件~/Library/Application Support/eosio/nodeos/config
中的config.ini
,找到以下内容:
# print contract's output to console (eosio::chain_plugin)
contracts-console = false
将false
改为true
再次调用合约hi
方法:
yuyangdeMacBook-Pro:cleos yuyang$ cleos push action hello.code hi '["user"]' -p user@active
executed transaction: 89eed32e56d38ef91db92633568f40343f8e9389ceb0df5c15c7c46c51319d23 104 bytes 7449 us
# hello.code <= hello.code::hi {"user":"user"}
>> Hello, user
2018-08-29T06:48:46.087 thread-0 main.cpp:455 print_result warning: transaction executed locally, but may not be confirmed by the network yet
可以看到>> Hello, user
的打印内容
再查看nodeos
中的输出:
2018-08-29T06:48:46.005 thread-0 producer_plugin.cpp:1302 produce_block ] Produced block 0000d235ac33678d... #53813 @ 2018-08-29T06:48:46.000 signed by eosio [trxs: 0, lib: 53812, confirmed: 0]
2018-08-29T06:48:46.085 thread-0 apply_context.cpp:28 print_debug ]
[(hello.code,hi)->hello.code]: CONSOLE OUTPUT BEGIN =====================
Hello, user
[(hello.code,hi)->hello.code]: CONSOLE OUTPUT END =====================
2018-08-29T06:48:46.503 thread-0 producer_plugin.cpp:1302 produce_block ] Produced block 0000d236ab3eb059... #53814 @ 2018-08-29T06:48:46.500 signed by eosio [trxs: 1, lib: 53813, confirmed: 0]
由于此时合约授权所有人调用hi
方法,所以也可以进行如下操作:
yuyangdeMacBook-Pro:cleos yuyang$ cleos push action hello.code hi '["user"]' -p tester@active
executed transaction: 5c5232d118c8cf60d593435fdaeef6905163cfeeb30473fe97050e71459d7095 104 bytes 303 us
# hello.code <= hello.code::hi {"user":"user"}
>> Hello, user
2018-08-29T07:41:36.166 thread-0 main.cpp:455 print_result warning: transaction executed locally, but may not be confirmed by the network yet
如果我们希望授权者和调用者为同一个账户,可以加入以下代码:
void hi( account_name user ) {
require_auth( user );
print( "Hello, ", name{user} );
}
重新编译wast
文件,生成abi
文件,再次部署合约并调用,如果此时授权者和调用者并非同一个账户,合约会抛出错误:
yuyangdeMacBook-Pro:cleos yuyang$ cleos push action hello.code hi '["tester"]' -p user@active
Error 3090004: Missing required authority
Ensure that you have the related authority inside your transaction!;
If you are currently using 'cleos push action' command, try to add the relevant authority using -p option.
nodeos
日记输出:
2018-08-29T08:15:36.002 thread-0 producer_plugin.cpp:1302 produce_block ] Produced block 0000fae9ce9a18be... #64233 @ 2018-08-29T08:15:36.000 signed by eosio [trxs: 0, lib: 64232, confirmed: 0]
2018-08-29T08:15:36.436 thread-0 http_plugin.cpp:472 handle_exception ] FC Exception encountered while processing chain.push_transaction
2018-08-29T08:15:36.436 thread-0 http_plugin.cpp:473 handle_exception ] Exception Details: 3090004 missing_auth_exception: Missing required authority
missing authority of tester
{"account":"tester"}
thread-0 apply_context.cpp:131 require_authorization
pending console output:
{"console":""}
thread-0 apply_context.cpp:61 exec_one
2018-08-29T08:15:36.503 thread-0 producer_plugin.cpp:1302 produce_block ] Produced block 0000faea889f31c9... #64234 @ 2018-08-29T08:15:36.500 signed by eosio [trxs: 0, lib: 64233, confirmed: 0]
我们重新将授权者设为tester
,正常
yuyangdeMacBook-Pro:cleos yuyang$ cleos push action hello.code hi '["tester"]' -p tester@active
executed transaction: bd9ed2dd386de13ce622c6c6184acd7085c8cc84a5ee0f86d58ae1abbaa5100e 104 bytes 332 us
# hello.code <= hello.code::hi {"user":"tester"}
>> Hello, tester
2018-08-29T08:16:59.641 thread-0 main.cpp:455 print_result warning: transaction executed locally, but may not be confirmed by the network yet
将授权者设为user
,正常
yuyangdeMacBook-Pro:cleos yuyang$ cleos push action hello.code hi '["user"]' -p user@active
executed transaction: aabe5baab544c9386f5162b8327e34735fa2530e6497d64b356dfe380f53587a 104 bytes 293 us
# hello.code <= hello.code::hi {"user":"user"}
>> Hello, user
2018-08-29T08:18:10.926 thread-0 main.cpp:455 print_result warning: transaction executed locally, but may not be confirmed by the network yet