{"record":{"id":"b26eac574773a20e","repo":"hyperledger/fabric","slug":"signature-s-ordererblockmetadata-and-ordererblockm","errorCode":null,"errorMessage":"signature's OrdererBlockMetadata and OrdererBlockMetadata extracted from block do not match","messagePattern":"signature's OrdererBlockMetadata and OrdererBlockMetadata extracted from block do not match","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/smartbft/verifier.go","lineNumber":438,"sourceCode":"\t// Ensure Metadata slice is of the right size\n\tif len(block.Metadata.Metadata) != len(cb.BlockMetadataIndex_name) {\n\t\treturn errors.Errorf(\"block metadata is of size %d but should be of size %d\",\n\t\t\tlen(block.Metadata.Metadata), len(cb.BlockMetadataIndex_name))\n\t}\n\n\tsignatureMetadata := &cb.Metadata{}\n\tif err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], signatureMetadata); err != nil {\n\t\treturn errors.Wrap(err, \"malformed signature metadata\")\n\t}\n\n\tordererMDFromBlock := &cb.OrdererBlockMetadata{}\n\tif err := proto.Unmarshal(signatureMetadata.Value, ordererMDFromBlock); err != nil {\n\t\treturn errors.Wrap(err, \"malformed orderer metadata in block\")\n\t}\n\n\t// Ensure the block's OrdererBlockMetadata matches the signature.\n\tif !proto.Equal(ordererMDFromBlock, ordererMD) {\n\t\treturn errors.Errorf(\"signature's OrdererBlockMetadata and OrdererBlockMetadata extracted from block do not match\")\n\t}\n\n\treturn nil\n}\n\ntype consenterVerifier struct {\n\tlogger        *flogging.FabricLogger\n\tchannel       string\n\tpolicyManager policies.Manager\n}\n\n// Evaluate evaluates signed data and returns no error if signature is valid and satisfies the policy\nfunc (cv *consenterVerifier) Evaluate(signatureSet []*protoutil.SignedData) error {\n\tpolicy, ok := cv.policyManager.GetPolicy(policies.ChannelOrdererWriters)\n\tif !ok {\n\t\tcv.logger.Errorf(\"[%s] Error: could not find policy %s in policy manager %v\", cv.channel, policies.ChannelOrdererWriters, cv.policyManager)\n\t\treturn errors.Errorf(\"could not find policy %s\", policies.ChannelOrdererWriters)\n\t}","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/smartbft/verifier.go#L420-L456","documentation":"The OrdererBlockMetadata decoded from the block's SIGNATURES metadata is not identical (proto.Equal) to the OrdererBlockMetadata carried in the signature being verified. This means the signature is not bound to this block's metadata, so the block fails verification. It is an integrity/consistency check in the SmartBFT verifier.","triggerScenarios":"VerifyConsenterSig is given a signature whose attached OrdererBlockMetadata differs from the one embedded in the block — e.g. signature copied from a different block, or block metadata was altered after signing.","commonSituations":"Replay of signatures across blocks during Byzantine/malicious activity or buggy custom code; blocks assembled by hand in tests; mixing blocks from different channels/heights.","solutions":["Use the signature produced for exactly this block (same seqNo and metadata); do not copy signatures across blocks","Re-fetch the block and its signature bundle from the consensus/ledger source","Check that no middleware or tooling rewrites block metadata after signing","If seen repeatedly from one consenter, investigate that node for buggy or malicious behavior"],"exampleFix":"// before: signature taken from a cached map keyed only by height\nsig := sigCache[block.Header.Number]\n// after: key by block header hash / proposal digest\nsig := sigCache[hex.EncodeToString(protoutil.BlockHeaderHash(block.Header))]","handlingStrategy":"try-catch","validationCode":"// verify binding yourself before calling the API\nsigMD, _ := protoutil.GetMetadataFromBlock(blk, cb.BlockMetadataIndex_SIGNATURES)\nobm := &cb.OrdererBlockMetadata{}\nif err := proto.Unmarshal(sigMD.Value, obm); err != nil { return err }\nif !proto.Equal(obm, expectedOrdererMD) {\n    return errors.New(\"signature not bound to this block's metadata\")\n}","typeGuard":"func signatureBoundToBlock(sigOrdererMD, blkOrdererMD *cb.OrdererBlockMetadata) bool {\n    return proto.Equal(sigOrdererMD, blkOrdererMD)\n}","tryCatchPattern":"if err := verifier.VerifyConsenterSig(blk, sig); err != nil {\n    if strings.Contains(err.Error(), \"do not match\") {\n        log.Warnf(\"signature/block binding failed for block %d; re-fetching block\", blk.Header.Number)\n        return reFetchBlockAndSignature(blk.Header.Number)\n    }\n    return err\n}","preventionTips":["Never reuse signatures across blocks; key signature caches by block hash/proposal digest","Treat repeated mismatches from one consenter as a security signal and investigate","Avoid post-signing mutations of block metadata in tooling","Re-fetch blocks/signatures from consensus rather than repairing locally"],"tags":["smartbft","consensus","integrity","signature-verification"],"backgroundTag":"signature-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"}