原文連接:醒者呆的博客園,www.cnblogs.com/Evsward/p/e…html
EOS,智能合約,abi,wasm,cleos,eosiocpp,開發調試,錢包,帳戶,簽名權限node
本文旨在針對EOS智能合約進行一個完整的實操演練,過程當中深刻熟悉掌握整個EOS智能合約的流程,過程當中出現的問題也會及時研究併入咱們本身的知識體系。本文會主要跟隨EOS官方Wiki的智能合約部分進行研究學習,主要分爲ios
EOS的智能合約採用C++ 編寫,由於C++ 的高效性,沒有C++ 編程基礎的同窗能夠先學習《Efficient&Elegant:Java程序員入門Cpp》。EOS中用戶開發的應用程序或代碼都是經過WebAssembly(WASM)來與主鏈進行交互的,它的編譯工具是clang.llvm。關於EOS相關的基礎準備請先過目《區塊鏈3.0:擁抱EOS》,這裏面介紹了包括EOS概念,安裝部署以及工具等基礎內容,其中包括了上面提到的《開啓一個私有鏈》。這裏還有一些準備知識須要過目:c++
這些都瞭解了之後,咱們繼續智能合約的準備。git
首先,先肯定區塊鏈中錢包的概念:程序員
錢包是一個私鑰庫,用來受權發生在區塊鏈上的動做(action,記住這個概念)。這些私鑰使用密碼生成,被加密存儲在磁盤上。這個密碼應該被儲存在一個安全的密碼管理器中。
複製代碼
提取一下重點:github
操做流程:算法
liuwenbin@liuwenbin-H81M-DS2:~$ cleos wallet create
Creating wallet: default
Save password to use in the future to unlock this wallet.
Without password imported keys will not be retrievable.
"PW5KeMG82A6zEmmHK4sBj3HE8pxBYBFw4CXVoQGt24Zy7AoRgMWxn"
複製代碼
default改成自定義錢包名字wbs:數據庫
liuwenbin@liuwenbin-H81M-DS2:~$ cleos wallet create -n wbs
Creating wallet: wbs
Save password to use in the future to unlock this wallet.
Without password imported keys will not be retrievable.
"PW5KPAwucXR66NqqzW5R5wdkHCMNGyHLrCWVPaE1nhj7hfacP7ZaL"
複製代碼
wbs錢包解密:編程
liuwenbin@liuwenbin-H81M-DS2:~$ cleos wallet unlock -n wbs
password: Unlocked: wbs
複製代碼
加密即改成:
liuwenbin@liuwenbin-H81M-DS2:~$ cleos wallet lock -n wbs
Locked: wbs
複製代碼
若是不加-n參數,即操做默認錢包。
導入初始帳戶eosio的主祕鑰到錢包
全部新的blockchains,都是經過主祕鑰啓動,惟一初始帳戶:eosio。要與區塊鏈交互,須要將這個初始帳戶的私鑰導入到你的錢包。
複製代碼
這個主祕鑰咱們在上一篇文章也分析到了,是在~/.local/share/eosio/nodeos/config文件夾下的config.ini文件中自動配置的(可修改,默認安裝EOS會生成一個)。
# 值爲[公鑰,私鑰WIF編碼的]
private-key = ["EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV","5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"]
複製代碼
Private key should be encoded in base58 WIF。因此上篇文章中常常出現的WIF的意思是一種base58編碼方式。
複製代碼
$ cleos wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
imported private key for: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
複製代碼
如今咱們擁有了一個錢包default,該錢包內部包含一個默認主密鑰的帳戶eosio,默認的智能合約eosio.bios已經可使用,這個合約是EOS不少基本action的基礎系統,因此要保證這個合約的有效執行。這個合約可讓你可以直接控制資源分配,而且有權限訪問API。在公鏈上,這個合約將管理已募集和待募集token,以儲備帶寬給CPU、內存以及網絡活動使用。咱們提取一下重點:
這個默認合約eosio.bios能夠在EOS源碼位置contracts/eosio.bios找到。能夠經過cleos來指定這個合約執行:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/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...
複製代碼
命令行工具仍舊是使用cleos,經過set contract來執行指定合約,後面跟着帳戶名稱(這裏是默認的eosio,咱們剛剛導入的),而後是指定合約的路徑。
命令最後的「-p eosio」的含義是:使用帳戶eosio(使用的是帳戶的私鑰)來爲這個action簽名。
讀取 WAST/WASM文件(這個文件是被新部署到build目錄下的)
裝配 WASM
發佈合約
執行交易(合約也是一個交易),這裏經過兩個動做來生成一個交易,
從技術角度來將,abi是可選的,全部的EOSIO工具取決於它的易用性。
eosio <= eosio::setcode {...}
這一行的閱讀方式爲:action setcode是eosio命名空間下的,同時它是經過eosio帳戶受權來執行的,帶的參數有...
注意,action是能夠被多個合約執行的。
複製代碼
此時私鏈日誌顯示:
eosio generated block 11f6a66b... #6149 @ 2018-04-23T10:20:21.500 with 0 trxs, lib: 6148
1221781ms thread-0 abi_serializer.hpp:349 extract ] vo: {...}
複製代碼
私鏈日誌打出來合約部署的相關信息。其中vo的值不少,它的結構是:
{
"signatures": [
"EOSJzRXuKWJT77BGmBv26SoHGcKkR1XyaCBzkd8Yck5wE9fFptcgLReQZ8wZsxjizAbMxELmVnFPYkv5rT5VDxYk3UoiRPDTC"(這一看就是公鑰格式)
],
"compression": "zlib",
"packed_context_free_data": "",
"packed_trx": "不少內容"
}
複製代碼
這裏面的結構包含一個簽名串,一個加密信息,打包交易信息是核心數據,它包含了很長的交易相關的打包後的格式的內容。
上面提到了如何在錢包中導入默認帳戶eosio,下面來看一下如何建立帳戶。
注意:key和帳戶是分開的,咱們接下來建立兩個帳戶,他們可使用同一套祕鑰。
複製代碼
建立祕鑰
首先,建立祕鑰對:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos create key
Private key: 5KFGqe3Bv2maZ5jGMrufdkLhGV91ZYSysBWfxUUaR9VzbDk2UdA
Public key: EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3
複製代碼
已生成一對公鑰和私鑰。下面將私鑰導入咱們本身的錢包wbs
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos wallet import -n wbs 5KFGqe3Bv2maZ5jGMrufdkLhGV91ZYSysBWfxUUaR9VzbDk2UdA
imported private key for: EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3
複製代碼
注意格式:
Usage: cleos wallet import [OPTIONS] key
複製代碼
建立user和tester兩個帳戶
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos create account eosio user EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3 EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3
executed transaction: 927762a883e1c92a695a51c312eb5339bf911c1dbd56bab53e74d5fe20365106 352 bytes 102400 cycles
# eosio <= eosio::newaccount {"creator":"eosio","name":"user","owner":{"threshold":1,"keys":[{"key":"EOS6nbWS7ZReiPMdMABoEmVBYany...
複製代碼
注意,建立帳戶命令的格式是:cleos create account [OPTIONS] creator name OwnerKey ActiveKey
複製代碼
觀察執行結果,這時的action是newaccount,後面是action的參數,以json格式存在。下面同理生成tester帳戶(只需更改上面的user爲tester便可,祕鑰採用同一個)。
查詢帳戶
咱們能夠經過一個公鑰來查詢有它「管轄」的帳戶列表,這是經過eosio::account_history_api_plugin插件完成的操做:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos get accounts EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3
{
"account_names": [
"tester",
"user"
]
}
複製代碼
命令行是經過get accounts子命令來執行,後面要加入查詢條件 -> 公鑰。
經過以上的操做,咱們熟悉了cleos命令的一些經常使用操做,包括建立錢包、加解鎖,執行基礎系統級合約,以及很是經常使用的建立祕鑰、經過祕鑰建立帳戶,經過祕鑰反查帳戶。過程當中,咱們涉及到不少action的學習研究。
咱們已經準備好了錢包、帳戶、基本合約系統(用於支持基本action)。下面咱們能夠正式展開對智能合約的學習,本章主要經過源碼中已經存在的較簡單的token和exchange兩個合約來學習。
爲了不混淆,咱們根據上面學習過的內容,從新建立一個帳戶eosio.token專門用來執行token智能合約。
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos create account eosio eosio.token EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3 EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3
executed transaction: 745de4ad0d7e2f415a1fb962e7f072d4c036e831bdf01f06931d91bc2fcc3a91 352 bytes 102400 cycles
# eosio <= eosio::newaccount {"creator":"eosio","name":"eosio.token","owner":{"threshold":1,"keys":[{"key":"EOS6nbWS7ZReiPMdMABoE...
複製代碼
接下來,咱們使用這個帳戶部署eosio.token智能合約,一樣經過上面學習到的方式:指定路徑,指定加密帳戶{-p eosio.token}:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos set contract eosio.token build/contracts/eosio.token -p eosio.token
Reading WAST/WASM from build/contracts/eosio.token/eosio.token.wast...
Assembling WASM...
Publishing contract...
executed transaction: a95dc5be83d202730f798d2ce75df42eff518a3ed8f82e4c5b0f3c8881d75d70 8024 bytes 2200576 cycles
# eosio <= eosio::setcode {"account":"eosio.token","vmtype":0,"vmversion":0,"code":"0061736d01000000018a011660067f7e7f7f7f7f00...
# eosio <= eosio::setabi {"account":"eosio.token","abi":{"types":[],"structs":[{"name":"transfer","base":"","fields":[{"name"...
複製代碼
建立EOS的代幣
就像以太坊token那樣,咱們在EOS上能夠更加方便的建立一個基於EOS的代幣。首先,去token合約中的頭文件eosio.token.hpp,查看一下token相關的接口都有哪些,其中有一個create函數,咱們正是將要使用這個函數來建立token,因此咱們能夠留意一下它的參數都包括哪些。
void create(account_name issuer, // 發行人,有權限調用下面的freeze、recall以及whitelist函數。
asset maximum_supply, // 最大發行量,注意單位,這個單位就是該token的名字,symbol。
uint8_t issuer_can_freeze,
uint8_t issuer_can_recall,
uint8_t issuer_can_whitelist );
複製代碼
咱們能夠經過命令行來調用該create函數:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action eosio.token create '[ "eosio", "1000000000.0000 EOS", 0, 0, 0]' -p eosio.token
executed transaction: 5b3f974030f7311a0c65cc6d4123be18f7435d0b06b615d939ff81255059aaf8 248 bytes 104448 cycles
# eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"1000000000.0000 EOS","can_freeze":0,"can_recall":0,"can_whitelis...
複製代碼
直接按參數順序傳入值比較方便,若是你須要更加準確的傳值,能夠將以上動做的參數內容改寫爲「Key,Value」的形式改造一下,會比較冗餘。
思考,這個token沒有名字麼?就像EOS之於ethereum那樣。
複製代碼
代幣發放
咱們已經發行了一種代幣EOS,下面咱們能夠將這個代幣EOS發放給帳戶user(咱們上面建立的)。繼續查看那個eosio.token.hpp頭文件中關於issue(發放)操做的參數。
void issue( account_name to, asset quantity, string memo );// memo:備註,通常能夠不填寫。
複製代碼
而後,咱們繼續使用命令行工具cleos來push action到智能合約eosio.token中這個issue函數中:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action eosio.token issue '["user","100.0000 EOS", "memo"]' -p eosio
executed transaction: b93b9e709ce4720dd5e89789c14a785790605ec093194710736cf30bb195fae9 256 bytes 124928 cycles
# eosio.token <= eosio.token::issue {"to":"user","quantity":"100.0000 EOS","memo":"memo"}
>> issue
# eosio.token <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 EOS","memo":"memo"}
>> transfer
# eosio <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 EOS","memo":"memo"}
# user <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 EOS","memo":"memo"}
複製代碼
注意,在命令行結尾處,咱們仍要使用帳戶來簽名受權,這裏是用戶eosio(由於上面eosio是發行者,全都發行到它的兜裏啦,因此要從它兜裏取錢)。
咱們先來看issue函數的實現源碼:
void token::issue( account_name to, asset quantity, string memo )
{
print( "issue" );// >> issue
auto sym = quantity.symbol.name();// EOS
stats statstable( _self, sym );
const auto& st = statstable.get( sym );// 經過代幣id獲取代幣對象,包含代幣相關信息
require_auth( st.issuer );// 檢查發行人權限,是否有足夠
eosio_assert( quantity.is_valid(), "invalid quantity" );// 轉移金額是否有效
eosio_assert( quantity.amount > 0, "must issue positive quantity" );// 轉移金額必須是正數
statstable.modify( st, 0, [&]( auto& s ) {
s.supply.amount += quantity.amount;
});
add_balance( st.issuer, quantity, st, st.issuer );// TODO: 轉帳金額竟然要加到發行者的餘額中?等於發行者轉帳完畢仍舊是原發行量?
if( to != st.issuer )// 別發給本身
{
//這是action的內部函數,執行轉帳的意思
dispatch_inline( permission_level{st.issuer,N(active)}, _self, N(transfer), &token::transfer, { st.issuer, to, quantity, memo } );
}
}
複製代碼
TODO: add_balance函數源碼要好好研究
複製代碼
執行發放動做,經過日誌能夠看到包括了幾個步驟:
實際上,eosio.token能夠直接修改帳戶餘額而不使用「內聯調用transfer」。可是這種狀況下,eosio.token智能合約會要求咱們的token必須有全部的帳戶餘額,經過計算引用過他們的全部交易動做的總和。它還須要發送者和接收者的存款狀況,以支持他們能夠自動處理充值和提現。
若是你想看到廣播出去的真實交易的狀況,可使用-d -j選項來表達「不要廣播」以及「以json格式返回交易」:
「不要廣播」的意思是這條動做無效,只是用來作測試的。(這與上面的廣播出去的「真實交易」不一樣)
複製代碼
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos wallet unlock
password: Unlocked: default
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action eosio.token issue '["user","100.0000 EOS", "memo"]' -p eosio -d -j
{
"expiration": "2018-04-24T02:13:26",
"region": 0,
"ref_block_num": 26069,
"ref_block_prefix": 2157111066,
"max_net_usage_words": 0,
"max_kcpu_usage": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [{
"account": "eosio.token",
"name": "issue",
"authorization": [{
"actor": "eosio",
"permission": "active"
}
],
"data": "00000000007015d640420f000000000004454f5300000000046d656d6f"
}
],
"signatures": [
"EOSKjnxnDwjjyZaZPSeQnmMFYjpgBWRzby5YFqVqZEn6uA3TUhUo4yWmfQhXdxNykgsSVvAwGnkGUyUK7jcJt5qNg8xhstRqy"
],
"context_free_data": []
複製代碼
因爲次日來上班,咱們的錢包已經被鎖定,因此要先解鎖,而後再調用上面的命令。能夠看到輸出的內容不少,是按照咱們的要求「以json格式返回交易」,「不要廣播」。咱們能夠看到交易的具體信息,包含了不少屬性:有效期、地區、引用區塊號、引用區塊前綴、最大網絡使用詞數、最大cpu使用,延遲秒數、上下文的自由動做(爲空說明上下文中沒有動做),動做內容(包括動做執行人,動做名稱,受權信息包括行動者以及權限狀態、數據串、簽名、上下文自由數據(爲空,上下文無內容)。
代幣交易
如今user帳戶已經存在100個EOS代幣了,咱們使用上面創建的另外一個帳戶tester,用來測試代幣交易:從user帳戶中轉出一部分EOS到tester帳戶。
一樣的,咱們仍是先來看一下源碼中設計的transfer函數的參數列表:
void transfer( account_name from,
account_name to,
asset quantity,
string memo );
複製代碼
很簡單,下面使用cleos來調用:
這裏咱們能夠嘗試使用user帳戶自己來簽名動做。
複製代碼
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action eosio.token transfer '["user","tester","25.0000 EOS", "m"]' -p user
executed transaction: a31e56b49837e53fb1e7d55aa7aba934dc751938241488183e54ad52dc7804fe 256 bytes 110592 cycles
# eosio.token <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 EOS","memo":"m"}
>> transfer
# user <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 EOS","memo":"m"}
# tester <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 EOS","memo":"m"}
複製代碼
成功!仍舊是eosio.token合約發起一個transfer交易動做,輸出「>> transfer」,而後內聯調用帳戶餘額的操做分別操做發送者和接收者。
注意:咱們也能夠不使用push action的方式來交易,而是直接使用cleos的自命令transfer便可,後面的命令參數與上面的差很少。可是發行和發放token仍舊須要使用合約的push action來操做。我理解的是因爲交易比較經常使用且可不依賴某一個合約,因此被封裝在了根命令中,而其餘與合約相關的仍舊須要使用push action的方式。
複製代碼
查看餘額
咱們須要總體研究一下cleos的全部子命令,列舉的方式比較枯燥,這裏不展開,只是使用到哪裏就展現哪裏。咱們上面進行了代幣發放和代幣交易,此時兩個測試帳戶user和tester的EOS餘額都發生了變化。下面咱們要利用cleos查詢一下這兩個帳戶的代幣EOS的餘額情況:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos get currency balance eosio.token user EOS
75.0000 EOS
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos get currency balance eosio.token tester EOS
25.0000 EOS
複製代碼
能夠看到user帳戶的代幣餘額爲75,而tester帳戶的代幣餘額是25。這與咱們上面的交易動做是可匹配的。
重要發現
上面咱們留的TODO,由於我搞不懂爲何要先給代幣發行人餘額增長轉帳額,再轉帳,這個操做是怎麼來的。我在上面查詢餘額的命令發現:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos get currency balance eosio.token eosio EOS 0.0000 EOS
發行人的餘額是0!
複製代碼
因此這樣咱們就搞明白了爲何每次發放代幣的時候要先add_balance而後再sub_balance了。
咱們建立一個帳戶user1,而後用該帳戶部署合約exchange:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos create account eosio user1 EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3 EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3
executed transaction: b2c4a1acb831a4bd12d1686c271ca49b0ed85efe5a31f5c24abe212bc44d4009 352 bytes 102400 cycles
# eosio <= eosio::newaccount {"creator":"eosio","name":"user1","owner":{"threshold":1,"keys":[{"key":"EOS6nbWS7ZReiPMdMABoEmVBYan...
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos set contract user1 build/contracts/exchange -p user1
Reading WAST/WASM from build/contracts/exchange/exchange.wast...
Assembling WASM...
Publishing contract...
executed transaction: 4e2bc4496ef25f187bb90da0f0b3398d5c1970ba62b062ab34160d09975c0591 34056 bytes 2200576 cycles
# eosio <= eosio::setcode {"account":"user1","vmtype":0,"vmversion":0,"code":"0061736d0100000001cd023160067f7e7f7f7f7f0060037f...
# eosio <= eosio::setabi {"account":"user1","abi":{"types":[{"new_type_name":"account_name","type":"name"}],"structs":[{"name...
複製代碼
exchange合約能作的事情有不少,包括建立和交易currency(電子貨幣)。它能作的事能夠參考源碼位置contract/exchange/*。
msig的意思是multi-signature,多重簽名的意思。這個合約是能夠支持多方對同一筆交易進行異步簽名,它是一個對用戶友好的支持多方贊成的異步進行提案、批覆以及最終發佈交易的合約。
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos create account eosio user2 EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3 EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3
executed transaction: b156f5aa2fec6ca8ed6e55ba2be2e403cef0dd26f3c022a51e74f9ccf348fef2 352 bytes 102400 cycles
# eosio <= eosio::newaccount {"creator":"eosio","name":"user2","owner":{"threshold":1,"keys":[{"key":"EOS6nbWS7ZReiPMdMABoEmVBYan...
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos set contract user2 build/contracts/eosio.msig -p user2
Reading WAST/WASM from build/contracts/eosio.msig/eosio.msig.wast...
Assembling WASM...
Publishing contract...
executed transaction: 66c64a3f55f011d40483c627ea43fd62f303fd96f9851141df67087867d4e02f 7320 bytes 2200576 cycles
# eosio <= eosio::setcode {"account":"user2","vmtype":0,"vmversion":0,"code":"0061736d01000000016b1260017f0060047f7e7e7f006004...
# eosio <= eosio::setabi {"account":"user2","abi":{"types":[{"new_type_name":"account_name","type":"name"},{"new_type_name":"...
複製代碼
部署方式與前面沒有大區別,這裏使用的是帳戶user2,它能作的事能夠參考源碼位置contract/eosio.msig/*。
咱們儘可能使用與合約名字相同的帳戶名字來發布合約,這樣能夠有效記錄該帳戶的功能,可快速與其餘普通用戶作出區分。
複製代碼
以上咱們提取了eos.io合約中的三個,進行了部署、學習與操做演練,下面咱們將嘗試開發本身的基於eos的智能合約。
eosiocpp:智能合約的引導程序工具。
複製代碼
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos/contracts$ eosiocpp -n testcontract
created testcontract from skeleton
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos/contracts$ ls
asserter CMakeLists.txt dice eosiolib eosio.system exchange identity libc++ musl proxy skeleton stltest test_api_db test_api_multi_index test.inline
bancor currency eosio.bios eosio.msig eosio.token hello infinite multi_index_test noop simple.token social test_api test_api_mem testcontract tic_tac_toe
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos/contracts$ cd testcontract/
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos/contracts/testcontract$ tree
.
├── testcontract.abi
├── testcontract.cpp
└── testcontract.hpp
0 directories, 3 files
複製代碼
咱們在源碼contracts目錄下執行了以上命令之後,就會獲得一個空的智能合約的開發框架,其中包含了abi,cpp以及hpp三個文件。
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <testcontract.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"
複製代碼
wast文件生成方式:
eosiocpp -o ${contract}.wast ${contract}.cpp
複製代碼
abi文件,Application Binary Interface,應用程序的二進制接口,這在以太坊是相同的概念,請參照《【精解】開發一個智能合約》。
abi是一個json格式的,用來描述智能合約如何在action和二進制程序中進行轉變的方法,也用來描述數據庫狀態。有了abi來描述你的智能合約,開發者和用戶均可以經過JSON無縫地與合約進行交互。
複製代碼
abi文件生成時源文件語法包括:
abi文件生成方式:
eosiocpp -g ${contract}.abi ${contract}.hpp
複製代碼
abi文件生成之後,咱們能夠找一個打開看一下,裏面包含的內容不少,有各類屬性,數據,方法功能的描述。
部署學習和操做咱們都已經學會,那麼如今要開發一個helloworld智能合約,首先在eos源碼中找到一個位置(由於要include相關庫),創建一個目錄hello,在裏面建立一個 1,hello.cpp,
//
// Created by liuwenbin on 18-4-24.
//
#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)) // CLion代碼檢查,這裏會報錯,先不理會
複製代碼
2,編譯wast
在hello.cpp路徑下執行:
eosiocpp -o hello.wast hello.cpp
複製代碼
會有不少警告出來,不要理會,查看一下,當前目錄應該已經有了hello.wast。
3,編譯abi 而後繼續在hello.cpp路徑下執行:
eosiocpp -g hello.abi hello.cpp
Generated hello.abi ...
複製代碼
查看當前目錄,又生成了一個hello.abi文件。
咱們的合約開發就完成了。下面的操做與上一章節的操做是相似的。 咱們先建立一個帳戶hello.a,而後用這個帳戶部署合約hello。部署完成之後,咱們能夠進行合約調用:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action hello.a hi '["Edward"]' -p hello.a
executed transaction: 8f4b9a4fe271a7981ee40348179dcdede025ebece10c38d0a4d5a0aa5d41ffac 232 bytes 102400 cycles
# hello.a <= hello.a::hi {"user":"Edward"}
>> Hello, Edward
複製代碼
經過帳戶hello.a 調用hi函數,傳入參數'[用戶名]',使用hello.a簽名該action。執行之後,會在日誌中打印出「>> ...」。
以上操做都是測試用帳戶,他們都是基於相同的公鑰建立的,咱們如今來查看下目前該公鑰有多少個帳戶:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos get accounts EOS6nbWS7ZReiPMdMABoEmVBYanyTMb3GYRQGsTRMCYx9vijWoaS3
{
"account_names": [
"eosio.token",
"hello.a",
"tester",
"tokener",
"user",
"user1",
"user2"
]
}
複製代碼
目前咱們的hello合約是不限制hi參數的,也就是說其實咱們是沒有「Edward」這個簽名人的,也就是說這個參數中不管是否傳入帳戶名,均可以輸出。
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action hello.a hi '["Edward"]' -p user
executed transaction: fc38858f89e7dfdd9dcff8db8626545f387960cee63f80e8352b7f7596a986a7 232 bytes 102400 cycles
# hello.a <= hello.a::hi {"user":"Edward"}
>> Hello, liuwenbin
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action hello.a hi '["Edward"]' -p user1
executed transaction: f81acea4f1ef9207afc5827608fd255bda063b0a0aeaa214f6bc2742ce480a34 232 bytes 102400 cycles
# hello.a <= hello.a::hi {"user":"Edward"}
>> Hello, liuwenbin
複製代碼
另外,咱們可使用user,也可使用user1來簽名hello.a部署的咱們的hello智能合約,這顯然是不合理的。
咱們指望智能合約hi函數的參數必須是有效帳戶名,同時只有該帳戶擁有當前action的簽名權。因此,咱們要修改hello.cpp文件。
/// @abi action
void hi(account_name user) {
require_auth(user);// 只有該user帳戶有權簽名當前action
print("Hello, ", name{user});
}
複製代碼
而後重複以上編譯和部署的操做。再傳入非有效帳戶名時,或者用其餘帳戶簽名的時候就會報錯:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action hello.a hi '["Edward"]' -p hello.a
Error 3120001: Invalid name
Name should be less than 13 characters and only contains the following symbol .12345abcdefghijklmnopqrstuvwxyz
Error Details:
Name not properly normalized (name: Edward, normalized: .dward)
'["Edward"]' is invalid args for action 'hi' code 'hello.a'
複製代碼
報錯Edward不是有效參數。
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action hello.a hi '["user"]' -p hello.a
Error 3030001: 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.
Error Details:
missing authority of user
複製代碼
hello.a沒法給帳戶user簽名。
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos push action hello.a hi '["tester"]' -p tester
executed transaction: be235f5fbd6173a4acac23b8faf4cd3de9d73721b839d3092b2db23eaa3ef51d 232 bytes 102400 cycles
# hello.a <= hello.a::hi {"user":"tester"}
>> Hello, tester
複製代碼
成功!咱們傳入有效用戶名tester,而且用tester帳戶自己去簽名當前action,最終成功輸出告終果。
思考,咱們可發現其餘eos的智能合約都是符合以上指望的,這是什麼規則?
複製代碼
這是Ricardian Contract,意思是該合約符合李嘉圖等價原則。
李嘉圖等價的合約,會指定其合法綁定者來關聯該合約的每個action。
複製代碼
關於帳戶和合約關係的一些心得:
liuwenbin@liuwenbin-H81M-DS2:~/work/CLionProjects/github.com/eos$ cleos get code hello.a
code hash: 7c6a300874835ad928de4f30712023758157bd50cb423ab039443f56a84167ff
複製代碼
咱們編寫一個智能合約,須要在本地私有鏈上進行調試,經過之後再上公有鏈。
官方聲稱這叫作Caveman debugging(瞬間不想再愛了,照以太坊差遠了),什麼意思呢?就是eosio::print能夠輸出log來調試,EOS目前沒辦法進行代碼斷點調試。
複製代碼
本文介紹了EOS智能合約的內容,這部份內容的確比以太坊的要少不少,由於以太坊上面成熟的開發框架更多一些,功能也更強大(例如能夠斷點調試,本地虛機等),而EOS比較新,在這方面沒有那麼多工具可選。可是EOS的智能合約比起使用Solidity的以太坊合約來說,仍是很是方便的,不少想法也比較新穎。本文主要從準備、學習、實戰和調試這幾個步驟進行按部就班地瞭解與學習。這期間,咱們學習了bios、token、exchange、msig以及本身實現了helloworld合約,掌握了錢包、帳戶、簽名權限等不少基本功能,熟悉了cleos和eosiocpp命令的使用,掌握了智能合約的編寫、編譯、部署以及調試的知識。
EOS官方文檔
圓方圓學院聚集大批區塊鏈名師,打造精品的區塊鏈技術課程。 在各大平臺都長期有優質免費公開課,歡迎報名收看。
公開課地址:ke.qq.com/course/3451…