eosio.cdt 在 1.2.x 和 1.3.x 的改動比較大, 雖然虛擬機是向後兼容的, 可是爲了不意外狀況, 咱們都會將陸續將合約代碼升級。下面來介紹一下大體的改動。git
# 安裝 eosio.cdt, 由於 llvm 庫比較大, 因此執行 clone 的時候比較慢 $ git clone https://github.com/EOSIO/eosio.cdt.git $ git submodule update --init --recursive $ ./build.sh $ sudo ./install.sh
uint64_t 別名 account_name, permission_name, scope_name, table_name, action_name 所有移除, 新增 typedef capi_name uint64_t
symbol_name 別名移除,用 symbol_code 代替
移除 time , weight_type typedefs
移除 transaction_id_type, block_id_type typedefs
checksum160 -> capi_checksum160, checksum256 -> capi_checksum256, checksum512 -> capi_checksum512, public_key -> capi_public_key, signature -> capi_signature
移除掉未實現的 api : require_write_lock 和 require_read_lockgithub
移除 bytes typedefs
移除文件 eosiolib/types.hpp
移除文件 eosiolib/optional.hpp, 用 std::optional 代替
移除 eosiolib/core_symbol.hpp 文件, 之後合約須要自行聲明 core_symbol
增長文件 eosiolib/name.hppapi
將 typedef eosio::extensions_types 移到 eosiolib/transaction.hpp
移除掉對 checksum struct 的 == 和 != 的重載
移除掉 API eosio::char_to_symbol, eosio::string_to_name, eosio::name_suffix, 都整合進了 name struct
移除掉宏命令 N(X), 重載運算符 ""_n ,例如 "foo"_n 或者 name("foo") 來轉換成 name struct 類型
將 eosio::name struct 的定義 和 ""_n 運算符 移至 eosiolib/name.hpp
ps: 讀者可使用 #define N(X) name(#X) 來減小代碼的改動了哈。app
移除name 顯式 隱式轉換成 uint64_t
添加 enum class eosio::name::raw : uint64_t 用於從 name struct 隱式轉換成 raw
添加 bool 類型轉換,會返回 name struct 轉化成 uint64_t 是否爲 0
構造函數都用 constexpr, 確保 name struct 實例化時,都會給 value 賦初值
添加新的 constexpr 方法 eosio::name::length, eosio::name::suffix
添加 name struct 的比較函數函數
移除 eosio::symbol_type strcut , 用 eosio::symbol class 代替
添加 eosio::symbol_code struct
移除掉 eosio::string_to_symbol, eosio::is_valid_symbol, eosio::symbol_name_length 方法,都整合進了 symbol_code struct
移除宏命令#define S(P,X) ::eosio::string_to_symbol(P,#X), 直接實例化 symbol class eg: symbol(symbol_code("SYS"), 4) or symbol("SYS", 4)
重構 eosio::extended_symbol structui
構造器現須要顯式傳入 quantity 和 symbol, 再也不有默認值spa
Rename EOSIO_ABO to EOSIO_DISPATCH, 更加明確的表達該宏指令的做用
根據 contract 的改動重構 EOSIO_DISPATCHcode
索引不能直接用 name struct 須要使用 eosio::name::raw
multi_index code 再也不使用 uint64_t, 使用 eosio::name對象
同 multi_index, 用 eosio::name 替代 uint64_t索引
添加 inline function: eosio::require_auth, eosio::has_auth, eosio::is_account
重構 eosio::permission_level, 用 eosio::name 替換 uint64_t
移除 宏命令 ACTION,整合到了 eosio.hpp
新增 action_wrapper struct, 它的出現,讓咱們對inline action 的使用更加便利化,至關於把 inline action 封裝成一個 struct,直接實例化即可以發送一個 inline action, 下面會寫例子。
修改 eosio::check_transaction_authorization 參數類型 std::set<capi_public_key> to std::set<public_key> , 使得能和 eosio 的 public_key 兼容。
eosio::check_permission_authorization 參數 account, permission 類型從 uint64_t 修改爲 eosio::name
新增 ignore struct, 會讓ABI 生成對應的類型, 但datastream 不會去序列化它
新增 ignore_wrapper, 方便其餘合約調用聲明的 action。
其中最大的地方當屬 去掉了 uint64_t 的別名,須要用 name struct 來代替, 不該該用新的別名 capi_name。 不說了,筆者改代碼改到想哭了。但爲何要作這個改動呢, 目前對於 account_name 等所使用的都是 implicit, 這意味着可能有一些 bad implicit。
Eg:
//@abi action void hi(){ name acc = name{10}; print(acc==10); }
我本意是要判斷 兩個 name struct 是否相等, 可是隱式轉換使我直接比較整數 10 也能返回 true。
因此重構了 name struct,消除了這種風險。
此次的改動也變得比較偏面向對象思惟, 像 eosio::char_to_symbol, eosio::string_to_name, eosio::name_suffix 都被整合進了 name struct 裏面。
symbol 和 symbol_code 也被重構了。宏命令 S 被移除,不能直接用 S(4, SYS) 去聲明一個 token symbol, 要用 symbol(symbol_code("SYS"), 4) or symbol("SYS", 4)去實例化一個symbol 對象, 也將一些針對 symbol 的函數整合進了 class。
contract( name receiver, name code, datastream<const char*> ds ):_self(receiver),_code(code),_ds(ds) {}
構造函數增長 code 和 ds 參數。增長 ds 參數是爲了方便咱們手動解析數據。 這跟後面要說到的 ignore struct 有比較大的關係。
這種改動也意味着咱們重寫 apply 的方式要改動.
Eg:
extern "C" { void apply( uint64_t receiver, uint64_t code, uint64_t action ) { auto self = receiver; // 攔截 失敗的 deferred_trx if( code == "eosio"_n.value && action == "onerror"_n.value ) { const auto act_data = unpack_action_data<onerror>(); auto sender = uint64_t( act_data.sender_id >> 64); if( sender == self){ test bos(eosio::name(receiver), eosio::name(code),datastream<const char*>(nullptr, 0)); bos.resend( act_data.unpack_sent_trx(), uint64_t( act_data.sender_id) ); } // 攔截 eosio.token 的 EOS 轉帳操做 } else if ( code == "eosio.token"_n.value ){ test bos(eosio::name(receiver), eosio::name(code),datastream<const char*>(nullptr, 0)); const auto t = unpack_action_data<transfer_args>(); if(t.from.value != self && t.to.value == self){ bos._transfer(t.from, t.to, t.quantity, t.memo); } }else if ( code == self || action == "onerror"_n.value ) { switch( action ) { EOSIO_DISPATCH_HELPER( test, (hi)) } } } }
在 action 的參數加上 ignore struct, 會告訴虛擬機,不要解析此數據, 讓本身手動解析。
使用 action_wrapper 把 hello:hi action 包裝起來。
使用inline action 時,用 ignore_wrapper 代表該參數是一個 ignore 類型。
Eg:
#include <eosiolib/eosio.hpp> #include<eosiolib/ignore.hpp> using namespace eosio; CONTRACT hello : public eosio::contract { public: using contract::contract; ACTION hi( name user, ignore<uint64_t>, ignore<std::string>) { print_f( "Hello % from hello", user ); // 讀取 ignore 數據。 uint64_t test; _ds >> test; printui(test); std::string str; _ds >> str; prints_l(str.c_str(),str.size()); } // 用 action_wrapper , 把 hello::hi action 包裝起來 using hi_action = action_wrapper<"hi"_n, &hello::hi>; ACTION inlineaction( name user, name inlinecode ){ print_f( "Hello % from send_inline", user ); // constructor takes two arguments (the code the contract is deployed on and the set of permissions) // 實例化 hi_action, 並進行調用。 // inlinecode 參數及對應的 hi action 的合約帳號。 hello::hi_action hi(inlinecode, {_self, "active"_n}); hi.send(user,ignore_wrapper(22),ignore_wrapper("asdfa")); } }; EOSIO_DISPATCH( hello, (hi)(inlineaction) )
結論:兩個版本的主要改動是消除隱式轉換的風險。 也重構了一些模塊的代碼, 變得更加直觀。 新增了 action_wrapper struct, 使得 inline action 的使用更加方便快捷。
轉載請註明來源:https://eos.live/detail/17811