Skip to content

Commit 6c91f52

Browse files
authored
crypto: convert BatchVerifier to interface (#5988)
1 parent e07ad9c commit 6c91f52

File tree

6 files changed

+71
-24
lines changed

6 files changed

+71
-24
lines changed

crypto/batchverifier.go

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,20 @@ import (
5050
)
5151

5252
// BatchVerifier enqueues signatures to be validated in batch.
53-
type BatchVerifier struct {
53+
type BatchVerifier interface {
54+
EnqueueSignature(sigVerifier SignatureVerifier, message Hashable, sig Signature)
55+
GetNumberOfEnqueuedSignatures() int
56+
Verify() error
57+
VerifyWithFeedback() (failed []bool, err error)
58+
}
59+
60+
type cgoBatchVerifier struct {
5461
messages []Hashable // contains a slice of messages to be hashed. Each message is varible length
5562
publicKeys []SignatureVerifier // contains a slice of public keys. Each individual public key is 32 bytes.
5663
signatures []Signature // contains a slice of signatures keys. Each individual signature is 64 bytes.
64+
useSingle bool
5765
}
5866

59-
const minBatchVerifierAlloc = 16
60-
6167
// Batch verifications errors
6268
var (
6369
ErrBatchHasFailedSigs = errors.New("At least one signature didn't pass verification")
@@ -69,27 +75,31 @@ func ed25519_randombytes_unsafe(p unsafe.Pointer, len C.size_t) {
6975
RandBytes(randBuf)
7076
}
7177

72-
// MakeBatchVerifier creates a BatchVerifier instance.
73-
func MakeBatchVerifier() *BatchVerifier {
78+
const minBatchVerifierAlloc = 16
79+
const useSingleVerifierDefault = true
80+
81+
// MakeBatchVerifier creates a BatchVerifier instance with the provided options.
82+
func MakeBatchVerifier() BatchVerifier {
7483
return MakeBatchVerifierWithHint(minBatchVerifierAlloc)
7584
}
7685

77-
// MakeBatchVerifierWithHint creates a BatchVerifier instance. This function pre-allocates
86+
// MakeBatchVerifierWithHint creates a cgoBatchVerifier instance. This function pre-allocates
7887
// amount of free space to enqueue signatures without expanding
79-
func MakeBatchVerifierWithHint(hint int) *BatchVerifier {
88+
func MakeBatchVerifierWithHint(hint int) BatchVerifier {
8089
// preallocate enough storage for the expected usage. We will reallocate as needed.
8190
if hint < minBatchVerifierAlloc {
8291
hint = minBatchVerifierAlloc
8392
}
84-
return &BatchVerifier{
93+
return &cgoBatchVerifier{
8594
messages: make([]Hashable, 0, hint),
8695
publicKeys: make([]SignatureVerifier, 0, hint),
8796
signatures: make([]Signature, 0, hint),
97+
useSingle: useSingleVerifierDefault,
8898
}
8999
}
90100

91101
// EnqueueSignature enqueues a signature to be enqueued
92-
func (b *BatchVerifier) EnqueueSignature(sigVerifier SignatureVerifier, message Hashable, sig Signature) {
102+
func (b *cgoBatchVerifier) EnqueueSignature(sigVerifier SignatureVerifier, message Hashable, sig Signature) {
93103
// do we need to reallocate ?
94104
if len(b.messages) == cap(b.messages) {
95105
b.expand()
@@ -99,7 +109,7 @@ func (b *BatchVerifier) EnqueueSignature(sigVerifier SignatureVerifier, message
99109
b.signatures = append(b.signatures, sig)
100110
}
101111

102-
func (b *BatchVerifier) expand() {
112+
func (b *cgoBatchVerifier) expand() {
103113
messages := make([]Hashable, len(b.messages), len(b.messages)*2)
104114
publicKeys := make([]SignatureVerifier, len(b.publicKeys), len(b.publicKeys)*2)
105115
signatures := make([]Signature, len(b.signatures), len(b.signatures)*2)
@@ -112,12 +122,12 @@ func (b *BatchVerifier) expand() {
112122
}
113123

114124
// GetNumberOfEnqueuedSignatures returns the number of signatures currently enqueued into the BatchVerifier
115-
func (b *BatchVerifier) GetNumberOfEnqueuedSignatures() int {
125+
func (b *cgoBatchVerifier) GetNumberOfEnqueuedSignatures() int {
116126
return len(b.messages)
117127
}
118128

119129
// Verify verifies that all the signatures are valid. in that case nil is returned
120-
func (b *BatchVerifier) Verify() error {
130+
func (b *cgoBatchVerifier) Verify() error {
121131
_, err := b.VerifyWithFeedback()
122132
return err
123133
}
@@ -126,11 +136,15 @@ func (b *BatchVerifier) Verify() error {
126136
// if all sigs are valid, nil will be returned for err (failed will have all false)
127137
// if some signatures are invalid, true will be set in failed at the corresponding indexes, and
128138
// ErrBatchVerificationFailed for err
129-
func (b *BatchVerifier) VerifyWithFeedback() (failed []bool, err error) {
139+
func (b *cgoBatchVerifier) VerifyWithFeedback() (failed []bool, err error) {
130140
if len(b.messages) == 0 {
131141
return nil, nil
132142
}
133143

144+
if b.useSingle {
145+
return b.singleVerify()
146+
}
147+
134148
const estimatedMessageSize = 64
135149
msgLengths := make([]uint64, 0, len(b.messages))
136150
var messages = make([]byte, 0, len(b.messages)*estimatedMessageSize)
@@ -141,17 +155,33 @@ func (b *BatchVerifier) VerifyWithFeedback() (failed []bool, err error) {
141155
msgLengths = append(msgLengths, uint64(len(messages)-lenWas))
142156
lenWas = len(messages)
143157
}
144-
allValid, failed := batchVerificationImpl(messages, msgLengths, b.publicKeys, b.signatures)
158+
allValid, failed := cgoBatchVerificationImpl(messages, msgLengths, b.publicKeys, b.signatures)
145159
if allValid {
146160
return failed, nil
147161
}
148162
return failed, ErrBatchHasFailedSigs
149163
}
150164

151-
// batchVerificationImpl invokes the ed25519 batch verification algorithm.
165+
func (b *cgoBatchVerifier) singleVerify() (failed []bool, err error) {
166+
failed = make([]bool, len(b.messages))
167+
var containsFailed bool
168+
169+
for i := range b.messages {
170+
failed[i] = !ed25519Verify(ed25519PublicKey(b.publicKeys[i]), HashRep(b.messages[i]), ed25519Signature(b.signatures[i]))
171+
if failed[i] {
172+
containsFailed = true
173+
}
174+
}
175+
if containsFailed {
176+
return failed, ErrBatchHasFailedSigs
177+
}
178+
return failed, nil
179+
}
180+
181+
// cgoBatchVerificationImpl invokes the ed25519 batch verification algorithm.
152182
// it returns true if all the signatures were authentically signed by the owners
153183
// otherwise, returns false, and sets the indexes of the failed sigs in failed
154-
func batchVerificationImpl(messages []byte, msgLengths []uint64, publicKeys []SignatureVerifier, signatures []Signature) (allSigsValid bool, failed []bool) {
184+
func cgoBatchVerificationImpl(messages []byte, msgLengths []uint64, publicKeys []SignatureVerifier, signatures []Signature) (allSigsValid bool, failed []bool) {
155185

156186
numberOfSignatures := len(msgLengths)
157187
valid := make([]C.int, numberOfSignatures)

crypto/batchverifier_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func BenchmarkBatchVerifierBigWithInvalid(b *testing.B) {
171171
failed, err := bv.VerifyWithFeedback()
172172
if err != nil {
173173
for i, f := range failed {
174-
if bv.signatures[i] == badSig {
174+
if bv.(*cgoBatchVerifier).signatures[i] == badSig {
175175
require.True(b, f)
176176
} else {
177177
require.False(b, f)

crypto/multisig.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func MultisigVerify(msg Hashable, addr Digest, sig MultisigSig) (err error) {
240240

241241
// MultisigBatchPrep performs checks on the assembled MultisigSig and adds to the batch.
242242
// The caller must call batchVerifier.verify() to verify it.
243-
func MultisigBatchPrep(msg Hashable, addr Digest, sig MultisigSig, batchVerifier *BatchVerifier) error {
243+
func MultisigBatchPrep(msg Hashable, addr Digest, sig MultisigSig, batchVerifier BatchVerifier) error {
244244
// short circuit: if msig doesn't have subsigs or if Subsigs are empty
245245
// then terminate (the upper layer should now verify the unisig)
246246
if (len(sig.Subsigs) == 0 || sig.Subsigs[0] == MultisigSubsig{}) {

crypto/onetimesig.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,23 @@ func (v OneTimeSignatureVerifier) Verify(id OneTimeSignatureIdentifier, message
324324
Batch: id.Batch,
325325
}
326326

327+
if !useSingleVerifierDefault {
328+
return v.batchVerify(batchID, offsetID, message, sig)
329+
}
330+
331+
if !ed25519Verify(ed25519PublicKey(v), HashRep(batchID), sig.PK2Sig) {
332+
return false
333+
}
334+
if !ed25519Verify(batchID.SubKeyPK, HashRep(offsetID), sig.PK1Sig) {
335+
return false
336+
}
337+
if !ed25519Verify(offsetID.SubKeyPK, HashRep(message), sig.Sig) {
338+
return false
339+
}
340+
return true
341+
}
342+
343+
func (v OneTimeSignatureVerifier) batchVerify(batchID OneTimeSignatureSubkeyBatchID, offsetID OneTimeSignatureSubkeyOffsetID, message Hashable, sig OneTimeSignature) bool {
327344
// serialize encoded batchID, offsetID, message into a continuous memory buffer with the layout
328345
// hashRep(batchID)... hashRep(offsetID)... hashRep(message)...
329346
const estimatedSize = 256
@@ -336,7 +353,7 @@ func (v OneTimeSignatureVerifier) Verify(id OneTimeSignatureIdentifier, message
336353
messageBuffer = HashRepToBuff(message, messageBuffer)
337354
messageLen := uint64(len(messageBuffer)) - offsetIDLen - batchIDLen
338355
msgLengths := []uint64{batchIDLen, offsetIDLen, messageLen}
339-
allValid, _ := batchVerificationImpl(
356+
allValid, _ := cgoBatchVerificationImpl(
340357
messageBuffer,
341358
msgLengths,
342359
[]PublicKey{PublicKey(v), PublicKey(batchID.SubKeyPK), PublicKey(offsetID.SubKeyPK)},

data/transactions/verify/txn.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ func (g *GroupContext) Equal(other *GroupContext) bool {
163163
// txnBatchPrep verifies a SignedTxn having no obviously inconsistent data.
164164
// Block-assembly time checks of LogicSig and accounting rules may still block the txn.
165165
// It is the caller responsibility to call batchVerifier.Verify().
166-
func txnBatchPrep(gi int, groupCtx *GroupContext, verifier *crypto.BatchVerifier) *TxGroupError {
166+
func txnBatchPrep(gi int, groupCtx *GroupContext, verifier crypto.BatchVerifier) *TxGroupError {
167167
s := &groupCtx.signedGroupTxns[gi]
168168
if !groupCtx.consensusParams.SupportRekeying && (s.AuthAddr != basics.Address{}) {
169169
return &TxGroupError{err: errRekeyingNotSupported, GroupIndex: gi, Reason: TxGroupErrorReasonGeneric}
@@ -206,7 +206,7 @@ func txnGroup(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader
206206

207207
// txnGroupBatchPrep verifies a []SignedTxn having no obviously inconsistent data.
208208
// it is the caller responsibility to call batchVerifier.Verify()
209-
func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader, ledger logic.LedgerForSignature, verifier *crypto.BatchVerifier, evalTracer logic.EvalTracer) (*GroupContext, error) {
209+
func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader, ledger logic.LedgerForSignature, verifier crypto.BatchVerifier, evalTracer logic.EvalTracer) (*GroupContext, error) {
210210
groupCtx, err := PrepareGroupContext(stxs, contextHdr, ledger, evalTracer)
211211
if err != nil {
212212
return nil, err
@@ -287,7 +287,7 @@ func checkTxnSigTypeCounts(s *transactions.SignedTxn, groupIndex int) (sigType s
287287
}
288288

289289
// stxnCoreChecks runs signatures validity checks and enqueues signature into batchVerifier for verification.
290-
func stxnCoreChecks(gi int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier) *TxGroupError {
290+
func stxnCoreChecks(gi int, groupCtx *GroupContext, batchVerifier crypto.BatchVerifier) *TxGroupError {
291291
s := &groupCtx.signedGroupTxns[gi]
292292
sigType, err := checkTxnSigTypeCounts(s, gi)
293293
if err != nil {
@@ -340,7 +340,7 @@ func LogicSigSanityCheck(gi int, groupCtx *GroupContext) error {
340340
// logicSigSanityCheckBatchPrep checks that the signature is valid and that the program is basically well formed.
341341
// It does not evaluate the logic.
342342
// it is the caller responsibility to call batchVerifier.Verify()
343-
func logicSigSanityCheckBatchPrep(gi int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier) error {
343+
func logicSigSanityCheckBatchPrep(gi int, groupCtx *GroupContext, batchVerifier crypto.BatchVerifier) error {
344344
if groupCtx.consensusParams.LogicSigVersion == 0 {
345345
return errors.New("LogicSig not enabled")
346346
}

data/transactions/verify/txnBatch.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ func (tbp *txnSigBatchProcessor) ProcessBatch(txns []execpool.InputJob) {
170170
tbp.postProcessVerifiedJobs(ctx, failed, err)
171171
}
172172

173-
func (tbp *txnSigBatchProcessor) preProcessUnverifiedTxns(uTxns []execpool.InputJob) (batchVerifier *crypto.BatchVerifier, ctx interface{}) {
173+
func (tbp *txnSigBatchProcessor) preProcessUnverifiedTxns(uTxns []execpool.InputJob) (batchVerifier crypto.BatchVerifier, ctx interface{}) {
174174
batchVerifier = crypto.MakeBatchVerifier()
175175
bl := makeBatchLoad(len(uTxns))
176176
// TODO: separate operations here, and get the sig verification inside the LogicSig to the batch here

0 commit comments

Comments
 (0)