1. 概述
官方關(guān)于智能合約介紹的文檔:
https://developers.eos.io/eosio-cpp/docs/introduction
Hello World智能合約文檔:
https://developers.eos.io/eosio-cpp/docs/hello-world
基于EOSIO的區(qū)塊鏈?zhǔn)褂?a target="_blank" rel="nofollow">WebAssembly(WASM)執(zhí)行用戶生成的應(yīng)用程序和代碼。WASM是一種新興的Web標(biāo)準(zhǔn),得到了Google,Microsoft,Apple和其他公司的廣泛支持。目前,用于構(gòu)建編譯為WASM的應(yīng)用程序的最成熟的工具鏈?zhǔn)?a target="_blank" rel="nofollow">clang / llvm及其C / C ++編譯器。為獲得最佳兼容性,建議使用EOSIO工具鏈。
第三方開發(fā)的其他工具鏈包括:Rust,Python和Solidity。雖然這些其他語言可能看起來更簡單,但它們的性能可能會影響可以構(gòu)建的應(yīng)用程序的規(guī)模。我們希望C ++將成為開發(fā)高性能和安全智能合約的最佳語言,并計(jì)劃在可預(yù)見的未來使用C ++。
在使用C++編寫完成合約代碼后,通過EOSIO軟件中提供的eosiocpp工具,將C++代碼編譯生成WASM(wasm的文本格式是后綴是wast)文件和abi文件,再利用cleos工具(將代碼部署到鏈上,也就是存到區(qū)塊數(shù)據(jù)中。
2. EOS智能合約與以太坊智能合約的區(qū)別
以下內(nèi)容節(jié)選自全面理解EOS——4.測試智能合約與EOS發(fā)幣
1.名稱不同
在EOS中具有賬號的概念,智能合約名也就是賬號名。而以太坊中的合約是一個個不同的地址。
2.升級方式不同
以太坊的合約不可升級,一旦部署之后,代碼不可修改。如果需要修改,只能在一個新的地址上重新部署。而EOS的智能合約和賬號綁定后,賬號可直接升級智能合約的代碼,其實(shí)就相當(dāng)于向鏈上重新上傳了代碼。
3.資源消耗不同
以太坊智能合約的執(zhí)行需要消耗gas,也就是每個步驟都有手續(xù)費(fèi),手續(xù)費(fèi)不夠就不會繼續(xù)執(zhí)行,之前的操作也會被回滾,而且手續(xù)費(fèi)也不退。而EOS的智能合約不需要首先費(fèi),但是部署合約要消耗RAM,傳送信息和執(zhí)行合約需要消耗抵押而得的CPU和網(wǎng)絡(luò)帶寬。
3.文件結(jié)構(gòu)
eosiocpp工具可以初始化一份新的合約,簡化我們的工作。
使用eosiocpp創(chuàng)建一份智能合約:
yuyangdeMacBook-Pro:eos yuyang$ eosiocpp -n hello
此命令會在當(dāng)前目錄創(chuàng)建一個名為hello的文件夾,里面包含三個文件:
hello.abi
abi文件
hello.cpp
包含合同函數(shù)實(shí)現(xiàn)的源文件
hello.hpp
頭文件:包含變量,常量,和cpp文件的函數(shù)引用
如果.cpp文件是使用eosiocpp工具生成的,則生成的.cpp文件將類似于以下內(nèi)容:
#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文件的內(nèi)容如下:
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <hello.hpp>
/**
* 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 創(chuàng)建賬號
創(chuàng)建名為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
導(dǎo)入密鑰對到錢包:
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
創(chuàng)建賬號:
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中的內(nèi)容:
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.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) )
EOSIO_ABI中定義了對外可調(diào)用的函數(shù)hi方法,在hi方法中打印了調(diào)用者的賬戶名
生成.wast文件
yuyangdeMacBook-Pro:eos yuyang$ cd hello
yuyangdeMacBook-Pro:hello yuyang$ eosiocpp -o hello.wast hello.cpp
多了hello.wasm和hello.wast文件
將剛才創(chuàng)建的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
應(yīng)用程序二進(jìn)制接口(ABI)是一個基于JSON的描述,介紹如何在JSON和二進(jìn)制表示之間轉(zhuǎn)換用戶操作。ABI還描述了如何將數(shù)據(jù)庫狀態(tài)轉(zhuǎn)換為JSON或從JSON轉(zhuǎn)換。通過ABI描述合同后,開發(fā)人員和用戶將能夠通過JSON無縫地與您的合同進(jìn)行交互。
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 合約交互
在執(zhí)行調(diào)用命令之前,我們先簡單地了解EOS中的一個概念:transaction和action。
qction表示單個操作,而transaction是一個或多個action的集合。action是合約和賬戶之間進(jìn)行通信的方式。action可以單獨(dú)執(zhí)行,或者組合起來作為一個整體執(zhí)行。EOS中的action就相當(dāng)于以太坊中的transaction。
調(diào)用合約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
如果看不到合約執(zhí)行時的打印內(nèi)容。可以重啟nodeos,并且加上--contracts-console參數(shù)啟動,以查看合約的輸出內(nèi)容:
yuyangdeMacBook-Pro:nodeos yuyang$ nodeos --contracts-console
或者編輯配置文件~/Library/Application Support/eosio/nodeos/config中的config.ini,找到以下內(nèi)容:
# print contract's output to console (eosio::chain_plugin)
contracts-console = false
將false改為true
再次調(diào)用合約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的打印內(nèi)容
再查看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]
由于此時合約授權(quán)所有人調(diào)用hi方法,所以也可以進(jìn)行如下操作:
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
如果我們希望授權(quán)者和調(diào)用者為同一個賬戶,可以加入以下代碼:
void hi( account_name user ) {
require_auth( user );
print( "Hello, ", name{user} );
}
重新編譯wast文件,生成abi文件,再次部署合約并調(diào)用,如果此時授權(quán)者和調(diào)用者并非同一個賬戶,合約會拋出錯誤:
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]
我們重新將授權(quán)者設(shè)為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
將授權(quán)者設(shè)為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