詳解 EOS 智能合約的 cpp 文件
以前的文章介紹了 eosio.token 智能合約的 hpp 文件,此次向你們介紹 eosio.token.cpp 文件,cpp 文件即 C++ 代碼文件,智能合約全部的業務邏輯內容都是在 cpp 文件中實現的。git
eosio.token.cpp 文件地址: https://github.com/EOSIO/eos/...github
瞭解 C/C++ 開發的同窗確定熟悉,cpp 文件的主要使命是實現 hpp 文件中聲明的函數(方法),包括公有函數(EOS 裏也叫 action)和私有函數。hpp 裏挖的坑,cpp 要一個不留地實現。數據庫
照慣例,私有函數都是工具函數,供類內部的其餘函數調用。segmentfault
做用:從指定帳戶中減去資產
參數:被操做帳戶,資產數,資產狀態函數
// 參數:被操做帳戶 資產種類與數量 資產狀態結構體 void token::sub_balance( account_name owner, asset value, const currency_stats& st ) { //創建一個 multi_index,用來操做數據庫 //這裏的參數 _self 表示數據的擁有者爲智能合約自己,參數 owner 表示儲存在名爲被操做帳戶的表中 //這樣並非直接創建了一個新表,而是讓 C++ 程序與數據庫對應的表之間創建了數據傳輸的通道 accounts from_acnts( _self, owner ); //在數據表中查詢要減小的代幣結構體,就是 hpp 文件中定義的 account 結構體 const auto& from = from_acnts.get( value.symbol.name() ); //校驗,要減小的代幣數量應該小於目前擁有的代幣數量,不然會報錯。 eosio_assert( from.balance.amount >= value.amount, "overdrawn balance" ); //判斷是否有被操做帳戶的受權 if( has_auth( owner ) ) { //校驗,帳戶是否被凍結 eosio_assert( !st.can_freeze || !from.frozen, "account is frozen by issuer" ); //校驗,這種代幣是否被凍結 eosio_assert( !st.can_freeze || !st.is_frozen, "all transfers are frozen by issuer" ); //校驗,帳戶是否在白名單中 eosio_assert( !st.enforce_whitelist || from.whitelist, "account is not white listed" ); //若是沒有被操做帳戶的受權,檢查是否有發幣者的受權 } else if( has_auth( st.issuer ) ) { //若是有發幣者的受權,那麼確定是在召回代幣,查看代幣是否能夠召回 eosio_assert( st.can_recall, "issuer may not recall token" ); } else { //若是兩種受權都沒有,則失敗,沒有足夠的權限 eosio_assert( false, "insufficient authority" ); } //經過 Lambda 表達式(匿名函數)修改將代幣結構體 from_acnts.modify( from, owner, [&]( auto& a ) { //匿名函數 函數體 a.balance -= value; }); }
做用:從指定帳戶中增長資產
參數:被操做帳戶,資產數,資產狀態,存儲資源支付帳戶工具
// 參數:被操做帳戶 代幣數量 代幣狀態結構體 儲存支付帳戶 void token::add_balance( account_name owner, asset value, const currency_stats& st, account_name ram_payer ) { //創建一個 multi_index,用來操做數據庫 accounts to_acnts( _self, owner ); //在數據表中查詢要增長的代幣結構體 auto to = to_acnts.find( value.symbol.name() ); //若是 to == to_acnts.end(),說明查找到數據表的末尾都沒有對應的結構體,說明該帳戶沒有該代幣 if( to == to_acnts.end() ) { //校驗,該代幣是否開啓了白名單功能 eosio_assert( !st.enforce_whitelist, "can only transfer to white listed accounts" ); //使用 emplace 方法,在數據表中增長一項 to_acnts.emplace( ram_payer, [&]( auto& a ){ //匿名函數體,代幣數量等於每次轉入的數量,由於以前沒有 a.balance = value; }); //若是數據表中已經存在此項,只需增長代幣數量 } else { //檢查帳戶是否在白名單中 eosio_assert( !st.enforce_whitelist || to->whitelist, "receiver requires whitelist by issuer" ); //使用 modify 方法,修改項目 to_acnts.modify( to, 0, [&]( auto& a ) { //直接修改代幣數量 a.balance += value; }); } }
EOS 合約中的公有函數大可能是供別的帳戶調用的 Action,根據 hpp 文件,咱們須要實現 create、issue、transfer 三個公有函數(action)。區塊鏈
create 函數用來建立一種新的代幣,並設置這種新代幣的各類參數。ui
//參數:發幣帳戶 void token::create( account_name issuer, //最大發行量 asset maximum_supply, //發幣者是否能夠凍結代幣 uint8_t issuer_can_freeze, //發幣者是否能夠召回代幣 uint8_t issuer_can_recall, //是否能夠設置白名單 uint8_t issuer_can_whitelist ) { //須要 eosio.token 帳戶自己的受權 require_auth( _self ); auto sym = maximum_supply.symbol; //校驗,新代幣名稱是否有效 eosio_assert( sym.is_valid(), "invalid symbol name" ); //校驗,最大發行量是否有效 eosio_assert( maximum_supply.is_valid(), "invalid supply"); //校驗,最大發行量是否大於零 eosio_assert( maximum_supply.amount > 0, "max-supply must be positive"); //創建一個 milti_index 數據表,用來與數據庫交互 stats statstable( _self, sym.name() ); //在表中搜索相同名稱的代幣 auto existing = statstable.find( sym.name() ); //校驗,是否已經存在相同名稱的代幣 eosio_assert( existing == statstable.end(), "token with symbol already exists" ); //使用 emplace 方法,在數據表中增長一項 statstable.emplace( _self, [&]( auto& s ) { // 使用匿名函數,將傳入的參數賦值給 currency_stats 結構體 s.supply.symbol = maximum_supply.symbol; s.max_supply = maximum_supply; s.issuer = issuer; s.can_freeze = issuer_can_freeze; s.can_recall = issuer_can_recall; s.can_whitelist = issuer_can_whitelist; }); }
transfer 應該是這個智能合約最經常使用的函數,就是將代幣從一個帳戶轉到另外一個。this
//轉出方帳戶名 void token::transfer( account_name from, //轉入方帳戶名 account_name to, //代幣種類與數量 asset quantity, //轉帳備忘(目前還沒實現) string /*memo*/ ) { //打印轉帳提示 print( "transfer" ); //檢查轉出方權限 require_auth( from ); //獲得代幣名稱 auto sym = quantity.symbol.name(); //創建一個 milti_index 數據表,用來與數據庫交互 stats statstable( _self, sym ); //在數據表中尋找代幣的 currency_stats 結構體 const auto& st = statstable.get( sym ); //向轉出方獲取回執 require_recipient( from ); //向轉入方獲取回執 require_recipient( to ); //校驗,轉出的代幣是否有效 eosio_assert( quantity.is_valid(), "invalid quantity" ); //校驗,轉帳數量要大於0 eosio_assert( quantity.amount > 0, "must transfer positive quantity" ); //調用 sub_balance 私有方法 sub_balance( from, quantity, st ); //調用 add_balance 私有方法 add_balance( to, quantity, st, from ); }
上面的 create 函數建立代幣後只是給定了參數,並無真正的代幣被建立出來,須要 issue 函數進行發幣。code
//參數:代幣接收方 代幣數量和種類 備忘 void token::issue( account_name to, asset quantity, string memo ) { //打印提示 print( "issue" ); //獲取代幣名稱 auto sym = quantity.symbol.name(); //創建一個 milti_index 數據表,用來與數據庫交互 stats statstable( _self, sym ); //在數據表中搜索代幣 currency_stats 結構體 const auto& st = statstable.get( sym ); //檢查發幣者受權 require_auth( st.issuer ); //檢查資產是否有效 eosio_assert( quantity.is_valid(), "invalid quantity" ); //檢查資產是否大於零 eosio_assert( quantity.amount > 0, "must issue positive quantity" ); //檢查創造的總資產是否大於最大代幣數 eosio_assert( quantity <= st.max_supply - st.supply, "quantity exceeds available supply"); //更新資產創造數量記錄 statstable.modify( st, 0, [&]( auto& s ) { s.supply += quantity; }); //給發佈者增長資產 add_balance( st.issuer, quantity, st, st.issuer ); //判斷代幣接受方是不是發幣者 if( to != st.issuer ) { //這裏使用了一個特殊處理,先給發幣者增長相應的代幣,再調用 transfer 函數轉帳給代幣接受方。 //這樣作的目的是讓代幣接受方收到通知 SEND_INLINE_ACTION( *this, transfer, {st.issuer,N(active)}, {st.issuer, to, quantity, memo} ); } }
你們知道 EOS 系統的智能合約是以 action 爲基本動做單位的,咱們要將須要聲明爲 action 的函數告知 EOS 系統,經過如下宏便可實現。
//將 create issue transfer 三個共有函數聲明爲 action,供其餘帳戶調用。 EOSIO_ABI( eosio::token, (create)(issue)(transfer) )
【許曉笛】 EOS 智能合約案例解析(1)
【許曉笛】 EOS 智能合約案例解析(3)
圓方圓學院聚集大批區塊鏈名師,打造精品的區塊鏈技術課程。 在各大平臺都長期有優質免費公開課,歡迎報名收看。