若是你想知道在EOS上部署了什麼版本的智能合約,你須要查看代碼哈希。咱們將看到如何計算代碼和ABI哈希,並編寫一個函數,經過比較它們的哈希來查看本地WASM文件是否與正在運行的協議相匹配。php
當經過eosio
setcode
操做設置或更新合約時,檢查合約代碼是否已經在運行。所以,經過查看setcode
實現,咱們能夠從WASM文件看到如何計算哈希值。前端
void apply_eosio_setcode(apply_context& context) { // some setup code fc::sha256 code_id; /// default ID == 0 if( act.code.size() > 0 ) { code_id = fc::sha256::hash( act.code.data(), (uint32_t)act.code.size() ); wasm_interface::validate(context.control, act.code); } const auto& account = db.get<account_object,by_name>(act.account); int64_t code_size = (int64_t)act.code.size(); int64_t old_size = (int64_t)account.code.size() * config::setcode_ram_bytes_multiplier; int64_t new_size = code_size * config::setcode_ram_bytes_multiplier; EOS_ASSERT( account.code_version != code_id, set_exact_code, "contract is already running this version of code" ); // ... }
這只是一個簡單的WASM字節表示的sha256哈希值。(第二個參數只是字節數組的長度,哈希函數須要它來知道要對多少字節進行哈希處理。)java
在node.js中,咱們能夠經過哈希一個WASM文件並將其與區塊鏈上代碼的哈希值進行比較來輕鬆實現這一點。node
const fs = require(`fs`) const crypto = require(`crypto`) const loadFileContents = file => { if (!fs.existsSync(file)) { throw new Error(`Code file "${file}" does not exist.`) } // no encoding => read as Buffer return fs.readFileSync(file) } const createHash = contents => { const hash = crypto.createHash(`sha256`) hash.update(contents) const digest = hash.digest(`hex`) return digest } // fetch code contract from blockchain const { code_hash: onChainCodeHash, abi_hash } = await api.rpc.fetch(`/v1/chain/get_raw_abi`, { account_name: `hello`, }) const contents = loadFileContents(`contracts/hello.wasm`) const codeHash = createHash(contents) if (codeHash === onChainCodeHash) { console.log(`Code is up-to-date.`) }
get-raw-abi
函數是一個很好的API端點,能夠經過一個查詢同時獲取賬戶的代碼和abi哈希。python
注意,WASM文件和代碼哈希依賴於編譯期間使用的eosio cpp
版本和-o
優化參數。來自同一個C++代碼,代碼哈希多是不一樣的。android
咱們能夠嘗試一樣的方法來計算ABI哈希,可是,因爲某種緣由,eosio setabi
操做不檢查ABI哈希,所以容許使用相同的哈希進行更新。程序員
可是get-raw-abi-api
端點返回一個abi哈希,所以必須從某個地方獲取它。經過檢查nodeos-chain
插件,咱們能夠看到它是爲每一個請求動態計算的:web
read_only::get_raw_abi_results read_only::get_raw_abi( const get_raw_abi_params& params )const { get_raw_abi_results result; result.account_name = params.account_name; const auto& d = db.db(); const auto& accnt = d.get<account_object,by_name>(params.account_name); result.abi_hash = fc::sha256::hash( accnt.abi.data(), accnt.abi.size() ); result.code_hash = accnt.code_version; if( !params.abi_hash || *params.abi_hash != result.abi_hash ) result.abi = blob{{accnt.abi.begin(), accnt.abi.end()}}; return result; }
計算結果與ABI字節表示的代碼哈希 - SHA256 徹底相同。然而,實際存儲ABI的方式有一個很大的區別。它不是做爲熟悉的JSON文件存儲的,而是做爲EOS稱之爲原始ABI的打包方式存儲的。mongodb
從raw abi轉換爲json很容易使用eosjs
,可是從json轉換爲raw abi須要一些nb的操做:編程
const {Serialize, Api} = require(`eosjs`) const {TextEncoder, TextDecoder} = require(`util`) // node only; native TextEncoder/Decoder const jsonToRawAbi = json => { const tmpApi = new Api({ textDecoder: new TextDecoder(), textEncoder: new TextEncoder(), }) const buffer = new Serialize.SerialBuffer({ textEncoder: tmpApi.textEncoder, textDecoder: tmpApi.textDecoder, }) const abiDefinition = tmpApi.abiTypes.get(`abi_def`) // need to make sure abi has every field in abiDefinition.fields // otherwise serialize throws const jsonExtended = abiDefinition.fields.reduce( (acc, {name: fieldName}) => Object.assign(acc, {[fieldName]: acc[fieldName] || []}), json, ) abiDefinition.serialize(buffer, jsonExtended) if (!Serialize.supportedAbiVersion(buffer.getString())) { throw new Error(`Unsupported abi version`) } buffer.restartRead() // convert to node buffer return Buffer.from(buffer.asUint8Array()) }
每一個ABI必須包含一組特定的字段,如version
、types
、structs
、actions
、tables
,即版本、類型、結構、操做、表等,而後將這些字段序列化爲更大的有效表示形式。
計算ABI哈希並用鏈上的值檢查它是很簡單的:
const contents = loadFileContents(`contracts/hello.abi`) const abi = JSON.parse(contents.toString(`utf8`)) const serializedAbi = jsonToRawAbi(abi) const abiHash = createHash(serializedAbi) // fetch abi hash from blockchain const { code_hash, abi_hash: onChainAbiHash } = await api.rpc.fetch(`/v1/chain/get_raw_abi`, { account_name: `hello`, }) if (abiHash === onChainAbiHash) { console.log(`ABI is up-to-date.`) return null }
======================================================================
分享一些比特幣、以太坊、EOS等區塊鏈相關的交互式在線編程實戰教程:
- EOS入門教程,本課程幫助你快速入門EOS區塊鏈去中心化應用的開發,內容涵蓋EOS工具鏈、帳戶與錢包、發行代幣、智能合約開發與部署、使用代碼與智能合約交互等核心知識點,最後綜合運用各知識點完成一個便籤DApp的開發。
- 深刻淺出玩轉EOS錢包開發,本課程以手機EOS錢包的完整開發過程爲主線,深刻學習EOS區塊鏈應用開發,課程內容即涵蓋帳戶、計算資源、智能合約、動做與交易等EOS區塊鏈的核心概念,同時也講解如何使用eosjs和eosjs-ecc開發包訪問EOS區塊鏈,以及如何在React前端應用中集成對EOS區塊鏈的支持。課程內容深刻淺出,很是適合前端工程師深刻學習EOS區塊鏈應用開發。
- java比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在Java代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是Java工程師不可多得的比特幣開發學習課程。
- php比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在Php代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是Php工程師不可多得的比特幣開發學習課程。
- c#比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈存儲、去中心化共識機制、密鑰與腳本、交易與UTXO等,同時也詳細講解如何在C#代碼中集成比特幣支持功能,例如建立地址、管理錢包、構造裸交易等,是C#工程師不可多得的比特幣開發學習課程。
- java以太坊開發教程,主要是針對java和android程序員進行區塊鏈以太坊開發的web3j詳解。
- python以太坊,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
- php以太坊,主要是介紹使用php進行智能合約開發交互,進行帳號建立、交易、轉帳、代幣開發以及過濾器和交易等內容。
- 以太坊入門教程,主要介紹智能合約與dapp應用開發,適合入門。
- 以太坊開發進階教程,主要是介紹使用node.js、mongodb、區塊鏈、ipfs實現去中心化電商DApp實戰,適合進階。
- ERC721以太坊通證明戰,課程以一個數字藝術品創做與分享DApp的實戰開發爲主線,深刻講解以太坊非同質化通證的概念、標準與開發方案。內容包含ERC-721標準的自主實現,講解OpenZeppelin合約代碼庫二次開發,實戰項目採用Truffle,IPFS,實現了通證以及去中心化的通證交易所。
- C#以太坊,主要講解如何使用C#開發基於.Net的以太坊應用,包括帳戶管理、狀態與交易、智能合約開發與交互、過濾器和交易等。
- Hyperledger Fabric 區塊鏈開發詳解,本課程面向初學者,內容即包含Hyperledger Fabric的身份證書與MSP服務、權限策略、通道配置與啓動、鏈碼通訊接口等核心概念,也包含Fabric網絡設計、nodejs鏈碼與應用開發的操做實踐,是Nodejs工程師學習Fabric區塊鏈開發的最佳選擇。
- Hyperledger Fabric java 區塊鏈開發詳解,課程面向初學者,內容即包含Hyperledger Fabric的身份證書與MSP服務、權限策略、頻道配置與啓動、鏈碼通訊接口等核心概念,也包含Fabric網絡設計、java鏈碼與應用開發的操做實踐,是java工程師學習Fabric區塊鏈開發的最佳選擇。
- tendermint區塊鏈開發詳解,本課程適合但願使用tendermint進行區塊鏈開發的工程師,課程內容即包括tendermint應用開發模型中的核心概念,例如ABCI接口、默克爾樹、多版本狀態庫等,也包括代幣發行等豐富的實操代碼,是go語言工程師快速入門區塊鏈開發的最佳選擇。
匯智網原創翻譯,轉載請標明出處。這裏是如何計算EOS代碼和ABI的哈希