{"record":{"id":"5c7122c66cc6e93c","repo":"canopy-network/canopy","slug":"account-auth-multisig-requires-threshold-0","errorCode":null,"errorMessage":"account-auth multisig requires threshold > 0","messagePattern":"account-auth multisig requires threshold > 0","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/crypto/bls.go","lineNumber":267,"sourceCode":"// String() returns the hex string representation of the public key\nfunc (b *BLS12381PublicKey) String() string {\n\treturn hex.EncodeToString(b.Bytes())\n}\n\nvar _ MultiPublicKeyI = &BLS12381MultiPublicKey{}\n\n// BLS12381MultiPublicKey is an aggregated public key created by combining multiple BLS public keys from different signers.\n// This type intentionally exposes two representations:\n//   - Address(): canonical multisig account identity for the signer set\n//   - Bytes()/Bitmap()/AddSigner(): order-preserving verification state where signer indices are meaningful\ntype BLS12381MultiPublicKey struct {\n\tsignatures [][]byte\n\tmask       *sign.Mask\n\tscheme     *bdn.Scheme\n\tthreshold  uint32\n}\n\nvar errAccountAuthThreshold = errors.New(\"account-auth multisig requires threshold > 0\")\n\n// NewBLSMultiPublicKey() creates a new BLS12381MultiPublicKey reference from a kyber mask object\nfunc newBLSMultiPublicKey(mask *sign.Mask, threshold uint32) *BLS12381MultiPublicKey {\n\treturn &BLS12381MultiPublicKey{mask: mask, scheme: newBLSScheme(), signatures: make([][]byte, len(mask.Publics())), threshold: threshold}\n}\n\n// NewMultiBLSFromPoints() creates a multi public key from a list of G1 points on a BLS12381 curve.\n// Important: this preserves caller order for backwards compatibility. Bitmap indices and AddSigner() indices refer to\n// this exact order, so this constructor must not canonicalize or sort the signer list.\nfunc NewMultiBLSFromPoints(publicKeys []kyber.Point, bitmap []byte) (MultiPublicKeyI, error) {\n\tmask, err := sign.NewMask(newBLSSuite(), publicKeys, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif bitmap != nil {\n\t\tif err = mask.SetMask(bitmap); err != nil {\n\t\t\treturn nil, err\n\t\t}","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/lib/crypto/bls.go#L249-L285","documentation":"errAccountAuthThreshold is thrown when a BLS multisig account-auth key is constructed with a threshold of 0. A multisig with threshold 0 would allow zero signatures to authorize a transaction, which is meaningless and unsafe, so NewAccountAuthMultiBLSFromPoints and NewAccountAuthMultiBLSFromPublicKey reject it up front.","triggerScenarios":"Calling NewAccountAuthMultiBLSFromPoints(publicKeys, bitmap, 0), or NewAccountAuthMultiBLSFromPublicKey with serialized bytes whose decoded MultiPublicKey has threshold == 0.","commonSituations":"Threshold field left at its zero value in config/structs, deserializing keys produced by older library versions that did not persist a threshold, or copying constructors without setting the threshold parameter.","solutions":["Pass a threshold >= 1 (and <= number of public keys) when calling NewAccountAuthMultiBLSFromPoints","Set the threshold field on the serialized MultiPublicKey before NewMultiBLSFromPublicKey/NewAccountAuthMultiBLSFromPublicKey","Validate/fix stored key material and re-serialize it with a correct threshold"],"exampleFix":"// before\nkey, _ := crypto.NewAccountAuthMultiBLSFromPoints(points, bitmap, 0)\n// after\nthreshold := uint32(2)\nif threshold == 0 || threshold > uint32(len(points)) { threshold = uint32(len(points)) }\nkey, _ := crypto.NewAccountAuthMultiBLSFromPoints(points, bitmap, threshold)","handlingStrategy":"validation","validationCode":"if threshold == 0 || threshold > uint32(len(publicKeys)) {\n\treturn errors.New(\"threshold must be in [1, len(publicKeys)]\")\n}\nkey, err := crypto.NewAccountAuthMultiBLSFromPoints(publicKeys, bitmap, threshold)","typeGuard":null,"tryCatchPattern":"key, err := crypto.NewAccountAuthMultiBLSFromPoints(points, bitmap, threshold)\nif err != nil {\n\treturn fmt.Errorf(\"multisig construction: %w\", err)\n}","preventionTips":["Always derive threshold from config with a default > 0","When deserializing keys, verify the decoded threshold before using them in account auth","Add unit tests asserting constructors reject threshold 0"],"tags":["bls","multisig","validation"],"backgroundTag":"invalid-constructor-argument","analyzedSha":"ee8197d91dd410f6592cb650a94c925ee6dc8bad","analyzedAt":"2026-09-06T09:30:15.973Z","contentChangedAt":"2026-09-06T09:30:15.973Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}