EOSForce.io主網智能合約教程(上)區塊鏈
EOSForce.io主網智能合約教程(上)
本文為EOSForce.io主網智能合約教程系列文章,
全教程目錄如下。
一、EOSForce主網智能合約概覽:
需要的知識
通訊模型
ABI宏及應用
多索引數據庫編程接口
命名規范
C /C編程接口
二、EOSForce主網智能合約教程:
智能合約起步
EOSFORCEIO Token 合約介紹
多索引表示例
在 EOSFORCEIO 上創建一個 Token
合約中隨機化
升級系統合約
怎樣寫 ABI 文件
由于文章篇幅有限,EOSForce主網智能合約教程4-7章會于接下來的推送中展示。
全文詳細內容也可登陸官網查看:eosforce.github.io/Documentation/#/zh-cn/contract
智能合約起步
第一步:安裝合約開發工具集(CDT)
EOSFORCEIO 合約開發工具集 - 按照安裝指導繼續。eosiocpp工具包含在工具集中,該工具編譯編譯合約并且生成 ABI 文件。
首先克隆代碼
git clone --recursive https://github.com/eosio/eosio.cdt cd eosio.cdt
然后運行 build.sh 并且提供打算部署 EOSFORCEIO 區塊鏈的核心符號。build.sh 將自動安裝所需要的依賴。
./build.sh
最后,安裝構建,安裝將把核心安裝到 /usr/local/eosio.cdt, 頂層工具(編譯器,鏈接器等等)的符號鏈接安裝到 /usr/local/bin
$ sudo ./install.sh
第二步:啟動節點
如果使用 docker 并且 容器不在運行,執行下面命令。
docker start eosio
如果在本地運行 nodeos , 用下列單個命令可以啟動單節點區塊鏈。
$ nodeos -e -p eosio --plugin eosio::chain_api_plugin \ --plugin eosio::history_api_plugin
這個命令設置了許多標志并且裝載了本教程其余部分所需要的可選插件。假如一切順利,妹0.5秒你將看到一條區塊生成消息。
在 docker 上,
docker logs --tail 25 eosio ... 3165501ms thread-0 producer_plugin.cpp:944 produce_block ] Produced block 00000a4c898956e0... #2636 @ 2018-05-25T16:52:45.500 signed by eosio [trxs: 0, lib: 2635, confirmed: 0] 3166004ms thread-0 producer_plugin.cpp:944 produce_block ] Produced block 00000a4d2d4a5893... #2637 @ 2018-05-25T16:52:46.000 signed by eosio [trxs: 0, lib: 2636, confirmed: 0] ...
第三步:創建錢包
錢包是私鑰倉庫,必須用私鑰來認證區塊鏈上 actions。這些私鑰加密存儲在磁盤上,使用密碼來保護。密碼應該存儲在一個安全的密碼管理器中或者記下來。
$ cleos wallet create --to-console Creating wallet: default Save password to use in the future to unlock this wallet. Without password imported keys will not be retrievable. "PW5JuBXoXJ8JHiCTXf...."
錢包經過一段時間將自動鎖定,通過下列命令解鎖
$ cleos wallet unlock
password:
為了安全目的,當不適用錢包的時候,一般最后讓其鎖定。為了鎖定錢包而不關閉 nodeos , 可以執行下列命令
cleos wallet lock Locked: default
本文其余部分需要錢包處于解鎖狀態。
裝載教程 Key
上述步驟發起的私鏈伴隨創建了一堆初始秘鑰,必須導入錢包(參見下文)
$ cleos wallet import --private-key 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 imported private key for: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
第四部:裝載 BIOS 合約
現在我們有一個錢包已經裝載了 eosio 賬戶的私鑰,我們部署一個缺省的系統合約。為了開發的目的,我們使用缺省的 eosio.bios 合約。 這個合約使你能直接控制其它賬戶的資源分配并且能訪問其它有特權的 API 調用。在公鏈上,這個合約將管理 tokens 抵押及解押以為合約申請CPU、網絡、內存。
eosio.bios 合約在 EOSFORCEIO 源代碼 contracts/eosio.bios 目錄下。下面的命令序列假設從 EOSFORCEIO 源代碼根目錄執行,但是你可以指定完整路徑 ${EOSIO_SOURCE}/build/contracts/eosio.bios 從任何地方執行。
如果你用 docker, 命令是:
$ cleos set contract eosio contracts/eosio.bios -p eosio@active Reading WAST... Assembling WASM... Publishing contract... executed transaction: 414cf0dc7740d22474992779b2416b0eabdbc91522c16521307dd682051af083 4068 bytes 10000 cycles # eosio <= eosio::setcode {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001ab011960037f7e7f0060057f7e7e7e... # eosio <= eosio::setabi {"account":"eosio","abi":{"types":[],"structs":[{"name":"set_account_limits","base":"","fields":[{"n...
如果從源代碼構建,命令是:
$ cleos set contract eosio build/contracts/eosio.bios -p eosio@active Reading WAST... Assembling WASM... Publishing contract... executed transaction: 414cf0dc7740d22474992779b2416b0eabdbc91522c16521307dd682051af083 4068 bytes 10000 cycles # eosio <= eosio::setcode {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001ab011960037f7e7f0060057f7e7e7e... # eosio <= eosio::setabi {"account":"eosio","abi":{"types":[],"structs":[{"name":"set_account_limits","base":"","fields":[{"n...
命令序列的結果是 cleos 生成包含兩個 actions 的 transaction, eosio::setcode 和 eosio::setabi。
code 定義了合約怎樣運行, abi 描述了參數怎樣在二進制和 json 表示間轉換。雖然 abi 技術上是可選的, DOSFORCEIO 所有工具都依賴它為了方便使用。
任何時候執行一個 transaction ,將看到如下輸出:
executed transaction: 414cf0dc7740d22474992779b2416b0eabdbc91522c16521307dd682051af083 4068 bytes 10000 cycles # eosio <= eosio::setcode {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001ab011960037f7e7f0060057f7e7e7e... # eosio <= eosio::setabi {"account":"eosio","abi":{"types":[],"structs":[{"name":"set_account_limits","base":"","fields":[{"n...
可以這樣解讀:eosio 定義的 action setcode 被 eosio 合約用參數 {args...} 執行。
# ${executor} <= ${contract}:${action} ${args...} > console output from this execution, if any
后面我們將會看到 actions 可以被多個合約處理。
這個調用的最后一個參數是 -p eosio@active 。 這告訴 cleos 用 eosio 賬戶的 active 授權來簽署 這個 action ,比如說用之前為 eosio 賬戶導入的私鑰 來簽署這個 action 。
第五步:創建賬戶
現在我們已經設置了基本的系統合約,我們可以開始創建我們自己的賬戶。我們將創建兩個賬戶 user 和 tester, 我們需要為每個賬戶關聯一個私鑰。 這個例子中,兩個賬戶將使用同一個私鑰。
我們首先為賬戶生成私鑰。
$ cleos create key --to-console Private key: 5Jmsawgsp1tQ3GD6JyGCwy1dcvqKZgX6ugMVMdjirx85iv5VyPR Public key: EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
然后我們將使用導入錢包:
$ cleos wallet import --private-key 5Jmsawgsp1tQ3GD6JyGCwy1dcvqKZgX6ugMVMdjirx85iv5VyPR imported private key for: EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4
主要用 cleos 實際生成的私鑰,不要用例子中顯示的。
私鑰不會自動加入錢包,因此忽略這步將導致失去對賬戶的控制。
創建兩個用戶賬戶
接下來我們創建兩個賬戶, user 和 tester, 用上面創建和導入的私鑰。
$ cleos create account eosio user EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 executed transaction: 8aedb926cc1ca31642ada8daf4350833c95cbe98b869230f44da76d70f6d6242 364 bytes 1000 cycles # eosio <= eosio::newaccount {"creator":"eosio","name":"user","owner":{"threshold":1,"keys":[{"key":"EOS7ijWCBmoXBi3CgtK7DJxentZZ... $ cleos create account eosio tester EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 executed transaction: 414cf0dc7740d22474992779b2416b0eabdbc91522c16521307dd682051af083 366 bytes 1000 cycles # eosio <= eosio::newaccount {"creator":"eosio","name":"tester","owner":{"threshold":1,"keys":[{"key":"EOS7ijWCBmoXBi3CgtK7DJxentZZ...
注意:create account 命令需要兩個密鑰,一個是 OwnerKey (在生產環境上應該高度保密),另一個是 ActiveKey。這個例子中,使用了同一把密鑰。
因為我們使用了 eosio::history_api_plugin , 因此我們可以查詢我們的密鑰控制的所有賬戶:
$ cleos get accounts EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 { "account_names": [ "tester", "user" ] }
EOSFORCEIO Token 合約介紹
Eosio.token, Exchange, 及 Eosio.msig 合約
在這個階段區塊鏈并不能做很多,因此讓我們部署 eosio.token 合約。這個合約使能創建許多不同的 tokens, 都運行在相同的合約上但是潛在被不同的用戶管理。
在我們部署 token 合約前,先要將要部署上去的賬戶。
$ cleos create account eosio eosio.token \ EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 \ ...
然后我們可以部署合約,合約在 ${EOSIO_SOURCE}/build/contracts/eosio.token 目錄下。
$ cleos set contract eosio.token build/contracts/eosio.token -p eosio.token@active Reading WAST... Assembling WASM... Publishing contract... executed transaction: 528bdbce1181dc5fd72a24e4181e6587dace8ab43b2d7ac9b22b2017992a07ad 8708 bytes 10000 cycles # eosio <= eosio::setcode {"account":"eosio.token","vmtype":0,"vmversion":0,"code":"0061736d0100000001ce011d60067f7e7f7f7f7f00... # eosio <= eosio::setabi {"account":"eosio.token","abi":{"types":[],"structs":[{"name":"transfer","base":"","fields":[{"name"...
創建 Currency Token
可以查看 eosio.token 合約的接口,定義在 contracts/eosio.token/eosio.token.hpp:
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 );
為了創建新的 token, 我們必須調用 create(...) action 用合適的參數。這個命令將使用最大供給的符號來識別這個 token 以區別于其它 tokens。 發行人有權調用發行與或執行其它動作比如凍結,撤銷,及白名單擁有者。
調用這個命令的簡潔方法,用位置參數:
$ cleos push action eosio.token create '[ "eosio", "1000000000.0000 SYS"]' \ -p eosio.token@active executed transaction: 0e49a421f6e75f4c5e09dd738a02d3f51bd18a0cf31894f68d335cd70d9c0e12 120 bytes 1000 cycles # eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"1000000000.0000 SYS"}
可選的調用這個命令的更詳細的方式,使用命名參數:
$ cleos push action eosio.token create \ '{"issuer":"eosio", "maximum_supply":"1000000000.0000 SYS"}' \ -p eosio.token@active executed transaction: 0e49a421f6e75f4c5e09dd738a02d3f51bd18a0cf31894f68d335cd70d9c0e12 120 bytes 1000 cycles # eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"1000000000.0000 SYS"}
這個命令創建一個新的 token SYS, 有四位小數點精度及最大 1000000000.0000 SYS 供應量。
為了創建這個 token, 我們需要 eosio.token 合約的權限因為他 “擁有”符號名字空間(比如“SYS”)。這個合約的為了版本可能允許其它方自動購買符號名字。 因為這個原因我們必須傳遞 -p eosio.token@active 以授權這個調用。
授予 tokens 給 “user”賬戶
現在已經創建了 token, 發行人可以授予新 token 給之前創建的賬戶 user。如果你沒有創建 user 賬戶,參見之前的指令。
我們將使用位置參數調用方式。
$ cleos push action eosio.token issue '[ "user", "100.0000 SYS", "memo" ]' \ -p eosio@active executed transaction: 822a607a9196112831ecc2dc14ffb1722634f1749f3ac18b73ffacd41160b019 268 bytes 1000 cycles # eosio.token <= eosio.token::issue {"to":"user","quantity":"100.0000 SYS","memo":"memo"} >> issue # eosio.token <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 SYS","memo":"memo"} >> transfer # eosio <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 SYS","memo":"memo"} # user <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 SYS","memo":"memo"}
這次輸出包含幾個不同的 actions: 一個 issue 及三個 transfers。雖然我們簽署的唯一 action 是 issue, issue action 執行一次“在線轉賬”并且 “在線轉賬”通知發送方賬戶和接收方賬戶。輸出指示了調用的所有 action 處理器,調用的順序,action 是否有其它輸出。
技術上, eosio.token 合約本可以忽略在線轉賬而直接修改余額。然而,這個例子,eosio.token 合約遵循 token 慣例,要求所有賬戶余額可以可以從 transfer action 總和中推導。也要求資金的發送方和接收方被通知以便他們能自動化處理存款和取款。
如果你想看廣播的實際的 transaction, 可以用 -d -j 選項指示 “不要廣播” 及 “以 json 格式返回 transaction”。
$ cleos push action eosio.token issue '["user", "100.0000 SYS", "memo"]' -p eosio@active -d -j { "expiration": "2018-05-25T19:02:58", "ref_block_num": 18200, "ref_block_prefix": 614206268, "max_net_usage_words": 0, "max_cpu_usage_ms": 0, "delay_sec": 0, "context_free_actions": [], "actions": [{ "account": "eosio.token", "name": "issue", "authorization": [{ "actor": "eosio", "permission": "active" } ], "data": "00000000007015d640420f00000000000453595300000000046d656d6f" } ], "transaction_extensions": [], "signatures": [ "SIG_K1_Khyk1GsxWCx4axqYMF2AREDvaZtZdFaQifNPkR9DomR7toJ4sGua7pMBNq2osV5TY8rcGNcgNwn1eFe3noAXsoUA26HNDJ" ], "context_free_data": [] }
轉賬 Tokens 給賬戶 “Tester”
現在賬戶 user 已經有 tokens, 我們將轉一些給賬戶 tester。我們指示 user 授權這次 action 用權限參數 -p user@active 。
$ cleos push action eosio.token transfer \ '[ "user", "tester", "25.0000 SYS", "m" ]' -p user@active executed transaction: 06d0a99652c11637230d08a207520bf38066b8817ef7cafaab2f0344aafd7018 268 bytes 1000 cycles # eosio.token <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 SYS","memo":"m"} >> transfer # user <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 SYS","memo":"m"} # tester <= eosio.token::transfer {"from":"user","to":"tester","quantity":"25.0000 SYS","memo":"m"}
部署 Exchange 合約
類似于上面的例子,我們能部署 exchange 合約。exchange 合約提供了創建及交易貨幣的能力。假設從 EOSFORCEIO 源碼的根目錄運行。
$ cleos create account eosio exchange \ EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 \ EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 executed transaction: 4d38de16631a2dc698f1d433f7eb30982d855219e7c7314a888efbbba04e571c 364 bytes 1000 cycles # eosio <= eosio::newaccount {"creator":"eosio","name":"exchange","owner":{"threshold":1,"keys":[{"key":"EOS7ijWCBmoXBi3CgtK7DJxe... $ cleos set contract exchange build/contracts/exchange -p exchange@active Reading WAST/WASM from build/contracts/exchange/exchange.wasm... Using already assembled WASM... Publishing contract... executed transaction: 503dddec456ae301ef467c6a05bc6bf61e1ea21ab911ef6cc6e0750001b675c8 33888 bytes 5841 us # eosio <= eosio::setcode {"account":"exchange","vmtype":0,"vmversion":0,"code":"0061736d0100000001bb022f60067f7e7f7f7f7f00600... # eosio <= eosio::setabi {"account":"exchange","abi":"0e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d650e0...
部署 Eosio.msig 合約
eosio.msig 合約允許多方異步簽署單個 transaction。EOSFORCEIO 在基礎層面提供多重簽名支持,但是它需要一個同步的旁通道可以搬運數據及簽署數據。 Eosio.msig 是一種更加友好的方式,可以異步建議,批準,及最終出版多方的同意。
采用下述命令部署 Eosio.msig 合約。
$ cleos create account eosio eosio.msig \ EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 \ EOS7ijWCBmoXBi3CgtK7DJxentZZeTkeUnaSDvyro9dq7Sd1C3dC4 # eosio <= eosio::newaccount {"creator":"eosio","name":"eosio.msig","owner":{"threshold":1,"keys":[{"key":"EOS7ijWCBmoXBi3CgtK7DJ... $ cleos set contract eosio.msig build/contracts/eosio.msig -p eosio.msig@active Reading WAST/WASM from build/contracts/eosio.msig/eosio.msig.wasm... Using already assembled WASM... Publishing contract... executed transaction: 3433c434bdef42ba2150d5df46c17e0258f20b7836a057911faa2daa66262338 8864 bytes 1319 us # eosio <= eosio::setcode {"account":"eosio.msig","vmtype":0,"vmversion":0,"code":"0061736d010000000198011760017f0060047f7e7e7... # eosio <= eosio::setabi {"account":"eosio.msig","abi":"0e656f73696f3a3a6162692f312e30030c6163636f756e745f6e616d65046e616d650...
多索引表示例
描述
在這篇教程中我們將瀏覽在智能合約中創建和使用多索引表的步驟。
注釋
多索引表是一種在內存中緩存狀態和數據為了快速訪問的方法。多索引表支持創建,讀,更新和刪除(CRUD)操作,有些區塊鏈不支持(區塊鏈僅支持創建和讀)。
多索引表提供了一種快速訪問的數據存儲,是一種實用的在智能合約中存儲數據的方式。區塊鏈記錄 transactions, 但你應該用多索引表存儲應用數據。
它們是多索引表,因為它們支持在數據上使用多個索引。主鍵索引類型必須是 uint64_t 并且必須唯一,但是其它二級索引可以有重復。最多可以建16個索引并且 字段類型可以是 uint64_t, uint128_t, uint256_t, double 和 long double 。
如果你想在字符串上建索引,你需要將其轉換成整數類型,并且將結果存儲在一個字段上然后在其上建索引。
1、創建一個 struct
創建一個 struct , 它將被存在多索引表中,并且在想建索引的字段上定義 getters 。
記住這些 getters 其中之一必須名為 "primary_key()", 如果沒有,編譯器 (eosio-cpp)將生成一個錯誤。它無法發現字段用作主鍵。
如果你想建多個索引(最多允許16個),然后為想建索引的字段定義一個 getter, 這次名字不那么重要,因為你將傳遞 getter 名字進入 typedef 。
C struct [[eosio::table]] mystruct { uint64_t key; uint64_t secondid; std::string name; std::string account;
uint64_t primary_key() const { return key; } // getter for primary key uint64_t by_id() const {return secondid; } // getter for additional key };
這兒要注意兩件事:
屬性 [[eosio::table]] 對 ABI 生成器是需要的,eosio-cpp, 識別出你想通過 ABI 暴露這個表并且使它在智能合約外可見。
2.struct 名稱少于12個字符并且都是小寫。
typedef 多索引表并且定義索引
定義將使用 mystruct 的多索引表,告訴它索引什么以及怎樣獲取被索引的數據。主鍵將被自動創建,因此使用上述 struct, 如果我要一個只有主鍵的多索引表 將定義如下:
C
typedef eosio::multi_index datastore;
這定義了多索引出入表名 N(mystruct) 和 struct 名 "mystruct"。N(mystruct) 執行一次編譯轉換從 struct 名到 uint64_t 并且這個 uint64_t 被用來識別屬于這個多索引表的數據。
要增加二級索引,使用 indexed_by 模板做參數,因此定義變為:
C
typedef eosio::multi_index<N(mystruct), mystruct, indexed_by<N(secondid), const_mem_fun>> datastore;
這兒
indexed_by<N(secondid), const_mem_fun>
參數
字段名轉換為整數,N(secondid)
用戶自定義鍵提取器, const_mem_fun
如果想建3個索引
C
struct [[eosio::table]] mystruct { uint64_t key; uint64_t secondid; uint64_t anotherid; std::string name; std::string account; uint64_t primary_key() const { return key; } uint64_t by_id() const {return secondid; } uint64_t by_anotherid() const {return anotherid; } };
typedef eosio::multi_index<N(mystruct), mystruct, indexed_by<N(secondid), const_mem_fun>, indexed_by<N(anotherid), const_mem_fun>> datastore;
諸此類推。
這兒有一件重要的事要注意,struct 名稱匹配表名,并且名稱將出現在 abi 文件,必須滿足規則(少于12個字符并且都是小寫)。如果不滿足規則,這些表通過 abi 將 不可見(你可以通過編輯 abi 文件規避這個限制)。
3.創建定義類型的局部變量
// local instances of the multi indexes pollstable _polls; votes _votes;
現在我已經定義了一個有兩個索引的多索引表,我可以在智能合約中用這個表。
下面展示一個用了兩個多索引表的工作智能合約的例子。這兒你能看出怎樣迭代表以及怎樣在同一合約中用兩個表。
C
#include
using namespace eosio;
class youvote : public contract { public: youvote(account_name s):contract(s), _polls(s, s), _votes(s, s) {}
篇幅有限,此部分代碼略,詳見:https://eosforce.github.io/Documentation/#/zh-cn/contract/
END
1.TMT觀察網遵循行業規范,任何轉載的稿件都會明確標注作者和來源;
2.TMT觀察網的原創文章,請轉載時務必注明文章作者和"來源:TMT觀察網",不尊重原創的行為TMT觀察網或將追究責任;
3.作者投稿可能會經TMT觀察網編輯修改或補充。