From bba4c37c9d5e1a245fdccefa0fc75ba8fe6bd389 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 9 Apr 2024 10:38:01 -0400 Subject: [PATCH] More comprehensive testing of global opcode --- daemon/algod/api/server/v2/dryrun.go | 5 ++-- data/transactions/logic/eval.go | 2 +- data/transactions/logic/evalStateful_test.go | 9 ++++++ data/transactions/logic/eval_test.go | 13 ++------ data/transactions/logic/fields_test.go | 31 ++++++++++++++++++-- ledger/eval/eval_test.go | 2 +- 6 files changed, 46 insertions(+), 16 deletions(-) diff --git a/daemon/algod/api/server/v2/dryrun.go b/daemon/algod/api/server/v2/dryrun.go index 1546471250..1525604b27 100644 --- a/daemon/algod/api/server/v2/dryrun.go +++ b/daemon/algod/api/server/v2/dryrun.go @@ -309,8 +309,9 @@ func (dl *dryrunLedger) LookupWithoutRewards(rnd basics.Round, addr basics.Addre } func (dl *dryrunLedger) LookupAgreement(rnd basics.Round, addr basics.Address) (basics.OnlineAccountData, error) { - // dryrun does not understand rewards, so we build the result without adding pending rewards - ad, rnd, err := dl.lookup(rnd, addr) + // dryrun does not understand rewards, so we build the result without adding pending rewards. + // we also have no history, so we return current values + ad, _, err := dl.lookup(rnd, addr) if err != nil { return basics.OnlineAccountData{}, err } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index ad7d03eb45..36ba3bb788 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -3741,7 +3741,7 @@ func (cx *EvalContext) globalFieldToValue(fs globalFieldSpec) (sv stackValue, er return sv, fmt.Errorf("invalid global field %s", fs.field) } - if fs.ftype.AVMType != sv.avmType() { + if err == nil && fs.ftype.AVMType != sv.avmType() { return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.avmType()) } diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 1156d875a2..e6faad35a5 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -489,6 +489,9 @@ func testApp(t *testing.T, program string, ep *EvalParams, problems ...string) ( return testAppBytes(t, ops.Program, ep, problems...) } +// testAppCreator is the creator of the 888 app that is inserted when testing an app call +const testAppCreator = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" + func testAppBytes(t *testing.T, program []byte, ep *EvalParams, problems ...string) (transactions.EvalDelta, error) { t.Helper() if ep == nil { @@ -499,6 +502,12 @@ func testAppBytes(t *testing.T, program []byte, ep *EvalParams, problems ...stri aid := ep.TxnGroup[0].Txn.ApplicationID if aid == 0 { aid = basics.AppIndex(888) + // we're testing an app call without the caller specifying details about + // the app, so conjure up buring app params to make the `global + // AppCreator` work. + addr, err := basics.UnmarshalChecksumAddress(testAppCreator) + require.NoError(t, err) + ep.Ledger.(*Ledger).NewApp(addr, 888, basics.AppParams{}) } return testAppFull(t, program, 0, aid, ep, problems...) } diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 3ede2c134b..3a81a9cbef 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -1160,8 +1160,6 @@ int 1 && ` -const testAddr = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" - const globalV2TestProgram = globalV1TestProgram + ` global LogicSigVersion int 1 @@ -1182,7 +1180,7 @@ int 888 ` const globalV3TestProgram = globalV2TestProgram + ` global CreatorAddress -addr ` + testAddr + ` +addr ` + testAppCreator + ` == && ` @@ -1239,10 +1237,10 @@ const globalV11TestProgram = globalV10TestProgram + ` // No new globals in v11 ` -func TestGlobal(t *testing.T) { +func TestAllGlobals(t *testing.T) { partitiontest.PartitionTest(t) - t.Parallel() + type desc struct { lastField GlobalField program string @@ -1270,10 +1268,6 @@ func TestGlobal(t *testing.T) { require.Equal(t, tests[AssemblerMaxVersion].lastField, invalidGlobalField-1, "did you add a new global field?") - ledger := NewLedger(nil) - addr, err := basics.UnmarshalChecksumAddress(testAddr) - require.NoError(t, err) - ledger.NewApp(addr, 888, basics.AppParams{}) for v := uint64(1); v <= AssemblerMaxVersion; v++ { _, ok := tests[v] require.True(t, ok) @@ -1294,7 +1288,6 @@ func TestGlobal(t *testing.T) { appcall.Txn.Group = crypto.Digest{0x07, 0x06} ep := defaultAppParams(appcall) - ep.Ledger = ledger testApp(t, tests[v].program, ep) }) } diff --git a/data/transactions/logic/fields_test.go b/data/transactions/logic/fields_test.go index 7cec0b936f..ab2548c306 100644 --- a/data/transactions/logic/fields_test.go +++ b/data/transactions/logic/fields_test.go @@ -29,7 +29,8 @@ import ( // ensure v2+ fields fail in TEAL assembler and evaluator on a version before they introduced // ensure v2+ fields error in v1 program -func TestGlobalFieldsVersions(t *testing.T) { +// ensure the types of the returned values are correct +func TestGlobalVersionsAndTypes(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() @@ -42,7 +43,7 @@ func TestGlobalFieldsVersions(t *testing.T) { require.Greater(t, len(fields), 1) for _, field := range fields { - text := fmt.Sprintf("global %s", field.field.String()) + text := "global " + field.field.String() // check assembler fails if version before introduction testLine(t, text, assemblerNoVersion, "...was introduced in...") for v := uint64(0); v < field.version; v++ { @@ -50,6 +51,32 @@ func TestGlobalFieldsVersions(t *testing.T) { } testLine(t, text, field.version, "") + // tack on a type check, and return a value (`int` gets compiled + // differently in different versions, so use intc_0 explicitly + switch field.ftype.AVMType { + case avmUint64: // ensure the return type is uint64 by using ! + text = "intcblock 1;" + text + "; !; pop; intc_0" + case avmBytes: // ensure the return type is bytes by using len + text = "intcblock 1;" + text + "; len; pop; intc_0" + case avmAny: + text = "intcblock 1;" + text + "; pop; intc_0" + } + + // check success in AssemblerMaxVersion and fs.version + for _, ver := range []uint64{AssemblerMaxVersion, field.version} { + ops := testProg(t, text, ver) + switch field.mode { + case ModeSig: + testLogicBytes(t, ops.Program, defaultSigParamsWithVersion(ver)) + case ModeApp: + testAppBytes(t, ops.Program, defaultAppParamsWithVersion(ver)) + case modeAny: + testLogicBytes(t, ops.Program, defaultSigParamsWithVersion(ver)) + testAppBytes(t, ops.Program, defaultAppParamsWithVersion(ver)) + default: + t.Fail() + } + } ops := testProg(t, text, AssemblerMaxVersion) // check on a version before the field version diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index bd67275437..cd49f5ad03 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -794,7 +794,7 @@ func (ledger *evalTestLedger) LookupAgreement(rnd basics.Round, addr basics.Addr } // OnlineCirculation just returns a deterministic value for a given round. -func (ledge *evalTestLedger) OnlineCirculation(rnd, voteRound basics.Round) (basics.MicroAlgos, error) { +func (ledger *evalTestLedger) OnlineCirculation(rnd, voteRound basics.Round) (basics.MicroAlgos, error) { return basics.MicroAlgos{Raw: uint64(rnd) * 1_000_000}, nil }