canopy-network/canopy · error

invalid bitmap index

Error message

invalid bitmap index

What it means

Raised in BLS12381MultiPublicKey.SignerEnabledAt when the bitmap index argument is out of range for the number of public keys in the multisig key (index < 0 or >= key count). The bitmap bitfield only has one bit per signer, so querying a bit beyond that is invalid input rather than a missing signer.

Source

Thrown at lib/crypto/bls.go:507

// It must not be used for signer-indexed bitmap semantics.
func (b *BLS12381MultiPublicKey) PubKeys() [][]byte {
	var pubs [][]byte
	for _, k := range b.PublicKeys() {
		pubs = append(pubs, k.Bytes())
	}
	sort.Slice(pubs, func(i, j int) bool {
		return bytes.Compare(pubs[i], pubs[j]) < 0
	})
	return pubs
}

// Bitmap() returns a bitfield where each bit represents the signing status of a specific signer
// in the public key list. A set bit (1) indicates the signer at that index signed, while a cleared bit (0)
// indicates they did not
func (b *BLS12381MultiPublicKey) Bitmap() []byte { return b.mask.Mask() }
func (b *BLS12381MultiPublicKey) SignerEnabledAt(i int) (bool, error) {
	if i > len(b.PublicKeys()) || i < 0 {
		return false, errors.New("invalid bitmap index")
	}
	mask := b.Bitmap()
	byteIndex := i / 8
	mm := byte(1) << (i & 7)
	return mask[byteIndex]&mm != 0, nil
}

// SetBitmap() is used to set the mask of a BLS Multi key
func (b *BLS12381MultiPublicKey) SetBitmap(bm []byte) error { return b.mask.SetMask(bm) }

// EnabledSignerCount returns the number of enabled signers in the bitmap.
func (b *BLS12381MultiPublicKey) EnabledSignerCount() int { return b.mask.CountEnabled() }

// Threshold returns the minimum enabled signers required for this multisig policy.
func (b *BLS12381MultiPublicKey) Threshold() uint32 { return b.threshold }
func newBLSScheme() *bdn.Scheme                     { return bdn.NewSchemeOnG2(newBLSSuite()) }
func newBLSSuite() pairing.Suite                    { return bls12381.NewBLS12381Suite() }

View on GitHub (pinned to ee8197d91d)

Solutions

  1. Loop with i < len(mpk.PublicKeys()) when querying each signer
  2. Clamp or bounds-check i before calling
  3. Handle the returned error instead of indexing blindly

Example fix

// before
for i := 0; i <= n; i++ { mpk.SignerEnabledAt(i) }
// after
for i := 0; i < n; i++ { mpk.SignerEnabledAt(i) }
Defensive patterns

Strategy: validation

Validate before calling

func safeSignerEnabled(mpk *crypto.BLS12381MultiPublicKey, i int) (bool, error) {
	if i < 0 || i >= len(mpk.PublicKeys()) { return false, errors.New("index out of range") }
	return mpk.SignerEnabledAt(i)
}

Type guard

func validSignerIndex(mpk *crypto.BLS12381MultiPublicKey, i int) bool {
	return i >= 0 && i < len(mpk.PublicKeys())
}

Try / catch

enabled, err := mpk.SignerEnabledAt(i)
if err != nil {
	return false, fmt.Errorf("signer %d: %w", i, err)
}

Prevention

When it happens

Trigger: Calling SignerEnabledAt(-1), SignerEnabledAt(len(PublicKeys())), or any i beyond the signer list.

Common situations: Iterating with <= instead of < over signer count; stale code assuming a fixed signer count; bitmap/signer-count mismatch after key rotation.

Related errors


AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06). Data as JSON: /api/errors/82b890541ad14324. Report an issue: GitHub.