prysm-pulse/crypto/bls/signature_batch.go
Nishant Das e5e4dee629
Update Batch Verification Routine (#10127)
* add changes so far

* a lot of tests

* add flags and comments

* raul's comment

* raul's review

* fix build

* fix

* fix

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-05-04 04:47:53 +00:00

149 lines
4.4 KiB
Go

package bls
import "github.com/pkg/errors"
// SignatureBatch refers to the defined set of
// signatures and its respective public keys and
// messages required to verify it.
type SignatureBatch struct {
Signatures [][]byte
PublicKeys []PublicKey
Messages [][32]byte
}
// NewSet constructs an empty signature batch object.
func NewSet() *SignatureBatch {
return &SignatureBatch{
Signatures: [][]byte{},
PublicKeys: []PublicKey{},
Messages: [][32]byte{},
}
}
// Join merges the provided signature batch to out current one.
func (s *SignatureBatch) Join(set *SignatureBatch) *SignatureBatch {
s.Signatures = append(s.Signatures, set.Signatures...)
s.PublicKeys = append(s.PublicKeys, set.PublicKeys...)
s.Messages = append(s.Messages, set.Messages...)
return s
}
// Verify the current signature batch using the batch verify algorithm.
func (s *SignatureBatch) Verify() (bool, error) {
return VerifyMultipleSignatures(s.Signatures, s.Messages, s.PublicKeys)
}
// Copy the attached signature batch and return it
// to the caller.
func (s *SignatureBatch) Copy() *SignatureBatch {
signatures := make([][]byte, len(s.Signatures))
pubkeys := make([]PublicKey, len(s.PublicKeys))
messages := make([][32]byte, len(s.Messages))
for i := range s.Signatures {
sig := make([]byte, len(s.Signatures[i]))
copy(sig, s.Signatures[i])
signatures[i] = sig
}
for i := range s.PublicKeys {
pubkeys[i] = s.PublicKeys[i].Copy()
}
for i := range s.Messages {
copy(messages[i][:], s.Messages[i][:])
}
return &SignatureBatch{
Signatures: signatures,
PublicKeys: pubkeys,
Messages: messages,
}
}
// RemoveDuplicates removes duplicate signature sets from the signature batch.
func (s *SignatureBatch) RemoveDuplicates() (int, *SignatureBatch, error) {
if len(s.Signatures) == 0 || len(s.PublicKeys) == 0 || len(s.Messages) == 0 {
return 0, s, nil
}
if len(s.Signatures) != len(s.PublicKeys) || len(s.Signatures) != len(s.Messages) {
return 0, s, errors.Errorf("mismatch number of signatures, publickeys and messages in signature batch. "+
"Signatures %d, Public Keys %d , Messages %d", s.Signatures, s.PublicKeys, s.Messages)
}
sigMap := make(map[string]int)
duplicateSet := make(map[int]bool)
for i := 0; i < len(s.Signatures); i++ {
if sigIdx, ok := sigMap[string(s.Signatures[i])]; ok {
if s.PublicKeys[sigIdx].Equals(s.PublicKeys[i]) &&
s.Messages[sigIdx] == s.Messages[i] {
duplicateSet[i] = true
continue
}
}
sigMap[string(s.Signatures[i])] = i
}
sigs := s.Signatures[:0]
pubs := s.PublicKeys[:0]
msgs := s.Messages[:0]
for i := 0; i < len(s.Signatures); i++ {
if duplicateSet[i] {
continue
}
sigs = append(sigs, s.Signatures[i])
pubs = append(pubs, s.PublicKeys[i])
msgs = append(msgs, s.Messages[i])
}
s.Signatures = sigs
s.PublicKeys = pubs
s.Messages = msgs
return len(duplicateSet), s, nil
}
// AggregateBatch aggregates common messages in the provided batch to
// reduce the number of pairings required when we finally verify the
// whole batch.
func (s *SignatureBatch) AggregateBatch() (*SignatureBatch, error) {
if len(s.Signatures) == 0 || len(s.PublicKeys) == 0 || len(s.Messages) == 0 {
return s, nil
}
if len(s.Signatures) != len(s.PublicKeys) || len(s.Signatures) != len(s.Messages) {
return s, errors.Errorf("mismatch number of signatures, publickeys and messages in signature batch. "+
"Signatures %d, Public Keys %d , Messages %d", s.Signatures, s.PublicKeys, s.Messages)
}
msgMap := make(map[[32]byte]*SignatureBatch)
for i := 0; i < len(s.Messages); i++ {
currMsg := s.Messages[i]
currBatch, ok := msgMap[currMsg]
if ok {
currBatch.Signatures = append(currBatch.Signatures, s.Signatures[i])
currBatch.Messages = append(currBatch.Messages, s.Messages[i])
currBatch.PublicKeys = append(currBatch.PublicKeys, s.PublicKeys[i])
continue
}
currBatch = &SignatureBatch{
Signatures: [][]byte{s.Signatures[i]},
Messages: [][32]byte{s.Messages[i]},
PublicKeys: []PublicKey{s.PublicKeys[i]},
}
msgMap[currMsg] = currBatch
}
newSt := NewSet()
for rt, b := range msgMap {
if len(b.PublicKeys) > 1 {
aggPub := AggregateMultiplePubkeys(b.PublicKeys)
aggSig, err := AggregateCompressedSignatures(b.Signatures)
if err != nil {
return nil, err
}
copiedRt := rt
b.PublicKeys = []PublicKey{aggPub}
b.Signatures = [][]byte{aggSig.Marshal()}
b.Messages = [][32]byte{copiedRt}
}
newObj := *b
newSt = newSt.Join(&newObj)
}
return newSt, nil
}