1. 程式人生 > >EOS智慧合約入門

EOS智慧合約入門

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    ] 
[email protected]:~/eos$ cleos get currency balance acctoken inita
75.0000 EOS
[email protected]:~/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    ]