hyperledger/fabric · error

mismatched block header

Error message

mismatched block header

What it means

In SmartBFT, each consenter signs over the proposal, and the signature records the block header it signed. VerifyConsenterSig -> verifySignatureIsBoundToProposal compares the proposal's header bytes with the header in the received signature (sig.BlockHeader). This error means the signature is not bound to the proposed block — a signature from another proposal/block is being applied to this one.

Source

Thrown at orderer/consensus/smartbft/verifier.go:388

	for indexInBlock := range blockData {
		res = append(res, indexToRequestInfo[indexInBlock])
	}

	return res, nil
}

func (v *Verifier) verifySignatureIsBoundToProposal(sig *Signature, identityID uint64, prop types.Proposal) error {
	// We verify the following fields:
	// ConsenterMetadata    []byte
	// SignatureHeader      []byte
	// BlockHeader          []byte
	// OrdererBlockMetadata []byte

	// Ensure block header is equal
	if !bytes.Equal(prop.Header, sig.BlockHeader) {
		v.Logger.Errorf("Expected block header %s but got %s", base64.StdEncoding.EncodeToString(prop.Header),
			base64.StdEncoding.EncodeToString(sig.BlockHeader))
		return errors.Errorf("mismatched block header")
	}

	// Ensure signature header matches the identity
	sigHdr := &cb.IdentifierHeader{}
	if err := proto.Unmarshal(sig.IdentifierHeader, sigHdr); err != nil {
		return errors.Wrap(err, "malformed signature header")
	}
	if identityID != uint64(sigHdr.Identifier) {
		v.Logger.Warnf("Expected identity %d but got %d", identityID,
			sigHdr.Identifier)
		return errors.Errorf("identity in signature header does not match expected identity")
	}

	// Ensure orderer block metadata's consenter MD matches the proposal
	ordererMD := &cb.OrdererBlockMetadata{}
	if err := proto.Unmarshal(sig.OrdererBlockMetadata, ordererMD); err != nil {
		return errors.Wrap(err, "malformed orderer metadata in signature")
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Discard/re-request the signature for the correct proposal; the bad signature bundle should be dropped, not retried as-is.
  2. Force a view change so a fresh proposal and matching signatures are collected.
  3. Check for replayed messages: ensure the node's clocks/sequence handling is correct and Fabric version is uniform across consenters.
  4. If it follows a restart, verify the persisted signature metadata corresponds to the same block height/view as the current proposal.

Example fix

// before: signature bound to a different block
prop.Header = "AAAA..."; sig.BlockHeader = "BBBB..." -> error
// after: collect a signature over the actual proposal
prop.Header = "AAAA..."; sig.BlockHeader = "AAAA..."
Defensive patterns

Strategy: try-catch

Validate before calling

if !bytes.Equal(sig.BlockHeader, prop.Header) {
    return errors.New("signature does not bind to this proposal; request a fresh signature")
}

Try / catch

err := verifier.VerifyConsenterSig(sig, identityID)
if err != nil && strings.Contains(err.Error(), "mismatched block header") {
    // drop the stale signature and re-request it from the consenter
}

Prevention

When it happens

Trigger: VerifyConsenterSig -> verifySignatureIsBoundToProposal: bytes.Equal(prop.Header, sig.BlockHeader) is false. Triggered when a VerificationData/signature bundle references a different proposal — e.g. message mix-up in the BFT message layer, a replayed signature from a previous block, or the signature was persisted/loaded (Sign/VerificationData round trip in verifier_assembler) against a different proposal.

Common situations: Network reordering or bugs mixing quorum messages across proposals; an attacker or faulty node replaying old signatures; process restart where signature metadata was persisted but the proposal changed; a node assembling a commit certificate from mismatched parts.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/ec11c178d25bd041. Report an issue: GitHub.