canopy-network/canopy · error

invalid public key

Error message

invalid public key

What it means

NewAccountAuthMultiBLSFromPoints returns "invalid public key" when the requested threshold exceeds the number of public keys in the constructed mask. Requiring more signers than exist would make the multisig impossible to satisfy, so the constructor rejects it.

Source

Thrown at lib/crypto/bls.go:307

// NewAccountAuthMultiBLSFromPoints creates a multisig public key intended for account authorization.
// Unlike the consensus-oriented constructor, this requires a positive threshold so callers cannot
// accidentally create an open multisig account.
func NewAccountAuthMultiBLSFromPoints(publicKeys []kyber.Point, bitmap []byte, threshold uint32) (MultiPublicKeyI, error) {
	if threshold == 0 {
		return nil, errAccountAuthThreshold
	}
	mask, err := sign.NewMask(newBLSSuite(), publicKeys, nil)
	if err != nil {
		return nil, err
	}
	if bitmap != nil {
		if err = mask.SetMask(bitmap); err != nil {
			return nil, err
		}
	}
	if threshold > uint32(len(mask.Publics())) {
		return nil, errors.New("invalid public key")
	}
	return newBLSMultiPublicKey(mask, threshold), nil
}

// NewMultiBLSFromPublicKey creates a BLS multikey from serialized bytes.
// The encoded public key order is preserved exactly so bitmap signer indices survive a Bytes()/decode roundtrip.
func NewMultiBLSFromPublicKey(publicKey []byte) (MultiPublicKeyI, error) {
	size, errInvalidPK := len(publicKey), errors.New("invalid public key")
	if size == 0 || size > 1_000_000 {
		return nil, errInvalidPK
	}
	// unmarshal into a multi-public-key
	mpk := new(MultiPublicKey)
	if err := proto.Unmarshal(publicKey, mpk); err != nil {
		return nil, err
	}
	// sanity check the size
	if len(mpk.PublicKeys) == 0 || len(mpk.Bitmap) == 0 || mpk.Threshold > uint32(len(mpk.PublicKeys)) {

View on GitHub (pinned to ee8197d91d)

Solutions

  1. Ensure threshold <= len(publicKeys) before calling
  2. Pass the full signer list, or lower the threshold to match the supplied keys
  3. Log/validate the signer set and threshold pair at config load time

Example fix

// before
mpk, _ := crypto.NewAccountAuthMultiBLSFromPoints(points, bitmap, 3) // len(points)==2
// after
threshold := uint32(3)
if threshold > uint32(len(points)) { threshold = uint32(len(points)) }
mpk, _ := crypto.NewAccountAuthMultiBLSFromPoints(points, bitmap, threshold)
Defensive patterns

Strategy: validation

Validate before calling

if threshold > uint32(len(publicKeys)) {
	return errors.New("threshold exceeds signer count")
}
mpk, err := crypto.NewAccountAuthMultiBLSFromPoints(publicKeys, bitmap, threshold)

Try / catch

mpk, err := crypto.NewAccountAuthMultiBLSFromPoints(points, bitmap, threshold)
if err != nil {
	return nil, fmt.Errorf("threshold %d vs %d signers: %w", threshold, len(points), err)
}

Prevention

When it happens

Trigger: NewAccountAuthMultiBLSFromPoints called with threshold > len(publicKeys), e.g. threshold 3 with only 2 keys supplied.

Common situations: Mismatch between a configured threshold value and the actual signer set passed in; trimming the key list during refactoring; loading a config where threshold and signer list come from different sources.

Related errors


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