Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
89981d0
Jul 4, 2021
a87eb20
Payset module now enqueue transactions into the batch verifier
Jul 5, 2021
96704f5
Jul 5, 2021
b82a9fa
Jul 7, 2021
c6c1fd4
Merge branch 'algorand:master' into add-batch-verification
id-ms Jul 7, 2021
2571f7b
Merge branch 'algorand:master' into add-batch-verification
id-ms Jul 11, 2021
3d1a102
Merge branch 'algorand:master' into add-batch-verification
id-ms Jul 14, 2021
4ddff84
Merge branch 'algorand:master' into add-batch-verification
id-ms Jul 18, 2021
ca33235
fixing lint
Jul 18, 2021
2483487
Merge branch 'add-batch-verification' of github.com:algoidan/go-algor…
Jul 18, 2021
3997626
adding more mutlisig tests
Jul 18, 2021
978bc0c
Merge branch 'algorand:master' into add-batch-verification
id-ms Jul 20, 2021
b39af7d
fixing some CRs + using the batchverifier for every txn check
Jul 22, 2021
7bcbaee
optimizing the errors
Jul 22, 2021
e97e7ae
Merge branch 'algorand:master' into add-batch-verification
id-ms Jul 22, 2021
76d1979
Merge branch 'add-batch-verification' of github.com:algoidan/go-algor…
Jul 22, 2021
b49ffc1
Jul 22, 2021
06baf86
Merge branch 'algorand:master' into add-batch-verification
id-ms Jul 27, 2021
9cfdc96
Merge branch 'algorand:master' into add-batch-verification
id-ms Jul 29, 2021
a3b2930
Merge branch 'algorand:master' into add-batch-verification
id-ms Aug 2, 2021
9aca551
Merge branch 'algorand:master' into add-batch-verification
id-ms Aug 4, 2021
65d9f57
fixing errors according to convention. + fix typo
Aug 5, 2021
437ca32
Merge branch 'algorand:master' into add-batch-verification
id-ms Aug 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions crypto/batchverifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (C) 2019-2021 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.

package crypto

import "errors"

// BatchVerifier enqueues signatures to be validated in batch.
type BatchVerifier struct {
messages []Hashable // contains a slice of messages to be hashed. Each message is varible length
publicKeys []SignatureVerifier // contains a slice of public keys. Each individual public key is 32 bytes.
signatures []Signature // contains a slice of signatures keys. Each individual signature is 64 bytes.
}

const minBatchVerifierAlloc = 16

// Batch verifications errors
var (
ErrBatchVerificationFailed = errors.New("At least on signature didn't pass verification")
ErrZeroTranscationsInBatch = errors.New("Could not validate empty signature set")
)

// MakeBatchVerifierDefaultSize create a BatchVerifier instance. This function pre-allocates
// amount of free space to enqueue signatures without exapneding
func MakeBatchVerifierDefaultSize() *BatchVerifier {
return MakeBatchVerifier(minBatchVerifierAlloc)
}

// MakeBatchVerifier create a BatchVerifier instance. This function pre-allocates
// a given space so it will not expaned the storage
func MakeBatchVerifier(hint int) *BatchVerifier {
// preallocate enough storage for the expected usage. We will reallocate as needed.
if hint < minBatchVerifierAlloc {
hint = minBatchVerifierAlloc
}
return &BatchVerifier{
messages: make([]Hashable, 0, hint),
publicKeys: make([]SignatureVerifier, 0, hint),
signatures: make([]Signature, 0, hint),
}
}

// EnqueueSignature enqueues a signature to be enqueued
func (b *BatchVerifier) EnqueueSignature(sigVerifier SignatureVerifier, message Hashable, sig Signature) {
// do we need to reallocate ?
if len(b.messages) == cap(b.messages) {
b.expand()
}
b.messages = append(b.messages, message)
b.publicKeys = append(b.publicKeys, sigVerifier)
b.signatures = append(b.signatures, sig)
}

func (b *BatchVerifier) expand() {
messages := make([]Hashable, len(b.messages), len(b.messages)*2)
publicKeys := make([]SignatureVerifier, len(b.publicKeys), len(b.publicKeys)*2)
signatures := make([]Signature, len(b.signatures), len(b.signatures)*2)
copy(messages, b.messages)
copy(publicKeys, b.publicKeys)
copy(signatures, b.signatures)
b.messages = messages
b.publicKeys = publicKeys
b.signatures = signatures
}

// GetNumberOfEnqueuedSignatures returns the number of signatures current enqueue onto the bacth verifier object
func (b *BatchVerifier) GetNumberOfEnqueuedSignatures() int {
return len(b.messages)
}

// Verify verifies that all the signatures are valid. in that case nil is returned
// if the batch is zero an appropriate error is return.
func (b *BatchVerifier) Verify() error {
if b.GetNumberOfEnqueuedSignatures() == 0 {
return ErrZeroTranscationsInBatch
}

for i := range b.messages {
verifier := SignatureVerifier(b.publicKeys[i])
if !verifier.Verify(b.messages[i], b.signatures[i]) {
return ErrBatchVerificationFailed
}
}
return nil
}
119 changes: 119 additions & 0 deletions crypto/batchverifier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright (C) 2019-2021 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.

package crypto

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestBatchVerifierSingle(t *testing.T) {
// test expected success
bv := MakeBatchVerifier(1)
msg := randString()
var s Seed
RandBytes(s[:])
sigSecrets := GenerateSignatureSecrets(s)
sig := sigSecrets.Sign(msg)
bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig)
require.NoError(t, bv.Verify())

// test expected failuire
bv = MakeBatchVerifier(1)
msg = randString()
RandBytes(s[:])
sigSecrets = GenerateSignatureSecrets(s)
sig = sigSecrets.Sign(msg)
// break the signature:
sig[0] = sig[0] + 1
bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig)
require.Error(t, bv.Verify())
}

func TestBatchVerifierBulk(t *testing.T) {
for i := 1; i < 64*2+3; i++ {
n := i
bv := MakeBatchVerifier(n)
var s Seed

for i := 0; i < n; i++ {
msg := randString()
RandBytes(s[:])
sigSecrets := GenerateSignatureSecrets(s)
sig := sigSecrets.Sign(msg)
bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig)
}
require.Equal(t, n, bv.GetNumberOfEnqueuedSignatures())
require.NoError(t, bv.Verify())
}

}

func TestBatchVerifierBulkWithExpand(t *testing.T) {
n := 64
bv := MakeBatchVerifier(1)
var s Seed
RandBytes(s[:])

for i := 0; i < n; i++ {
msg := randString()
sigSecrets := GenerateSignatureSecrets(s)
sig := sigSecrets.Sign(msg)
bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig)
}
require.NoError(t, bv.Verify())
}

func TestBatchVerifierWithInvalidSiganture(t *testing.T) {
n := 64
bv := MakeBatchVerifier(1)
var s Seed
RandBytes(s[:])

for i := 0; i < n-1; i++ {
msg := randString()
sigSecrets := GenerateSignatureSecrets(s)
sig := sigSecrets.Sign(msg)
bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig)
}

msg := randString()
sigSecrets := GenerateSignatureSecrets(s)
sig := sigSecrets.Sign(msg)
sig[0] = sig[0] + 1
bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig)

require.Error(t, bv.Verify())
}

func BenchmarkBatchVerifier(b *testing.B) {
c := makeCurve25519Secret()
bv := MakeBatchVerifier(1)
for i := 0; i < b.N; i++ {
str := randString()
bv.EnqueueSignature(c.SignatureVerifier, str, c.Sign(str))
}

b.ResetTimer()
require.NoError(b, bv.Verify())
}

func TestEmpty(t *testing.T) {
bv := MakeBatchVerifierDefaultSize()
require.Error(t, bv.Verify())
}
19 changes: 11 additions & 8 deletions crypto/cryptoerror.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ package crypto

import "errors"

const errorinvalidversion = "Invalid version"
const errorinvalidaddress = "Invalid address"
const errorinvalidthreshold = "Invalid threshold"
const errorinvalidnumberofsignature = "Invalid number of signatures"
const errorkeynotexist = "Key does not exist"
const errorsubsigverification = "Verification failure: subsignature"
const errorkeysnotmatch = "Public key lists do not match"
const errorinvalidduplicates = "Invalid duplicates"
var (
errInvalidVersion = errors.New("Invalid version")
errInvalidAddress = errors.New("Invalid address")
errInvalidThreshold = errors.New("Invalid threshold")
errInvalidNumberOfSignature = errors.New("Invalid number of signatures")
errKeyNotExist = errors.New("Key does not exist")
errSubsigVerification = errors.New("Verification failure: subsignature")
errKeysNotMatch = errors.New("Public key lists do not match")
errInvalidDuplicates = errors.New("Invalid duplicates")
errInvalidNumberOfSig = errors.New("invalid number of signatures to add")
)

var errUnknownVersion = errors.New("unknown version")
Loading