如何用JavaScript计算Hyperledger Fabric当前区块哈希及转换Block对象为字节数组
Got it, let's resolve this. The Hyperledger Fabric Java SDK includes utilities for handling block hashes and serialization, but the Node SDK doesn't ship with this out of the box. Below is a complete, production-ready implementation that aligns with Fabric's protobuf specification, allowing you to convert a decoded Block object to a byte array (and compute the block hash for queryBlockByHash()).
Prerequisite
First, install the official Fabric protobuf definitions package (required for correct serialization):
npm install @hyperledger/fabric-protos
1. Function to Convert Decoded Block Object to Byte Array
This function takes your decoded Block JSON object, maps it to Fabric's protobuf structure, and serializes it to a Buffer (byte array) that matches the format peers store:
const { common } = require('@hyperledger/fabric-protos'); function blockObjToByteArray(blockObj) { // 1. Build and serialize the BlockHeader const header = new common.BlockHeader(); header.setNumber(BigInt(blockObj.header.number)); // Use BigInt to handle large block numbers header.setPreviousHash(Buffer.from(blockObj.header.previous_hash, 'hex')); header.setDataHash(Buffer.from(blockObj.header.data_hash, 'hex')); // 2. Build and serialize BlockData const blockData = new common.BlockData(); const dataEntries = blockObj.data.data.map(entry => { // Adjust based on your blockObj's data format: if (typeof entry === 'string') return Buffer.from(entry, 'hex'); if (Buffer.isBuffer(entry)) return entry; // If entry is a decoded Envelope object, re-serialize it: const envelope = new common.Envelope(); envelope.setPayload(Buffer.from(entry.payload, 'hex')); envelope.setSignature(Buffer.from(entry.signature, 'hex')); return envelope.serializeBinary(); }); blockData.setDataList(dataEntries); // 3. Build and serialize BlockMetadata const blockMetadata = new common.BlockMetadata(); const metadataEntries = blockObj.metadata.metadata.map(entry => { if (typeof entry === 'string') return Buffer.from(entry, 'hex'); if (Buffer.isBuffer(entry)) return entry; // If entry is a decoded metadata object, adjust as needed: return Buffer.from(entry.value, 'hex'); }); blockMetadata.setMetadataList(metadataEntries); // 4. Assemble and serialize the full Block const block = new common.Block(); block.setHeader(header); block.setData(blockData); block.setMetadata(blockMetadata); return block.serializeBinary(); // Returns Buffer (byte array) }
2. Function to Compute Block Hash (for queryBlockByHash())
If your goal is to get the block hash to pass to queryBlockByHash() (since that method expects the hash, not the full block byte array), use this function (matches Java SDK's ChainUtils.computeBlockHash logic):
const crypto = require('crypto'); const { common } = require('@hyperledger/fabric-protos'); function computeBlockHash(blockObj) { // Serialize only the BlockHeader (Fabric computes block hash from the header) const header = new common.BlockHeader(); header.setNumber(BigInt(blockObj.header.number)); header.setPreviousHash(Buffer.from(blockObj.header.previous_hash, 'hex')); header.setDataHash(Buffer.from(blockObj.header.data_hash, 'hex')); const headerBytes = header.serializeBinary(); // Compute SHA256 hash of the header bytes return crypto.createHash('sha256').update(headerBytes).digest(); }
Usage Example
Using your sample blockObj:
let blockObj = { header: { number: '100', previous_hash: '99478ca59eb60cc968cec9dde401ce70b1e470aeab4a458ba870f4a717b2e24a', data_hash: '1bee34054bc4457f51e3c00bfa552878503deb3220f2d171b55d2a35299afdb0' }, data: { data: [/* Your Envelope entries (hex strings or decoded objects) */] }, metadata: { metadata: [/* Your metadata entries */] } }; // Convert to byte array (if needed for other use cases) const blockByteArray = blockObjToByteArray(blockObj); // Compute hash for queryBlockByHash() const blockHash = computeBlockHash(blockObj); // Use the hash in queryBlockByHash() channel.queryBlockByHash(blockHash, targetPeer, useAdmin) .then(queriedBlock => console.log('Queried Block:', queriedBlock)) .catch(err => console.error('Query failed:', err));
Key Notes
- BigInt for Block Numbers: JavaScript's
Numbertype can lose precision for block numbers larger than 2^53, so always useBigIntwhen setting the block number in the protobuf. - Adjust for Your Block Object Structure: If your
blockObjhas nested decoded protobuf objects (instead of hex strings), update the mapping logic to match the actual structure of your data. - Compatibility: Ensure the version of
@hyperledger/fabric-protosmatches your Fabric Node SDK version to avoid serialization mismatches.
内容的提问来源于stack exchange,提问作者Zach Gollwitzer

