// +build linux,amd64 linux,arm64 darwin,amd64 darwin,arm64 windows,amd64 // +build !blst_disabled package blst import ( "fmt" "github.com/dgraph-io/ristretto" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/config/features" "github.com/prysmaticlabs/prysm/config/params" "github.com/prysmaticlabs/prysm/crypto/bls/common" ) var maxKeys = int64(1000000) var pubkeyCache, _ = ristretto.NewCache(&ristretto.Config{ NumCounters: maxKeys, MaxCost: 1 << 26, // ~64mb is cache max size BufferItems: 64, }) // PublicKey used in the BLS signature scheme. type PublicKey struct { p *blstPublicKey } // PublicKeyFromBytes creates a BLS public key from a BigEndian byte slice. func PublicKeyFromBytes(pubKey []byte) (common.PublicKey, error) { if features.Get().SkipBLSVerify { return &PublicKey{}, nil } if len(pubKey) != params.BeaconConfig().BLSPubkeyLength { return nil, fmt.Errorf("public key must be %d bytes", params.BeaconConfig().BLSPubkeyLength) } if cv, ok := pubkeyCache.Get(string(pubKey)); ok { return cv.(*PublicKey).Copy(), nil } // Subgroup check NOT done when decompressing pubkey. p := new(blstPublicKey).Uncompress(pubKey) if p == nil { return nil, errors.New("could not unmarshal bytes into public key") } // Subgroup and infinity check if !p.KeyValidate() { // NOTE: the error is not quite accurate since it includes group check return nil, common.ErrInfinitePubKey } pubKeyObj := &PublicKey{p: p} copiedKey := pubKeyObj.Copy() pubkeyCache.Set(string(pubKey), copiedKey, 48) return pubKeyObj, nil } // AggregatePublicKeys aggregates the provided raw public keys into a single key. func AggregatePublicKeys(pubs [][]byte) (common.PublicKey, error) { if features.Get().SkipBLSVerify { return &PublicKey{}, nil } if len(pubs) == 0 { return nil, errors.New("nil or empty public keys") } agg := new(blstAggregatePublicKey) mulP1 := make([]*blstPublicKey, 0, len(pubs)) for _, pubkey := range pubs { pubKeyObj, err := PublicKeyFromBytes(pubkey) if err != nil { return nil, err } mulP1 = append(mulP1, pubKeyObj.(*PublicKey).p) } // No group check needed here since it is done in PublicKeyFromBytes // Note the checks could be moved from PublicKeyFromBytes into Aggregate // and take advantage of multi-threading. agg.Aggregate(mulP1, false) return &PublicKey{p: agg.ToAffine()}, nil } // Marshal a public key into a LittleEndian byte slice. func (p *PublicKey) Marshal() []byte { return p.p.Compress() } // Copy the public key to a new pointer reference. func (p *PublicKey) Copy() common.PublicKey { np := *p.p return &PublicKey{p: &np} } // IsInfinite checks if the public key is infinite. func (p *PublicKey) IsInfinite() bool { zeroKey := new(blstPublicKey) return p.p.Equals(zeroKey) } // Aggregate two public keys. func (p *PublicKey) Aggregate(p2 common.PublicKey) common.PublicKey { if features.Get().SkipBLSVerify { return p } agg := new(blstAggregatePublicKey) // No group check here since it is checked at decompression time agg.Add(p.p, false) agg.Add(p2.(*PublicKey).p, false) p.p = agg.ToAffine() return p }