Skip to content

Commit

Permalink
Add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
algorandskiy committed Nov 15, 2022
1 parent f234c96 commit c6f47ac
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 20 deletions.
30 changes: 14 additions & 16 deletions data/transactions/verify/txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,16 @@ const (
TxGroupErrorReasonMsigNotWellFormed
// TxGroupErrorReasonLogicSigFailed defines logic sig validation errors
TxGroupErrorReasonLogicSigFailed

// TxGroupErrorReasonNumTracked is number of enum values
TxGroupErrorReasonNumValues
)

// ErrTxGroupError is an error from txn pre-validation (well form-ness, signature format, etc).
// It can be unwrapped into underlying error, as well as has a specific failure reason code.
type ErrTxGroupError struct {
err error
reason TxGroupErrorReason
Reason TxGroupErrorReason
}

// Error returns an error message from the underlying error
Expand All @@ -112,11 +115,6 @@ func (e *ErrTxGroupError) Unwrap() error {
return e.err
}

// Reason returns a reason code
func (e *ErrTxGroupError) Reason() TxGroupErrorReason {
return e.reason
}

// PrepareGroupContext prepares a verification group parameter object for a given transaction
// group.
func PrepareGroupContext(group []transactions.SignedTxn, contextHdr bookkeeping.BlockHeader, ledger logic.LedgerForSignature) (*GroupContext, error) {
Expand Down Expand Up @@ -152,11 +150,11 @@ func (g *GroupContext) Equal(other *GroupContext) bool {
// It is the caller responsibility to call batchVerifier.Verify().
func txnBatchPrep(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext, verifier *crypto.BatchVerifier) *ErrTxGroupError {
if !groupCtx.consensusParams.SupportRekeying && (s.AuthAddr != basics.Address{}) {
return &ErrTxGroupError{err: errRekeyingNotSupported, reason: TxGroupErrorReasonGeneric}
return &ErrTxGroupError{err: errRekeyingNotSupported, Reason: TxGroupErrorReasonGeneric}
}

if err := s.Txn.WellFormed(groupCtx.specAddrs, groupCtx.consensusParams); err != nil {
return &ErrTxGroupError{err: err, reason: TxGroupErrorReasonNotWellFormed}
return &ErrTxGroupError{err: err, Reason: TxGroupErrorReasonNotWellFormed}
}

return stxnCoreChecks(s, txnIdx, groupCtx, verifier)
Expand Down Expand Up @@ -197,7 +195,7 @@ func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr bookkeeping.Blo
// re-wrap the error, take underlying one and copy the reason code
err = &ErrTxGroupError{
err: fmt.Errorf("transaction %+v invalid : %w", stxn, prepErr.err),
reason: prepErr.reason,
Reason: prepErr.Reason,
}
return nil, err
}
Expand All @@ -208,7 +206,7 @@ func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr bookkeeping.Blo
}
feeNeeded, overflow := basics.OMul(groupCtx.consensusParams.MinTxnFee, minFeeCount)
if overflow {
err = &ErrTxGroupError{err: errTxGroupInvalidFee, reason: TxGroupErrorReasonInvalidFee}
err = &ErrTxGroupError{err: errTxGroupInvalidFee, Reason: TxGroupErrorReasonInvalidFee}
return nil, err
}
// feesPaid may have saturated. That's ok. Since we know
Expand All @@ -219,7 +217,7 @@ func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr bookkeeping.Blo
err: fmt.Errorf(
"txgroup had %d in fees, which is less than the minimum %d * %d",
feesPaid, minFeeCount, groupCtx.consensusParams.MinTxnFee),
reason: TxGroupErrorReasonInvalidFee,
Reason: TxGroupErrorReasonInvalidFee,
}
return nil, err
}
Expand Down Expand Up @@ -253,10 +251,10 @@ func stxnCoreChecks(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContex
if s.Txn.Sender == transactions.StateProofSender && s.Txn.Type == protocol.StateProofTx {
return nil
}
return &ErrTxGroupError{err: errTxnSigHasNoSig, reason: TxGroupErrorReasonHasNoSig}
return &ErrTxGroupError{err: errTxnSigHasNoSig, Reason: TxGroupErrorReasonHasNoSig}
}
if numSigs > 1 {
return &ErrTxGroupError{err: errTxnSigNotWellFormed, reason: TxGroupErrorReasonSigNotWellFormed}
return &ErrTxGroupError{err: errTxnSigNotWellFormed, Reason: TxGroupErrorReasonSigNotWellFormed}
}

if hasSig {
Expand All @@ -265,17 +263,17 @@ func stxnCoreChecks(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContex
}
if hasMsig {
if err := crypto.MultisigBatchPrep(s.Txn, crypto.Digest(s.Authorizer()), s.Msig, batchVerifier); err != nil {
return &ErrTxGroupError{err: fmt.Errorf("multisig validation failed: %w", err), reason: TxGroupErrorReasonMsigNotWellFormed}
return &ErrTxGroupError{err: fmt.Errorf("multisig validation failed: %w", err), Reason: TxGroupErrorReasonMsigNotWellFormed}
}
return nil
}
if hasLogicSig {
if err := logicSigVerify(s, txnIdx, groupCtx); err != nil {
return &ErrTxGroupError{err: err, reason: TxGroupErrorReasonLogicSigFailed}
return &ErrTxGroupError{err: err, Reason: TxGroupErrorReasonLogicSigFailed}
}
return nil
}
return &ErrTxGroupError{err: errUnknownSignature, reason: TxGroupErrorReasonGeneric}
return &ErrTxGroupError{err: errUnknownSignature, Reason: TxGroupErrorReasonGeneric}
}

// LogicSigSanityCheck checks that the signature is valid and that the program is basically well formed.
Expand Down
4 changes: 2 additions & 2 deletions data/txHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,8 @@ func (handler *TxHandler) postProcessReportErrors(err error) {

var txGroupErr *verify.ErrTxGroupError
if errors.As(err, &txGroupErr) {
txGroupErr = err.(*verify.ErrTxGroupError)
switch txGroupErr.Reason() {
// txGroupErr = err.(*verify.ErrTxGroupError)
switch txGroupErr.Reason {
case verify.TxGroupErrorReasonNotWellFormed:
transactionMessagesTxnNotWellFormed.Inc(nil)
case verify.TxGroupErrorReasonInvalidFee:
Expand Down
70 changes: 68 additions & 2 deletions data/txHandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,9 +570,75 @@ func runHandlerBenchmark(maxGroupSize int, b *testing.B) {
wg.Wait()
}

func BenchmarkPostProcessError(b *testing.B) {
func TestPostProcessError(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

collect := func() map[string]float64 {
result := map[string]float64{}
transactionMessagesTxnSigVerificationFailed.AddMetric(result)
transactionMessagesAlreadyCommitted.AddMetric(result)
transactionMessagesTxGroupInvalidFee.AddMetric(result)
transactionMessagesTxnNotWellFormed.AddMetric(result)
transactionMessagesTxnSigNotWellFormed.AddMetric(result)
transactionMessagesTxnMsigNotWellFormed.AddMetric(result)
transactionMessagesTxnLogicSig.AddMetric(result)
return result
}
var txh TxHandler

err := errors.New("couldn't find latest resources")
errSome := errors.New("some error")
txh.postProcessReportErrors(errSome)
result := collect()
require.Len(t, result, 0)

counter := 0
for i := verify.TxGroupErrorReasonGeneric; i <= verify.TxGroupErrorReasonLogicSigFailed; i++ {
errTxGroup := &verify.ErrTxGroupError{Reason: i}
txh.postProcessReportErrors(errTxGroup)
result = collect()
if i == verify.TxGroupErrorReasonSigNotWellFormed {
// TxGroupErrorReasonSigNotWellFormed and TxGroupErrorReasonHasNoSig increment the same metric
counter--
require.Equal(t, result[metrics.TransactionMessagesTxnSigNotWellFormed.Name], float64(2))
}
require.Len(t, result, counter)
counter++
}

// there are one less metrics than number of tracked values, and one generic non-tracked so -2
const expected = int(verify.TxGroupErrorReasonNumValues) - 2
require.Len(t, result, expected)

errVerify := crypto.ErrBatchVerificationFailed
txh.postProcessReportErrors(errVerify)
result = collect()
require.Len(t, result, expected+1)
}

func TestPostProcessErrorWithVerify(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

txn := transactions.Transaction{}
stxn := transactions.SignedTxn{Txn: txn}

// var cache verify.VerifiedTransactionCache
hdr := bookkeeping.BlockHeader{
UpgradeState: bookkeeping.UpgradeState{
CurrentProtocol: protocol.ConsensusCurrentVersion,
},
}
_, err := verify.TxnGroup([]transactions.SignedTxn{stxn}, hdr, nil, nil)
var txGroupErr *verify.ErrTxGroupError
require.ErrorAs(t, err, &txGroupErr)

result := map[string]float64{}
transactionMessagesTxnNotWellFormed.AddMetric(result)
require.Len(t, result, 0)

var txh TxHandler
txh.postProcessReportErrors(err)
transactionMessagesTxnNotWellFormed.AddMetric(result)
require.Len(t, result, 1)
}

0 comments on commit c6f47ac

Please sign in to comment.