{"record":{"id":"784a3a5340778302","repo":"hyperledger/fabric","slug":"transaction-d-has-no-signature","errorCode":null,"errorMessage":"transaction %d has no signature","messagePattern":"transaction (.+?) has no signature","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"protoutil/blockutils.go","lineNumber":338,"sourceCode":"\n\t// If we have a single transaction, and the block is a config block, then no need to check\n\t// well formed-ness, because there cannot be another transaction in the original block.\n\tif HasConfigTx(bd) {\n\t\treturn nil\n\t}\n\n\tfor i, rawTx := range bd.Data {\n\t\tenv := &cb.Envelope{}\n\t\tif err := proto.Unmarshal(rawTx, env); err != nil {\n\t\t\treturn fmt.Errorf(\"transaction %d is invalid: %v\", i, err)\n\t\t}\n\n\t\tif len(env.Payload) == 0 {\n\t\t\treturn fmt.Errorf(\"transaction %d has no payload\", i)\n\t\t}\n\n\t\tif len(env.Signature) == 0 {\n\t\t\treturn fmt.Errorf(\"transaction %d has no signature\", i)\n\t\t}\n\n\t\texpected, err := proto.Marshal(env)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed re-marshaling envelope: %v\", err)\n\t\t}\n\n\t\tif len(expected) < len(rawTx) {\n\t\t\treturn fmt.Errorf(\"transaction %d has %d trailing bytes\", i, len(rawTx)-len(expected))\n\t\t}\n\t\tif !bytes.Equal(expected, rawTx) {\n\t\t\treturn fmt.Errorf(\"transaction %d (%s) does not match its raw form (%s)\", i,\n\t\t\t\tbase64.StdEncoding.EncodeToString(expected), base64.StdEncoding.EncodeToString(rawTx))\n\t\t}\n\t}\n\n\treturn nil\n}","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/protoutil/blockutils.go#L320-L356","documentation":"VerifyTransactionsAreWellFormed requires every envelope in a block to carry a signature. If proto.Unmarshal succeeded but env.Signature is empty, the transaction is unsigned and cannot be authenticated, so the function rejects it. This mirrors the payload check and enforces the Envelope invariant that both Payload and Signature must be set.","triggerScenarios":"BlockDataHash encounters, at transaction index i, an Envelope whose Signature byte slice is empty — typically an envelope constructed programmatically without calling Sign, or one whose signature was stripped.","commonSituations":"Test fixtures building envelopes with only Payload set; code that re-marshals envelopes and drops the Signature field; envelopes deserialized from a truncated/corrupted block; forgetting env.Signature = signer.Sign(payload).","solutions":["Ensure the envelope was signed before marshaling: set env.Signature from the signer over the serialized Payload.","Verify the block source — if a peer produced this block, compare with other replicas to determine if data is corrupt.","In test code, use protoutil helpers that build signed envelopes rather than hand-constructing &cb.Envelope{}.","Reject unsigned envelopes at ingestion before they ever reach a block."],"exampleFix":"// before\nenv := &cb.Envelope{Payload: payloadBytes}\n// after\nsig, err := signer.Sign(payloadBytes)\nif err != nil { return err }\nenv := &cb.Envelope{Payload: payloadBytes, Signature: sig}","handlingStrategy":"validation","validationCode":"env := &cb.Envelope{}\nif err := proto.Unmarshal(rawTx, env); err != nil { return err }\nif len(env.Signature) == 0 {\n\treturn fmt.Errorf(\"tx %d: unsigned envelope\", i)\n}","typeGuard":"func isSigned(env *cb.Envelope) bool { return env != nil && len(env.Signature) > 0 }","tryCatchPattern":null,"preventionTips":["Sign every envelope over its serialized Payload before marshaling.","Use msp/signing helpers instead of manual signature handling.","Never strip or overwrite env.Signature when transforming envelopes.","Test fixtures should use the same signing path as production code."],"tags":["hyperledger-fabric","protobuf","signature","block-validation"],"backgroundTag":"missing-signature","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"}