Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BatchVerifier: Rename and unexport local functions in verify/txn #4578

Merged
merged 13 commits into from
Sep 30, 2022
48 changes: 16 additions & 32 deletions data/transactions/verify/txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,42 +99,26 @@ func (g *GroupContext) Equal(other *GroupContext) bool {
g.minAvmVersion == other.minAvmVersion
}

// Txn verifies a SignedTxn as being signed and having no obviously inconsistent data.
algorandskiy marked this conversation as resolved.
Show resolved Hide resolved
// txnBatchVerifyPrep verifies a SignedTxn having no obviously inconsistent data.
// Block-assembly time checks of LogicSig and accounting rules may still block the txn.
func Txn(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext) error {
batchVerifier := crypto.MakeBatchVerifier()

if err := TxnBatchVerify(s, txnIdx, groupCtx, batchVerifier); err != nil {
return err
}

// this case is used for comapact certificate where no signature is supplied
if batchVerifier.GetNumberOfEnqueuedSignatures() == 0 {
return nil
}
return batchVerifier.Verify()
}

// TxnBatchVerify verifies a SignedTxn having no obviously inconsistent data.
// Block-assembly time checks of LogicSig and accounting rules may still block the txn.
// it is the caller responsibility to call batchVerifier.verify()
func TxnBatchVerify(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext, verifier *crypto.BatchVerifier) error {
// it is the caller responsibility to call batchVerifier.Verify()
func txnBatchVerifyPrep(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext, verifier *crypto.BatchVerifier) error {
if !groupCtx.consensusParams.SupportRekeying && (s.AuthAddr != basics.Address{}) {
return errors.New("nonempty AuthAddr but rekeying not supported")
return errors.New("nonempty AuthAddr but rekeying is not supported")
}

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

return stxnVerifyCore(s, txnIdx, groupCtx, verifier)
return stxnCoreChecks(s, txnIdx, groupCtx, verifier)
}

// TxnGroup verifies a []SignedTxn as being signed and having no obviously inconsistent data.
func TxnGroup(stxs []transactions.SignedTxn, contextHdr bookkeeping.BlockHeader, cache VerifiedTransactionCache, ledger logic.LedgerForSignature) (groupCtx *GroupContext, err error) {
batchVerifier := crypto.MakeBatchVerifier()

if groupCtx, err = TxnGroupBatchVerify(stxs, contextHdr, cache, ledger, batchVerifier); err != nil {
if groupCtx, err = txnGroupBatchVerifyPrep(stxs, contextHdr, cache, ledger, batchVerifier); err != nil {
return nil, err
}

algonautshant marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -149,9 +133,9 @@ func TxnGroup(stxs []transactions.SignedTxn, contextHdr bookkeeping.BlockHeader,
return
}

// TxnGroupBatchVerify verifies a []SignedTxn having no obviously inconsistent data.
// it is the caller responsibility to call batchVerifier.verify()
func TxnGroupBatchVerify(stxs []transactions.SignedTxn, contextHdr bookkeeping.BlockHeader, cache VerifiedTransactionCache, ledger logic.LedgerForSignature, verifier *crypto.BatchVerifier) (groupCtx *GroupContext, err error) {
// txnGroupBatchVerifyPrep verifies a []SignedTxn having no obviously inconsistent data.
// it is the caller responsibility to call batchVerifier.Verify()
func txnGroupBatchVerifyPrep(stxs []transactions.SignedTxn, contextHdr bookkeeping.BlockHeader, cache VerifiedTransactionCache, ledger logic.LedgerForSignature, verifier *crypto.BatchVerifier) (groupCtx *GroupContext, err error) {
algonautshant marked this conversation as resolved.
Show resolved Hide resolved
groupCtx, err = PrepareGroupContext(stxs, contextHdr, ledger)
if err != nil {
return nil, err
Expand All @@ -160,7 +144,7 @@ func TxnGroupBatchVerify(stxs []transactions.SignedTxn, contextHdr bookkeeping.B
minFeeCount := uint64(0)
feesPaid := uint64(0)
for i, stxn := range stxs {
err = TxnBatchVerify(&stxn, i, groupCtx, verifier)
err = txnBatchVerifyPrep(&stxn, i, groupCtx, verifier)
if err != nil {
err = fmt.Errorf("transaction %+v invalid : %w", stxn, err)
return
Expand Down Expand Up @@ -190,7 +174,7 @@ func TxnGroupBatchVerify(stxs []transactions.SignedTxn, contextHdr bookkeeping.B
return
}

func stxnVerifyCore(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier) error {
func stxnCoreChecks(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier) error {
numSigs := 0
hasSig := false
hasMsig := false
Expand Down Expand Up @@ -246,7 +230,7 @@ func stxnVerifyCore(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContex
func LogicSigSanityCheck(txn *transactions.SignedTxn, groupIndex int, groupCtx *GroupContext) error {
batchVerifier := crypto.MakeBatchVerifier()

if err := LogicSigSanityCheckBatchVerify(txn, groupIndex, groupCtx, batchVerifier); err != nil {
if err := logicSigSanityCheckBatchVerifyPrep(txn, groupIndex, groupCtx, batchVerifier); err != nil {
return err
}

Expand All @@ -258,10 +242,10 @@ func LogicSigSanityCheck(txn *transactions.SignedTxn, groupIndex int, groupCtx *
return batchVerifier.Verify()
}

// LogicSigSanityCheckBatchVerify checks that the signature is valid and that the program is basically well formed.
// logicSigSanityCheckBatchVerifyPrep checks that the signature is valid and that the program is basically well formed.
// It does not evaluate the logic.
// it is the caller responsibility to call batchVerifier.verify()
func LogicSigSanityCheckBatchVerify(txn *transactions.SignedTxn, groupIndex int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier) error {
// it is the caller responsibility to call batchVerifier.Verify()
func logicSigSanityCheckBatchVerifyPrep(txn *transactions.SignedTxn, groupIndex int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier) error {
lsig := txn.Lsig

if groupCtx.consensusParams.LogicSigVersion == 0 {
Expand Down Expand Up @@ -404,7 +388,7 @@ func PaysetGroups(ctx context.Context, payset [][]transactions.SignedTxn, blkHea

batchVerifier := crypto.MakeBatchVerifierWithHint(len(payset))
for i, signTxnsGrp := range txnGroups {
groupCtxs[i], grpErr = TxnGroupBatchVerify(signTxnsGrp, blkHeader, nil, ledger, batchVerifier)
groupCtxs[i], grpErr = txnGroupBatchVerifyPrep(signTxnsGrp, blkHeader, nil, ledger, batchVerifier)
// abort only if it's a non-cache error.
if grpErr != nil {
return grpErr
Expand Down
44 changes: 29 additions & 15 deletions data/transactions/verify/txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ var spec = transactions.SpecialAddresses{
RewardsPool: poolAddr,
}

func verifyTxn(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext) error {
batchVerifier := crypto.MakeBatchVerifier()

if err := txnBatchVerifyPrep(s, txnIdx, groupCtx, batchVerifier); err != nil {
return err
}

// this case is used for comapact certificate where no signature is supplied
algonautshant marked this conversation as resolved.
Show resolved Hide resolved
if batchVerifier.GetNumberOfEnqueuedSignatures() == 0 {
return nil
}
algonautshant marked this conversation as resolved.
Show resolved Hide resolved
return batchVerifier.Verify()
}

func keypair() *crypto.SignatureSecrets {
var seed crypto.Seed
crypto.RandBytes(seed[:])
Expand Down Expand Up @@ -117,14 +131,14 @@ func TestSignedPayment(t *testing.T) {
groupCtx, err := PrepareGroupContext(stxns, blockHeader, nil)
require.NoError(t, err)
require.NoError(t, payment.WellFormed(spec, proto), "generateTestObjects generated an invalid payment")
require.NoError(t, Txn(&stxn, 0, groupCtx), "generateTestObjects generated a bad signedtxn")
require.NoError(t, verifyTxn(&stxn, 0, groupCtx), "generateTestObjects generated a bad signedtxn")

stxn2 := payment.Sign(secret)
require.Equal(t, stxn2.Sig, stxn.Sig, "got two different signatures for the same transaction (our signing function is deterministic)")

stxn2.MessUpSigForTesting()
require.Equal(t, stxn.ID(), stxn2.ID(), "changing sig caused txid to change")
require.Error(t, Txn(&stxn2, 0, groupCtx), "verify succeeded with bad sig")
require.Error(t, verifyTxn(&stxn2, 0, groupCtx), "verify succeeded with bad sig")

require.True(t, crypto.SignatureVerifier(addr).Verify(payment, stxn.Sig), "signature on the transaction is not the signature of the hash of the transaction under the spender's key")
}
Expand All @@ -137,15 +151,15 @@ func TestTxnValidationEncodeDecode(t *testing.T) {
for _, txn := range signed {
groupCtx, err := PrepareGroupContext([]transactions.SignedTxn{txn}, blockHeader, nil)
require.NoError(t, err)
if Txn(&txn, 0, groupCtx) != nil {
if verifyTxn(&txn, 0, groupCtx) != nil {
t.Errorf("signed transaction %#v did not verify", txn)
}

x := protocol.Encode(&txn)
var signedTx transactions.SignedTxn
protocol.Decode(x, &signedTx)

if Txn(&signedTx, 0, groupCtx) != nil {
if verifyTxn(&signedTx, 0, groupCtx) != nil {
t.Errorf("signed transaction %#v did not verify", txn)
}
}
Expand All @@ -159,14 +173,14 @@ func TestTxnValidationEmptySig(t *testing.T) {
for _, txn := range signed {
groupCtx, err := PrepareGroupContext([]transactions.SignedTxn{txn}, blockHeader, nil)
require.NoError(t, err)
if Txn(&txn, 0, groupCtx) != nil {
if verifyTxn(&txn, 0, groupCtx) != nil {
t.Errorf("signed transaction %#v did not verify", txn)
}

txn.Sig = crypto.Signature{}
txn.Msig = crypto.MultisigSig{}
txn.Lsig = transactions.LogicSig{}
if Txn(&txn, 0, groupCtx) == nil {
if verifyTxn(&txn, 0, groupCtx) == nil {
t.Errorf("transaction %#v verified without sig", txn)
}
}
Expand Down Expand Up @@ -205,42 +219,42 @@ func TestTxnValidationStateProof(t *testing.T) {
groupCtx, err := PrepareGroupContext([]transactions.SignedTxn{stxn}, blockHeader, nil)
require.NoError(t, err)

err = Txn(&stxn, 0, groupCtx)
err = verifyTxn(&stxn, 0, groupCtx)
require.NoError(t, err, "state proof txn %#v did not verify", stxn)

stxn2 := stxn
stxn2.Txn.Type = protocol.PaymentTx
stxn2.Txn.Header.Fee = basics.MicroAlgos{Raw: proto.MinTxnFee}
err = Txn(&stxn2, 0, groupCtx)
err = verifyTxn(&stxn2, 0, groupCtx)
require.Error(t, err, "payment txn %#v verified from StateProofSender", stxn2)

secret := keypair()
stxn2 = stxn
stxn2.Txn.Header.Sender = basics.Address(secret.SignatureVerifier)
stxn2.Txn.Header.Fee = basics.MicroAlgos{Raw: proto.MinTxnFee}
stxn2 = stxn2.Txn.Sign(secret)
err = Txn(&stxn2, 0, groupCtx)
err = verifyTxn(&stxn2, 0, groupCtx)
require.Error(t, err, "state proof txn %#v verified from non-StateProofSender", stxn2)

// state proof txns are not allowed to have non-zero values for many fields
stxn2 = stxn
stxn2.Txn.Header.Fee = basics.MicroAlgos{Raw: proto.MinTxnFee}
err = Txn(&stxn2, 0, groupCtx)
err = verifyTxn(&stxn2, 0, groupCtx)
require.Error(t, err, "state proof txn %#v verified", stxn2)

stxn2 = stxn
stxn2.Txn.Header.Note = []byte{'A'}
err = Txn(&stxn2, 0, groupCtx)
err = verifyTxn(&stxn2, 0, groupCtx)
require.Error(t, err, "state proof txn %#v verified", stxn2)

stxn2 = stxn
stxn2.Txn.Lease[0] = 1
err = Txn(&stxn2, 0, groupCtx)
err = verifyTxn(&stxn2, 0, groupCtx)
require.Error(t, err, "state proof txn %#v verified", stxn2)

stxn2 = stxn
stxn2.Txn.RekeyTo = basics.Address(secret.SignatureVerifier)
err = Txn(&stxn2, 0, groupCtx)
err = verifyTxn(&stxn2, 0, groupCtx)
require.Error(t, err, "state proof txn %#v verified", stxn2)
}

Expand All @@ -258,7 +272,7 @@ func TestDecodeNil(t *testing.T) {
// This used to panic when run on a zero value of SignedTxn.
groupCtx, err := PrepareGroupContext([]transactions.SignedTxn{st}, blockHeader, nil)
require.NoError(t, err)
Txn(&st, 0, groupCtx)
verifyTxn(&st, 0, groupCtx)
}
}

Expand Down Expand Up @@ -425,7 +439,7 @@ func BenchmarkTxn(b *testing.B) {
groupCtx, err := PrepareGroupContext(txnGroup, blk.BlockHeader, nil)
require.NoError(b, err)
for i, txn := range txnGroup {
err := Txn(&txn, i, groupCtx)
err := verifyTxn(&txn, i, groupCtx)
require.NoError(b, err)
}
}
Expand Down
4 changes: 2 additions & 2 deletions test/e2e-go/upgrades/rekey_support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ func TestRekeyUpgrade(t *testing.T) {
_, err = client.BroadcastTransaction(rekeyed)
// non empty err means the upgrade have not happened yet (as expected), ensure the error
if err != nil {
// should be either "nonempty AuthAddr but rekeying not supported" or "txn dead"
if !strings.Contains(err.Error(), "nonempty AuthAddr but rekeying not supported") &&
// should be either "nonempty AuthAddr but rekeying is not supported" or "txn dead"
if !strings.Contains(err.Error(), "nonempty AuthAddr but rekeying is not supported") &&
!strings.Contains(err.Error(), "txn dead") {
a.NoErrorf(err, "error message should be one of :\n%s\n%s", "nonempty AuthAddr but rekeying not supported", "txn dead")
}
Expand Down