{"record":{"id":"a7a0ae295e38ece7","repo":"hyperledger/fabric","slug":"block-d-s-hash-s-mismatches-block-d-s-pre","errorCode":null,"errorMessage":"block [%d]'s hash (%s) mismatches block [%d]'s prev block hash (%s)","messagePattern":"block \\[(.+?)\\]'s hash \\((.+?)\\) mismatches block \\[(.+?)\\]'s prev block hash \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"orderer/common/cluster/util.go","lineNumber":255,"sourceCode":"\t\tclaimedHash := hex.EncodeToString(block.Header.DataHash)\n\t\treturn errors.Errorf(\"computed hash of block (%d) (%s) doesn't match claimed hash (%s)\",\n\t\t\tseq, computedHash, claimedHash)\n\t}\n\t// We have a previous block in the buffer, ensure current block's previous hash matches the previous one.\n\tif indexInBuffer > 0 {\n\t\tprevBlock := blockBuff[indexInBuffer-1]\n\t\tcurrSeq := block.Header.Number\n\t\tif prevBlock.Header == nil {\n\t\t\treturn errors.New(\"previous block header is nil\")\n\t\t}\n\t\tprevSeq := prevBlock.Header.Number\n\t\tif prevSeq+1 != currSeq {\n\t\t\treturn errors.Errorf(\"sequences %d and %d were received consecutively\", prevSeq, currSeq)\n\t\t}\n\t\tif !bytes.Equal(block.Header.PreviousHash, protoutil.BlockHeaderHash(prevBlock.Header)) {\n\t\t\tclaimedPrevHash := hex.EncodeToString(block.Header.PreviousHash)\n\t\t\tactualPrevHash := hex.EncodeToString(protoutil.BlockHeaderHash(prevBlock.Header))\n\t\t\treturn errors.Errorf(\"block [%d]'s hash (%s) mismatches block [%d]'s prev block hash (%s)\",\n\t\t\t\tprevSeq, actualPrevHash, currSeq, claimedPrevHash)\n\t\t}\n\t}\n\treturn nil\n}\n\n// VerifyBlockSignature verifies the signature on the block with the given BlockVerifier and the given config.\nfunc VerifyBlockSignature(block *common.Block, verifier protoutil.BlockVerifierFunc) error {\n\treturn verifier(block.Header, block.Metadata)\n}\n\n// EndpointCriteria defines criteria of how to connect to a remote orderer node.\ntype EndpointCriteria struct {\n\tEndpoint   string   // Endpoint of the form host:port\n\tTLSRootCAs [][]byte // PEM encoded TLS root CA certificates\n}\n\n// String returns a string representation of this EndpointCriteria","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/cluster/util.go#L237-L273","documentation":"VerifyBlockHash verifies the hash linkage of the chain: each block's Header.PreviousHash must equal the SHA-256 hash of the previous block's header (protoutil.BlockHeaderHash). This error is returned when the current block claims a previous-hash that does not match the hash actually computed from the previous buffered block, meaning the two blocks do not belong to the same hash chain. This is the core tamper/fork detection of the replication path.","triggerScenarios":"Calling cluster.VerifyBlockHash(indexInBuffer, blockBuff) where bytes.Equal(block.Header.PreviousHash, protoutil.BlockHeaderHash(prevBlock.Header)) is false — the PreviousHash field of the current block disagrees with the computed hash of the preceding block.","commonSituations":"Blocks pulled from a Byzantine or forked orderer; a ledger directory that mixes blocks from different chains/forks (e.g., after a manual copy or a restore from a mismatched backup); a different network/channel's block injected into the buffer; corrupted storage flipping bytes in a header.","solutions":["Compare the hex hashes in the error against a known-good orderer's ledger to identify which block diverged.","Re-pull the divergent block (and subsequent blocks) from a trusted orderer via BlockPuller and overwrite the local copies.","If the local ledger is corrupted, stop the node and resync the chain (restore from backup taken at a consistent height or re-join the channel).","Verify all orderers run the same Fabric version and belong to the same consenter set; a fork indicates a TLS/endpoint configuration pulling from the wrong cluster.","Never manually edit or copy individual block files into the ledger directory."],"exampleFix":"// before\nbuffer := []*common.Block{blockFromOrdererA, blockFromOrdererB} // different forks\nerr := cluster.VerifyBlockHash(1, buffer) // hash mismatch\n// after\nbuffer := []*common.Block{blockFromOrdererA}\nnext, err := trustedPuller.PullBlock(blockFromOrdererA.Header.Number + 1) // same trusted source\nbuffer = append(buffer, next)\nerr = cluster.VerifyBlockHash(1, buffer)","handlingStrategy":"validation","validationCode":"func hashChainValid(buff []*common.Block) bool {\n    for i := 1; i < len(buff); i++ {\n        if !bytes.Equal(buff[i].Header.PreviousHash, protoutil.BlockHeaderHash(buff[i-1].Header)) {\n            return false\n        }\n    }\n    return true\n}\nif !hashChainValid(blockBuff) { return errors.New(\"pulled blocks do not form a hash chain; refusing to apply\") }","typeGuard":"func blockLinksTo(prev, curr *common.Block) bool {\n    return prev.Header != nil && curr.Header != nil &&\n        bytes.Equal(curr.Header.PreviousHash, protoutil.BlockHeaderHash(prev.Header))\n}","tryCatchPattern":"if err := cluster.VerifyBlockHash(idx, blockBuff); err != nil {\n    if strings.Contains(err.Error(), \"mismatches block\") {\n        // treat as integrity violation: halt, alert, re-pull from a trusted orderer\n        return ErrLedgerIntegrity\n    }\n    return err\n}","preventionTips":["Never manually copy or edit block files between ledgers","Pull blocks only from orderers in the channel's consenter set over verified TLS","Restore ledgers only from consistent full backups of the same chain height","Treat any hash-chain mismatch as a fork/integrity incident, not a transient error"],"tags":["blockchain","hyperledger-fabric","orderer","hash-mismatch","fork","tampering"],"backgroundTag":"block-hash-mismatch","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}