如果你想知道在EOS上部署了什么版本的智能合約,你需要查看代碼哈希。我們將看到如何計(jì)算代碼和ABI哈希,并編寫一個(gè)函數(shù),通過(guò)比較它們的哈希來(lái)查看本地WASM文件是否與正在運(yùn)行的協(xié)議相匹配。
EOS代碼的哈希
當(dāng)通過(guò)eosio
setcode
操作設(shè)置或更新合約時(shí),檢查合約代碼是否已經(jīng)在運(yùn)行。因此,通過(guò)查看setcode
實(shí)現(xiàn),我們可以從WASM文件看到如何計(jì)算哈希值。
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" );
// ...
}
這只是一個(gè)簡(jiǎn)單的WASM字節(jié)表示的sha256哈希值。(第二個(gè)參數(shù)只是字節(jié)數(shù)組的長(zhǎng)度,哈希函數(shù)需要它來(lái)知道要對(duì)多少字節(jié)進(jìn)行哈希處理。)
在node.js中,我們可以通過(guò)哈希一個(gè)WASM文件并將其與區(qū)塊鏈上代碼的哈希值進(jìn)行比較來(lái)輕松實(shí)現(xiàn)這一點(diǎn)。
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
函數(shù)是一個(gè)很好的API端點(diǎn),可以通過(guò)一個(gè)查詢同時(shí)獲取帳戶的代碼和abi哈希。
注意,WASM文件和代碼哈希依賴于編譯期間使用的eosio cpp
版本和-o
優(yōu)化參數(shù)。來(lái)自同一個(gè)C++代碼,代碼哈希可能是不同的。
EOS ABI 的哈希
我們可以嘗試同樣的方法來(lái)計(jì)算ABI哈希,但是,由于某種原因,eosio setabi
操作不檢查ABI哈希,因此允許使用相同的哈希進(jìn)行更新。
但是get-raw-abi-api
端點(diǎn)返回一個(gè)abi哈希,因此必須從某個(gè)地方獲取它。通過(guò)檢查nodeos-chain
插件,我們可以看到它是為每個(gè)請(qǐng)求動(dòng)態(tài)計(jì)算的:
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;
}
計(jì)算結(jié)果與ABI字節(jié)表示的代碼哈希 - SHA256 完全相同。然而,實(shí)際存儲(chǔ)ABI的方式有一個(gè)很大的區(qū)別。它不是作為熟悉的JSON文件存儲(chǔ)的,而是作為EOS稱之為原始ABI的打包方式存儲(chǔ)的。
從raw abi轉(zhuǎn)換為json很容易使用eosjs
,但是從json轉(zhuǎn)換為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())
}
每個(gè)ABI必須包含一組特定的字段,如version
、types
、structs
、actions
、tables
,即版本、類型、結(jié)構(gòu)、操作、表等,然后將這些字段序列化為更大的有效表示形式。
計(jì)算ABI哈希并用鏈上的值檢查它是很簡(jiǎn)單的:
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等區(qū)塊鏈相關(guān)的交互式在線編程實(shí)戰(zhàn)教程:
- EOS入門教程,本課程幫助你快速入門EOS區(qū)塊鏈去中心化應(yīng)用的開發(fā),內(nèi)容涵蓋EOS工具鏈、賬戶與錢包、發(fā)行代幣、智能合約開發(fā)與部署、使用代碼與智能合約交互等核心知識(shí)點(diǎn),最后綜合運(yùn)用各知識(shí)點(diǎn)完成一個(gè)便簽DApp的開發(fā)。
- 深入淺出玩轉(zhuǎn)EOS錢包開發(fā),本課程以手機(jī)EOS錢包的完整開發(fā)過(guò)程為主線,深入學(xué)習(xí)EOS區(qū)塊鏈應(yīng)用開發(fā),課程內(nèi)容即涵蓋賬戶、計(jì)算資源、智能合約、動(dòng)作與交易等EOS區(qū)塊鏈的核心概念,同時(shí)也講解如何使用eosjs和eosjs-ecc開發(fā)包訪問(wèn)EOS區(qū)塊鏈,以及如何在React前端應(yīng)用中集成對(duì)EOS區(qū)塊鏈的支持。課程內(nèi)容深入淺出,非常適合前端工程師深入學(xué)習(xí)EOS區(qū)塊鏈應(yīng)用開發(fā)。
- java比特幣開發(fā)教程,本課程面向初學(xué)者,內(nèi)容即涵蓋比特幣的核心概念,例如區(qū)塊鏈存儲(chǔ)、去中心化共識(shí)機(jī)制、密鑰與腳本、交易與UTXO等,同時(shí)也詳細(xì)講解如何在Java代碼中集成比特幣支持功能,例如創(chuàng)建地址、管理錢包、構(gòu)造裸交易等,是Java工程師不可多得的比特幣開發(fā)學(xué)習(xí)課程。
- php比特幣開發(fā)教程,本課程面向初學(xué)者,內(nèi)容即涵蓋比特幣的核心概念,例如區(qū)塊鏈存儲(chǔ)、去中心化共識(shí)機(jī)制、密鑰與腳本、交易與UTXO等,同時(shí)也詳細(xì)講解如何在Php代碼中集成比特幣支持功能,例如創(chuàng)建地址、管理錢包、構(gòu)造裸交易等,是Php工程師不可多得的比特幣開發(fā)學(xué)習(xí)課程。
- c#比特幣開發(fā)教程,本課程面向初學(xué)者,內(nèi)容即涵蓋比特幣的核心概念,例如區(qū)塊鏈存儲(chǔ)、去中心化共識(shí)機(jī)制、密鑰與腳本、交易與UTXO等,同時(shí)也詳細(xì)講解如何在C#代碼中集成比特幣支持功能,例如創(chuàng)建地址、管理錢包、構(gòu)造裸交易等,是C#工程師不可多得的比特幣開發(fā)學(xué)習(xí)課程。
- java以太坊開發(fā)教程,主要是針對(duì)java和android程序員進(jìn)行區(qū)塊鏈以太坊開發(fā)的web3j詳解。
- python以太坊,主要是針對(duì)python工程師使用web3.py進(jìn)行區(qū)塊鏈以太坊開發(fā)的詳解。
- php以太坊,主要是介紹使用php進(jìn)行智能合約開發(fā)交互,進(jìn)行賬號(hào)創(chuàng)建、交易、轉(zhuǎn)賬、代幣開發(fā)以及過(guò)濾器和交易等內(nèi)容。
- 以太坊入門教程,主要介紹智能合約與dapp應(yīng)用開發(fā),適合入門。
- 以太坊開發(fā)進(jìn)階教程,主要是介紹使用node.js、mongodb、區(qū)塊鏈、ipfs實(shí)現(xiàn)去中心化電商DApp實(shí)戰(zhàn),適合進(jìn)階。
- ERC721以太坊通證實(shí)戰(zhàn),課程以一個(gè)數(shù)字藝術(shù)品創(chuàng)作與分享DApp的實(shí)戰(zhàn)開發(fā)為主線,深入講解以太坊非同質(zhì)化通證的概念、標(biāo)準(zhǔn)與開發(fā)方案。內(nèi)容包含ERC-721標(biāo)準(zhǔn)的自主實(shí)現(xiàn),講解OpenZeppelin合約代碼庫(kù)二次開發(fā),實(shí)戰(zhàn)項(xiàng)目采用Truffle,IPFS,實(shí)現(xiàn)了通證以及去中心化的通證交易所。
- C#以太坊,主要講解如何使用C#開發(fā)基于.Net的以太坊應(yīng)用,包括賬戶管理、狀態(tài)與交易、智能合約開發(fā)與交互、過(guò)濾器和交易等。
- Hyperledger Fabric 區(qū)塊鏈開發(fā)詳解,本課程面向初學(xué)者,內(nèi)容即包含Hyperledger Fabric的身份證書與MSP服務(wù)、權(quán)限策略、通道配置與啟動(dòng)、鏈碼通信接口等核心概念,也包含F(xiàn)abric網(wǎng)絡(luò)設(shè)計(jì)、nodejs鏈碼與應(yīng)用開發(fā)的操作實(shí)踐,是Nodejs工程師學(xué)習(xí)Fabric區(qū)塊鏈開發(fā)的最佳選擇。
- Hyperledger Fabric java 區(qū)塊鏈開發(fā)詳解,課程面向初學(xué)者,內(nèi)容即包含Hyperledger Fabric的身份證書與MSP服務(wù)、權(quán)限策略、頻道配置與啟動(dòng)、鏈碼通信接口等核心概念,也包含F(xiàn)abric網(wǎng)絡(luò)設(shè)計(jì)、java鏈碼與應(yīng)用開發(fā)的操作實(shí)踐,是java工程師學(xué)習(xí)Fabric區(qū)塊鏈開發(fā)的最佳選擇。
- tendermint區(qū)塊鏈開發(fā)詳解,本課程適合希望使用tendermint進(jìn)行區(qū)塊鏈開發(fā)的工程師,課程內(nèi)容即包括tendermint應(yīng)用開發(fā)模型中的核心概念,例如ABCI接口、默克爾樹、多版本狀態(tài)庫(kù)等,也包括代幣發(fā)行等豐富的實(shí)操代碼,是go語(yǔ)言工程師快速入門區(qū)塊鏈開發(fā)的最佳選擇。
匯智網(wǎng)原創(chuàng)翻譯,轉(zhuǎn)載請(qǐng)標(biāo)明出處。這里是如何計(jì)算EOS代碼和ABI的哈希