天天看點

EOS合約開發第七章-EOS合約簡介EOS合約簡介

EOS合約簡介

一、語言

基于EOSIO的塊鍊使用的是WebAssembly (WASM)來執行使用者編寫的智能合約。WASM是一種新興的Web标準,廣泛支援于谷歌、微軟、蘋果等。對編寫WASM标準的智能合約來說使用clang/llvm和它的C/C++編譯器是目前最為成熟的編譯工具鍊。

其他的第三方工具鍊在開發中,包括:Rust, Python, and Solidity。雖然這些語言可能看起來相對簡單,但它們可能會影響您所編寫的智能性能。我們認為,對于開發高性能和安全的智能合約,C++是最好的語言,将來eos的智能合約也還會繼續支援C++。

Linux / Mac OS Experience

EOSIO 支援下面的作業系統: - Amazon 2017.09 and higher - Centos 7 - Fedora 25 and higher (Fedora 27 推薦使用) - Mint 18 - Ubuntu 16.04 (Ubuntu 16.10 推薦使用) - MacOS Darwin 10.12 and higher (MacOS 10.13.x 推薦使用)

二、Action vs Transaction

Action表示單個操作,而transaction是一個或多個action的集合。Action是合約和賬戶之間進行通信的方式。Action可以單獨執行,或者組合組合起來作為一個整體執行。

僅有一個action的transaction.

{

  "expiration": "2018-04-01T15:20:44",

  "region": 0,

  "ref_block_num": 42580,

  "ref_block_prefix": 3987474256,

  "net_usage_words": 21,

  "kcpu_usage": 1000,

  "delay_sec": 0,

  "context_free_actions": [],

  "actions": [{

      "account": "eosio.token",

      "name": "issue",

      "authorization": [{

          "actor": "eosio",

          "permission": "active"

        }

      ],

      "data": "00000000007015d640420f000000000004454f5300000000046d656d6f"

    }

  ],

  "signatures": [

    ""

  ],

  "context_free_data": []

}

包含多個action的transaction, 這些action要麼全部成功要麼全部失敗.

{

  "expiration": "...",

  "region": 0,

  "ref_block_num": ...,

  "ref_block_prefix": ...,

  "net_usage_words": ..,

  "kcpu_usage": ..,

  "delay_sec": 0,

  "context_free_actions": [],

  "actions": [{

      "account": "...",

      "name": "...",

      "authorization": [{

          "actor": "...",

          "permission": "..."

        }

      ],

      "data": "..."

    }, {

      "account": "...",

      "name": "...",

      "authorization": [{

          "actor": "...",

          "permission": "..."

        }

      ],

      "data": "..."

    }

  ],

  "signatures": [

    ""

  ],

  "context_free_data": []

}

三、智能合約檔案

從簡單易用的角度出發,我們編寫了一個工具eosiocpp ,它可以建立一個新的智能合約。eosiocpp也可以建立3個合約檔案,它們僅僅包含了合約的架構。

$ eosiocpp -n ${contract}

上面的指令會在./${project}目錄下建立一個空的項目,它包含3個檔案

${contract}.abi ${contract}.hpp ${contract}.cpp

hpp

${contract}.hpp 這是合約的頭檔案,可以包含一些變量,常量和函數的聲明。

cpp

The ${contract}.cpp 這是合約的源碼檔案,包含合約的具體實作。

如果你用eosiocpp生成了一個 .cpp, 那它的内容大概類似如下:

#include <${contract}.hpp>

extern "C" {

    void init()  {

       eosio::print( "Init World!\n" ); // Replace with actual code

    }

    /// The apply method implements the dispatch of actions to this contract

    void apply( uint64_t code, uint64_t action ) {

       eosio::print( "Hello World: ", eosio::name(code), "->", eosio::name(action), "\n" );

    }

} // extern "C"

在這個例子裡,我們可以看到兩個函數,init和apply。它們會列印log并且不做任何檢查。任何人都可以在任何時刻執行BP允許的所有action。在不需要任何簽名的情況下,合約将被計入帶寬消耗。(Absent any required signatures, the contract will be billed for the bandwidth consumed.)

init

init 僅當合約第一次被部署的時候執行。 在這個函數裡可以初始化變量, 比如,在currency合約中總體的token的供應量。

apply

apply 是一個中轉函數, 他監聽所有傳入的action,并且根據action調用合約相應的函數。apply函數需要兩個參數, code 和 action。

code filter

這個參數是為了對action做出回應,比如下面的apply函數,你可以構造一個通用響應去忽略code。 (In order to respond to a particular action, structure the apply function as follows. You may also construct a response to general actions by omitting the code filter.)

if (code == N(${contract_name}) {

    // your handler to respond to particular action

}

當然你也可以為每個action構造各自的一個響應。

action filter

為了響應每一個action,比如構造比如下面的apply函數。通常和code filter一起使用

if (action == N(${action_name}) {

    //your handler to respond to a particular action

}

wast

任何合約程式想要部署到EOSIO的區塊鍊網絡中都必須編譯成WASM格式。這是EOS的支援唯一個的格式。

一旦你的CPP檔案寫好了,有就可以用eosiocpp把它編譯成WASM (.wast)檔案了

$ eosiocpp -o ${contract}.wast ${contract}.cpp

abi

ABI( Application Binary Interface)檔案是一個JSON格式的描述檔案,說明了如何在他們的JSON和二進制之間轉化使用者的action。ABI檔案也同時說明了如何轉換資料庫的狀态。一旦你用了ABI描述了你的合約,開發人員就和使用者就可以和你的合約通過JSON進行互動。

ABI可以通過.hpp檔案用eosiocpp生成。

$ eosiocpp -g ${contract}.abi ${contract}.hpp

下面這個例子展示了一個ABI檔案的架構:

{

  "types": [{

      "new_type_name": "account_name",

      "type": "name"

    }

  ],

  "structs": [{

      "name": "transfer",

      "base": "",

      "fields": {

        "from": "account_name",

        "to": "account_name",

        "quantity": "uint64"

      }

    },{

      "name": "account",

      "base": "",

      "fields": {

        "account": "name",

        "balance": "uint64"

      }

    }

  ],

  "actions": [{

      "action": "transfer",

      "type": "transfer"

    }

  ],

  "tables": [{

      "table": "account",

      "type": "account",

      "index_type": "i64",

      "key_names" : ["account"],

      "key_types" : ["name"]

    }

  ]

}

你會注意到這個ABI定義了一個actoin名字是transfer,類型是transfer。這就是告訴EOSIO,當調用的action是transfer時,它的格式是transfer,定義如下:

...

  "structs": [{

      "name": "transfer",

      "base": "",

      "fields": {

        "from": "account_name",

        "to": "account_name",

        "quantity": "uint64"

      }

    },{

...

ABI檔案有很多的部分組成,比如from,to和quantity。每個部分都有自己的類型,比如account_name和uint64。account_name是一個内建類型用base32字元串表示為uint64。想要看到更多的内建類型可以點選這裡

{

  "types": [{

      "new_type_name": "account_name",

      "type": "name"

    }

  ],

...

在上面types 數組裡,我們為已經存在的account_name類型定義了一個别名name 。

四、調試智能合約

為了能夠調試智能合約,你需要在你的本地環境中啟動一個nodeos。這個本地的nodeos可以是一個EOS私有的測試網絡或者是公網的測試網絡。

當你第一次建立智能合約的時候,推薦你最好在你自己的私有測試網絡中調試好,因為你對你自己的私有測試網絡有完全的掌控權。這可以讓你無限制的使用EOS(币)也可以随時複位它的狀态。當合約調試完畢,就可以部署到公共測試網絡了,本地先運作一個連接配接到公共測試網絡的nodeos,然後連接配接到這個節點就可以獲得log輸出了。

步驟是一樣的,是以下面這個手冊也适用于私有測試網絡中的測試。

如果你還沒有一個本地的nodeos環境,可以參考這個連接配接。預設情況下,你的本地nodes會運作在一個私有網絡中,除非你修改了config.ini檔案,讓他去連接配接公共測試網絡,如何修改可以參考這裡。

方法

調試最主要的方法就是用Caveman Debugging,我們增強了printing的功能,他可以去輸出變量的值并且檢查合約的流程。Printing可以通過下面API被合約使用: 這是c 這是 C++). C++的API是對C的封裝,是以大多數我們使用C++的API。

Print

Print C API 支援如下資料類型: - prints - a null terminated char array (string) - prints_l - any char array (string) with given size - printi - 64-bit unsigned integer - printi128 - 128-bit unsigned integer - printd - double encoded as 64-bit unsigned integer - printn - base32 string encoded as 64-bit unsigned integer - printhex - hex given binary of data and its size

同時 Print C++ API 對上面的C API進行了封裝,是以使用者不需要指定應該使用哪種類型的Print。Print C++ API 支援 - a null terminated char array (string) - integer (128-bit unsigned, 64-bit unsigned, 32-bit unsigned, signed, unsigned) - base32 string encoded as 64-bit unsigned integer - struct that has print() method

繼續閱讀