diff options
Diffstat (limited to 'data/transactions/logic/evalStateful_test.go')
-rw-r--r-- | data/transactions/logic/evalStateful_test.go | 864 |
1 files changed, 410 insertions, 454 deletions
diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index ee940026b..91dff7192 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -22,11 +22,11 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" - "github.com/algorand/go-algorand/data/transactions/logictest" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -44,22 +44,21 @@ func makeApp(li uint64, lb uint64, gi uint64, gb uint64) basics.AppParams { } } -func makeSampleEnv() (EvalParams, *logictest.Ledger) { +func makeSampleEnv() (*EvalParams, *transactions.Transaction, *Ledger) { return makeSampleEnvWithVersion(LogicVersion) } -func makeSampleEnvWithVersion(version uint64) (EvalParams, *logictest.Ledger) { - txn := makeSampleTxn() - ep := defaultEvalParamsWithVersion(nil, &txn, version) - ep.TxnGroup = makeSampleTxnGroup(txn) - ledger := logictest.MakeLedger(map[basics.Address]uint64{}) +func makeSampleEnvWithVersion(version uint64) (*EvalParams, *transactions.Transaction, *Ledger) { + ep := defaultEvalParamsWithVersion(nil, version) + ep.TxnGroup = transactions.WrapSignedTxnsWithAD(makeSampleTxnGroup(makeSampleTxn())) + ledger := MakeLedger(map[basics.Address]uint64{}) ep.Ledger = ledger - return ep, ledger + return ep, &ep.TxnGroup[0].Txn, ledger } -func makeOldAndNewEnv(version uint64) (EvalParams, EvalParams, *logictest.Ledger) { - new, sharedLedger := makeSampleEnv() - old, _ := makeSampleEnvWithVersion(version) +func makeOldAndNewEnv(version uint64) (*EvalParams, *EvalParams, *Ledger) { + new, _, sharedLedger := makeSampleEnv() + old, _, _ := makeSampleEnvWithVersion(version) old.Ledger = sharedLedger return old, new, sharedLedger } @@ -190,33 +189,19 @@ pop bytec_0 log ` - type desc struct { - source string - eval func([]byte, EvalParams) (bool, error) - check func([]byte, EvalParams) error - } - tests := map[runMode]desc{ - runModeSignature: { - source: opcodesRunModeAny + opcodesRunModeSignature, - eval: func(program []byte, ep EvalParams) (bool, error) { return Eval(program, ep) }, - check: func(program []byte, ep EvalParams) error { return Check(program, ep) }, - }, - runModeApplication: { - source: opcodesRunModeAny + opcodesRunModeApplication, - eval: func(program []byte, ep EvalParams) (bool, error) { return EvalStateful(program, ep) }, - check: func(program []byte, ep EvalParams) error { return CheckStateful(program, ep) }, - }, + tests := map[runMode]string{ + runModeSignature: opcodesRunModeAny + opcodesRunModeSignature, + runModeApplication: opcodesRunModeAny + opcodesRunModeApplication, } - txn := makeSampleTxn() - txgroup := makeSampleTxnGroup(txn) - txn.Lsig.Args = [][]byte{ - txn.Txn.Sender[:], - txn.Txn.Receiver[:], - txn.Txn.CloseRemainderTo[:], - txn.Txn.VotePK[:], - txn.Txn.SelectionPK[:], - txn.Txn.Note, + ep, tx, ledger := makeSampleEnv() + ep.TxnGroup[0].Lsig.Args = [][]byte{ + tx.Sender[:], + tx.Receiver[:], + tx.CloseRemainderTo[:], + tx.VotePK[:], + tx.SelectionPK[:], + tx.Note, } params := basics.AssetParams{ Total: 1000, @@ -225,57 +210,35 @@ log UnitName: "ALGO", AssetName: "", URL: string(protocol.PaymentTx), - Manager: txn.Txn.Sender, - Reserve: txn.Txn.Receiver, - Freeze: txn.Txn.Receiver, - Clawback: txn.Txn.Receiver, + Manager: tx.Sender, + Reserve: tx.Receiver, + Freeze: tx.Receiver, + Clawback: tx.Receiver, } algoValue := basics.TealValue{Type: basics.TealUintType, Uint: 0x77} - ledger := logictest.MakeLedger( - map[basics.Address]uint64{ - txn.Txn.Sender: 1, - }, - ) - ledger.NewApp(txn.Txn.Sender, 100, basics.AppParams{}) - ledger.NewLocal(txn.Txn.Sender, 100, "ALGO", algoValue) - ledger.NewAsset(txn.Txn.Sender, 5, params) + ledger.NewAccount(tx.Sender, 1) + ledger.NewApp(tx.Sender, 100, basics.AppParams{}) + ledger.NewLocals(tx.Sender, 100) + ledger.NewLocal(tx.Sender, 100, "ALGO", algoValue) + ledger.NewAsset(tx.Sender, 5, params) for mode, test := range tests { t.Run(fmt.Sprintf("opcodes_mode=%d", mode), func(t *testing.T) { - ops := testProg(t, test.source, AssemblerMaxVersion) - sb := strings.Builder{} - ep := defaultEvalParams(&sb, &txn) - ep.TxnGroup = txgroup - ep.Ledger = ledger - ep.Txn.Txn.ApplicationID = 100 - ep.Txn.Txn.ForeignAssets = []basics.AssetIndex{5} // needed since v4 - - err := test.check(ops.Program, ep) - require.NoError(t, err) - _, err = test.eval(ops.Program, ep) - if err != nil { - t.Log(hex.EncodeToString(ops.Program)) - t.Log(sb.String()) + ep.TxnGroup[0].Txn.ApplicationID = 100 + ep.TxnGroup[0].Txn.ForeignAssets = []basics.AssetIndex{5} // needed since v4 + if mode == runModeSignature { + testLogic(t, test, AssemblerMaxVersion, ep) + } else { + testApp(t, test, ep) } - require.NoError(t, err) }) } // check err opcode work in both modes - for mode, test := range tests { - t.Run(fmt.Sprintf("err_mode=%d", mode), func(t *testing.T) { - source := "err" - ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion) - require.NoError(t, err) - ep := defaultEvalParams(nil, nil) - err = test.check(ops.Program, ep) - require.NoError(t, err) - _, err = test.eval(ops.Program, ep) - require.Error(t, err) - require.NotContains(t, err.Error(), "not allowed in current mode") - require.Contains(t, err.Error(), "err opcode") - }) - } + source := "err" + testLogic(t, source, AssemblerMaxVersion, defaultEvalParams(nil), "encountered err") + testApp(t, source, defaultEvalParams(nil), "encountered err") + // require.NotContains(t, err.Error(), "not allowed in current mode") // check that ed25519verify and arg is not allowed in stateful mode between v2-v4 disallowedV4 := []string{ @@ -288,12 +251,8 @@ log } for _, source := range disallowedV4 { ops := testProg(t, source, 4) - ep := defaultEvalParams(nil, nil) - err := CheckStateful(ops.Program, ep) - require.Error(t, err) - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "not allowed in current mode") + testAppBytes(t, ops.Program, defaultEvalParams(nil), + "not allowed in current mode", "not allowed in current mode") } // check that arg is not allowed in stateful mode beyond v5 @@ -306,12 +265,8 @@ log } for _, source := range disallowed { ops := testProg(t, source, AssemblerMaxVersion) - ep := defaultEvalParams(nil, nil) - err := CheckStateful(ops.Program, ep) - require.Error(t, err) - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "not allowed in current mode") + testAppBytes(t, ops.Program, defaultEvalParams(nil), + "not allowed in current mode", "not allowed in current mode") } // check stateful opcodes are not allowed in stateless mode @@ -333,13 +288,8 @@ log } for _, source := range statefulOpcodeCalls { - ops := testProg(t, source, AssemblerMaxVersion) - ep := defaultEvalParams(nil, nil) - err := Check(ops.Program, ep) - require.Error(t, err) - _, err = Eval(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "not allowed in current mode") + testLogic(t, source, AssemblerMaxVersion, defaultEvalParams(nil), + "not allowed in current mode", "not allowed in current mode") } require.Equal(t, runMode(1), runModeSignature) @@ -353,9 +303,9 @@ func TestBalance(t *testing.T) { t.Parallel() - ep, ledger := makeSampleEnv() + ep, tx, ledger := makeSampleEnv() text := "int 2; balance; int 177; ==" - ledger.NewAccount(ep.Txn.Txn.Receiver, 177) + ledger.NewAccount(tx.Receiver, 177) testApp(t, text, ep, "invalid Account reference") text = `int 1; balance; int 177; ==` @@ -363,7 +313,7 @@ func TestBalance(t *testing.T) { text = `txn Accounts 1; balance; int 177; ==;` // won't assemble in old version teal - testProg(t, text, directRefEnabledVersion-1, expect{2, "balance arg 0 wanted type uint64..."}) + testProg(t, text, directRefEnabledVersion-1, Expect{2, "balance arg 0 wanted type uint64..."}) // but legal after that testApp(t, text, ep) @@ -373,48 +323,112 @@ func TestBalance(t *testing.T) { ledger.NewAccount(addr, 13) testApp(t, text, ep, "assert failed") - ledger.NewAccount(ep.Txn.Txn.Sender, 13) + ledger.NewAccount(tx.Sender, 13) testApp(t, text, ep) } -func testApp(t *testing.T, program string, ep EvalParams, problems ...string) transactions.EvalDelta { +func testApps(t *testing.T, programs []string, txgroup []transactions.SignedTxn, version uint64, ledger LedgerForLogic, + expected ...Expect) { + t.Helper() + codes := make([][]byte, len(programs)) + for i, program := range programs { + if program != "" { + codes[i] = testProg(t, program, version).Program + } + } + ep := NewEvalParams(transactions.WrapSignedTxnsWithAD(txgroup), makeTestProtoV(version), &transactions.SpecialAddresses{}) + ep.Ledger = ledger + testAppsBytes(t, codes, ep, expected...) +} + +func testAppsBytes(t *testing.T, programs [][]byte, ep *EvalParams, expected ...Expect) { + t.Helper() + require.Equal(t, len(programs), len(ep.TxnGroup)) + for i := range ep.TxnGroup { + if programs[i] != nil { + if len(expected) > 0 && expected[0].l == i { + testAppFull(t, programs[i], i, basics.AppIndex(888), ep, expected[0].s) + break // Stop after first failure + } else { + testAppFull(t, programs[i], i, basics.AppIndex(888), ep) + } + } + } +} + +func testApp(t *testing.T, program string, ep *EvalParams, problems ...string) transactions.EvalDelta { t.Helper() ops := testProg(t, program, ep.Proto.LogicSigVersion) - err := CheckStateful(ops.Program, ep) - require.NoError(t, err) + return testAppBytes(t, ops.Program, ep, problems...) +} - // we only use this to test stateful apps. While, I suppose - // it's *legal* to have an app with no stateful ops, this - // convenience routine can assume it, and check it. - pass, err := Eval(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "not allowed in current mode") - require.False(t, pass) +func testAppBytes(t *testing.T, program []byte, ep *EvalParams, problems ...string) transactions.EvalDelta { + t.Helper() + ep.reset() + aid := ep.TxnGroup[0].Txn.ApplicationID + if aid == basics.AppIndex(0) { + aid = basics.AppIndex(888) + } + return testAppFull(t, program, 0, aid, ep, problems...) +} + +// testAppFull gives a lot of control to caller - in particular, notice that +// ep.reset() is in testAppBytes, not here. This means that ADs in the ep are +// not cleared, so repeated use of a single ep is probably not a good idea +// unless you are *intending* to see how ep is modified as you go. +func testAppFull(t *testing.T, program []byte, gi int, aid basics.AppIndex, ep *EvalParams, problems ...string) transactions.EvalDelta { + t.Helper() + + var checkProblem string + var evalProblem string + switch len(problems) { + case 2: + checkProblem = problems[0] + evalProblem = problems[1] + case 1: + evalProblem = problems[0] + case 0: + default: + require.Fail(t, "Misused testApp: %d problems", len(problems)) + } sb := &strings.Builder{} ep.Trace = sb - pass, err = EvalStateful(ops.Program, ep) - if len(problems) == 0 { + + err := CheckContract(program, ep) + if checkProblem == "" { require.NoError(t, err, sb.String()) - require.True(t, pass, sb.String()) - delta, err := ep.Ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) - return delta + } else { + require.Error(t, err, "Check\n%s\nExpected: %v", sb, checkProblem) + require.Contains(t, err.Error(), checkProblem, sb.String()) } - require.Error(t, err, sb.String()) - for _, problem := range problems { - require.Contains(t, err.Error(), problem) + // We continue on to check Eval() of things that failed Check() because it's + // a nice confirmation that Check() is usually stricter than Eval(). This + // may mean that the problems argument is often duplicated, but this seems + // the best way to be concise about all sorts of tests. + + if ep.Ledger == nil { + ep.Ledger = MakeLedger(nil) } - if ep.Ledger != nil { - delta, err := ep.Ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) - require.Empty(t, delta.GlobalDelta) - require.Empty(t, delta.LocalDeltas) - require.Empty(t, delta.Logs) + + pass, err := EvalApp(program, gi, aid, ep) + delta := ep.TxnGroup[gi].EvalDelta + if evalProblem == "" { + require.NoError(t, err, "Eval%s\nExpected: PASS", sb) + require.True(t, pass, "Eval%s\nExpected: PASS", sb) return delta } - return transactions.EvalDelta{} + + // There is an evalProblem to check. REJECT is special and only means that + // the app didn't accept. Maybe it's an error, maybe it's just !pass. + if evalProblem == "REJECT" { + require.True(t, err != nil || !pass, "Eval%s\nExpected: REJECT", sb) + } else { + require.Error(t, err, "Eval\n%s\nExpected: %v", sb, evalProblem) + require.Contains(t, err.Error(), evalProblem) + } + return delta } func TestMinBalance(t *testing.T) { @@ -422,35 +436,36 @@ func TestMinBalance(t *testing.T) { t.Parallel() - ep, ledger := makeSampleEnv() + ep, tx, ledger := makeSampleEnv() - ledger.NewAccount(ep.Txn.Txn.Sender, 234) - ledger.NewAccount(ep.Txn.Txn.Receiver, 123) + ledger.NewAccount(tx.Sender, 234) + ledger.NewAccount(tx.Receiver, 123) testApp(t, "int 0; min_balance; int 1001; ==", ep) // Sender makes an asset, min balance goes up - ledger.NewAsset(ep.Txn.Txn.Sender, 7, basics.AssetParams{Total: 1000}) + ledger.NewAsset(tx.Sender, 7, basics.AssetParams{Total: 1000}) testApp(t, "int 0; min_balance; int 2002; ==", ep) schemas := makeApp(1, 2, 3, 4) - ledger.NewApp(ep.Txn.Txn.Sender, 77, schemas) + ledger.NewApp(tx.Sender, 77, schemas) + ledger.NewLocals(tx.Sender, 77) // create + optin + 10 schema base + 4 ints + 6 bytes (local - // and global count b/c NewApp opts the creator in) + // and global count b/c NewLocals opts the creator in) minb := 2*1002 + 10*1003 + 4*1004 + 6*1005 testApp(t, fmt.Sprintf("int 0; min_balance; int %d; ==", 2002+minb), ep) // request extra program pages, min balance increase withepp := makeApp(1, 2, 3, 4) withepp.ExtraProgramPages = 2 - ledger.NewApp(ep.Txn.Txn.Sender, 77, withepp) + ledger.NewApp(tx.Sender, 77, withepp) minb += 2 * 1002 testApp(t, fmt.Sprintf("int 0; min_balance; int %d; ==", 2002+minb), ep) testApp(t, "int 1; min_balance; int 1001; ==", ep) // 1 == Accounts[0] testProg(t, "txn Accounts 1; min_balance; int 1001; ==", directRefEnabledVersion-1, - expect{2, "min_balance arg 0 wanted type uint64..."}) + Expect{2, "min_balance arg 0 wanted type uint64..."}) testProg(t, "txn Accounts 1; min_balance; int 1001; ==", directRefEnabledVersion) testApp(t, "txn Accounts 1; min_balance; int 1001; ==", ep) // 1 == Accounts[0] // Receiver opts in - ledger.NewHolding(ep.Txn.Txn.Receiver, 7, 1, true) + ledger.NewHolding(tx.Receiver, 7, 1, true) testApp(t, "int 1; min_balance; int 2002; ==", ep) // 1 == Accounts[0] testApp(t, "int 2; min_balance; int 1001; ==", ep, "invalid Account reference 2") @@ -464,15 +479,12 @@ func TestAppCheckOptedIn(t *testing.T) { txn := makeSampleTxn() txgroup := makeSampleTxnGroup(txn) - now := defaultEvalParams(nil, nil) - now.Txn = &txn - now.TxnGroup = txgroup - pre := defaultEvalParamsWithVersion(nil, nil, directRefEnabledVersion-1) - pre.Txn = &txn - pre.TxnGroup = txgroup - testApp(t, "int 2; int 100; app_opted_in; int 1; ==", now, "ledger not available") - - ledger := logictest.MakeLedger( + now := defaultEvalParams(&txn) + now.TxnGroup = transactions.WrapSignedTxnsWithAD(txgroup) + pre := defaultEvalParamsWithVersion(&txn, directRefEnabledVersion-1) + pre.TxnGroup = transactions.WrapSignedTxnsWithAD(txgroup) + + ledger := MakeLedger( map[basics.Address]uint64{ txn.Txn.Receiver: 1, txn.Txn.Sender: 1, @@ -491,16 +503,16 @@ func TestAppCheckOptedIn(t *testing.T) { testApp(t, "int 0; int 100; app_opted_in; int 0; ==", now) // Receiver opted in - ledger.NewApp(txn.Txn.Receiver, 100, basics.AppParams{}) + ledger.NewLocals(txn.Txn.Receiver, 100) testApp(t, "int 1; int 100; app_opted_in; int 1; ==", now) testApp(t, "int 1; int 2; app_opted_in; int 1; ==", now) testApp(t, "int 1; int 2; app_opted_in; int 0; ==", pre) // in pre, int 2 is an actual app id testApp(t, "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01\"; int 2; app_opted_in; int 1; ==", now) testProg(t, "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01\"; int 2; app_opted_in; int 1; ==", directRefEnabledVersion-1, - expect{3, "app_opted_in arg 0 wanted type uint64..."}) + Expect{3, "app_opted_in arg 0 wanted type uint64..."}) // Sender opted in - ledger.NewApp(txn.Txn.Sender, 100, basics.AppParams{}) + ledger.NewLocals(txn.Txn.Sender, 100) testApp(t, "int 0; int 100; app_opted_in; int 1; ==", now) } @@ -524,7 +536,7 @@ int 1 ==` pre, now, ledger := makeOldAndNewEnv(directRefEnabledVersion - 1) - ledger.NewAccount(now.Txn.Txn.Receiver, 1) + ledger.NewAccount(now.TxnGroup[0].Txn.Receiver, 1) testApp(t, text, now, "invalid Account reference") text = `int 1 // account idx @@ -543,11 +555,12 @@ int 1` testApp(t, text, now, "no app for account") // Make a different app (not 100) - ledger.NewApp(now.Txn.Txn.Receiver, 9999, basics.AppParams{}) + ledger.NewApp(now.TxnGroup[0].Txn.Receiver, 9999, basics.AppParams{}) testApp(t, text, now, "no app for account") // create the app and check the value from ApplicationArgs[0] (protocol.PaymentTx) does not exist - ledger.NewApp(now.Txn.Txn.Receiver, 100, basics.AppParams{}) + ledger.NewApp(now.TxnGroup[0].Txn.Receiver, 100, basics.AppParams{}) + ledger.NewLocals(now.TxnGroup[0].Txn.Receiver, 100) testApp(t, text, now) text = `int 1 // account idx @@ -559,17 +572,17 @@ err exist: byte 0x414c474f ==` - ledger.NewLocal(now.Txn.Txn.Receiver, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) + ledger.NewLocal(now.TxnGroup[0].Txn.Receiver, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) testApp(t, text, now) testApp(t, strings.Replace(text, "int 1 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01\"", -1), now) testProg(t, strings.Replace(text, "int 1 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01\"", -1), directRefEnabledVersion-1, - expect{4, "app_local_get_ex arg 0 wanted type uint64..."}) + Expect{4, "app_local_get_ex arg 0 wanted type uint64..."}) testApp(t, strings.Replace(text, "int 100 // app id", "int 2", -1), now) // Next we're testing if the use of the current app's id works // as a direct reference. The error is because the sender // account is not opted into 123. - ledger.NewApp(now.Txn.Txn.RekeyTo, 123, basics.AppParams{}) + now.TxnGroup[0].Txn.ApplicationID = 123 testApp(t, strings.Replace(text, "int 100 // app id", "int 123", -1), now, "no app for account") testApp(t, strings.Replace(text, "int 100 // app id", "int 2", -1), pre, "no app for account") testApp(t, strings.Replace(text, "int 100 // app id", "int 9", -1), now, "invalid App reference 9") @@ -577,7 +590,8 @@ byte 0x414c474f "no such address") // check special case account idx == 0 => sender - ledger.NewApp(now.Txn.Txn.Sender, 100, basics.AppParams{}) + ledger.NewApp(now.TxnGroup[0].Txn.Sender, 100, basics.AppParams{}) + ledger.NewLocals(now.TxnGroup[0].Txn.Sender, 100) text = `int 0 // account idx int 100 // app id txn ApplicationArgs 0 @@ -588,15 +602,15 @@ exist: byte 0x414c474f ==` - ledger.NewLocal(now.Txn.Txn.Sender, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) + ledger.NewLocal(now.TxnGroup[0].Txn.Sender, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) testApp(t, text, now) testApp(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), now) testApp(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui02\"", -1), now, "invalid Account reference") // check reading state of other app - ledger.NewApp(now.Txn.Txn.Sender, 56, basics.AppParams{}) - ledger.NewApp(now.Txn.Txn.Sender, 100, basics.AppParams{}) + ledger.NewApp(now.TxnGroup[0].Txn.Sender, 56, basics.AppParams{}) + ledger.NewApp(now.TxnGroup[0].Txn.Sender, 100, basics.AppParams{}) text = `int 0 // account idx int 56 // app id txn ApplicationArgs 0 @@ -607,7 +621,8 @@ exist: byte 0x414c474f ==` - ledger.NewLocal(now.Txn.Txn.Sender, 56, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) + ledger.NewLocals(now.TxnGroup[0].Txn.Sender, 56) + ledger.NewLocal(now.TxnGroup[0].Txn.Sender, 56, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) testApp(t, text, now) // check app_local_get @@ -617,11 +632,12 @@ app_local_get byte 0x414c474f ==` - ledger.NewLocal(now.Txn.Txn.Sender, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) + ledger.NewLocal(now.TxnGroup[0].Txn.Sender, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) + now.TxnGroup[0].Txn.ApplicationID = 100 testApp(t, text, now) testApp(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), now) testProg(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), directRefEnabledVersion-1, - expect{3, "app_local_get arg 0 wanted type uint64..."}) + Expect{3, "app_local_get arg 0 wanted type uint64..."}) testApp(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01\"", -1), now) testApp(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui02\"", -1), now, "invalid Account reference") @@ -633,7 +649,7 @@ app_local_get int 0 ==` - ledger.NewLocal(now.Txn.Txn.Sender, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) + ledger.NewLocal(now.TxnGroup[0].Txn.Sender, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) testApp(t, text, now) } @@ -666,14 +682,14 @@ byte 0x414c474f && ` pre, now, ledger := makeOldAndNewEnv(directRefEnabledVersion - 1) - ledger.NewAccount(now.Txn.Txn.Sender, 1) + ledger.NewAccount(now.TxnGroup[0].Txn.Sender, 1) - now.Txn.Txn.ApplicationID = 100 - now.Txn.Txn.ForeignApps = []basics.AppIndex{now.Txn.Txn.ApplicationID} + now.TxnGroup[0].Txn.ApplicationID = 100 + now.TxnGroup[0].Txn.ForeignApps = []basics.AppIndex{now.TxnGroup[0].Txn.ApplicationID} testApp(t, text, now, "no such app") // create the app and check the value from ApplicationArgs[0] (protocol.PaymentTx) does not exist - ledger.NewApp(now.Txn.Txn.Sender, 100, basics.AppParams{}) + ledger.NewApp(now.TxnGroup[0].Txn.Sender, 100, basics.AppParams{}) testApp(t, text, now, "err opcode") @@ -692,7 +708,8 @@ byte 0x414c474f // check app_global_get default value text = "byte 0x414c474f55; app_global_get; int 0; ==" - ledger.NewLocal(now.Txn.Txn.Sender, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) + ledger.NewLocals(now.TxnGroup[0].Txn.Sender, 100) + ledger.NewLocal(now.TxnGroup[0].Txn.Sender, 100, string(protocol.PaymentTx), basics.TealValue{Type: basics.TealBytesType, Bytes: "ALGO"}) testApp(t, text, now) text = ` @@ -711,15 +728,17 @@ int 4141 // check that even during application creation (Txn.ApplicationID == 0) // we will use the the kvCow if the exact application ID (100) is // specified in the transaction - now.Txn.Txn.ApplicationID = 0 - now.Txn.Txn.ForeignApps = []basics.AppIndex{100} - testApp(t, text, now) + now.TxnGroup[0].Txn.ApplicationID = 0 + now.TxnGroup[0].Txn.ForeignApps = []basics.AppIndex{100} + + testAppFull(t, testProg(t, text, LogicVersion).Program, 0, 100, now) // Direct reference to the current app also works - ledger.NewApp(now.Txn.Txn.Receiver, 100, basics.AppParams{}) - now.Txn.Txn.ForeignApps = []basics.AppIndex{} - testApp(t, strings.Replace(text, "int 1 // ForeignApps index", "int 100", -1), now) - testApp(t, strings.Replace(text, "int 1 // ForeignApps index", "global CurrentApplicationID", -1), now) + now.TxnGroup[0].Txn.ForeignApps = []basics.AppIndex{} + testAppFull(t, testProg(t, strings.Replace(text, "int 1 // ForeignApps index", "int 100", -1), LogicVersion).Program, + 0, 100, now) + testAppFull(t, testProg(t, strings.Replace(text, "int 1 // ForeignApps index", "global CurrentApplicationID", -1), LogicVersion).Program, + 0, 100, now) } const assetsTestTemplate = `int 0//account @@ -848,23 +867,23 @@ func TestAssets(t *testing.T) { func testAssetsByVersion(t *testing.T, assetsTestProgram string, version uint64) { for _, field := range AssetHoldingFieldNames { - fs := assetHoldingFieldSpecByName[field] + fs := AssetHoldingFieldSpecByName[field] if fs.version <= version && !strings.Contains(assetsTestProgram, field) { t.Errorf("TestAssets missing field %v", field) } } for _, field := range AssetParamsFieldNames { - fs := assetParamsFieldSpecByName[field] + fs := AssetParamsFieldSpecByName[field] if fs.version <= version && !strings.Contains(assetsTestProgram, field) { t.Errorf("TestAssets missing field %v", field) } } txn := makeSampleTxn() - pre := defaultEvalParamsWithVersion(nil, &txn, directRefEnabledVersion-1) + pre := defaultEvalParamsWithVersion(&txn, directRefEnabledVersion-1) require.GreaterOrEqual(t, version, uint64(directRefEnabledVersion)) - now := defaultEvalParamsWithVersion(nil, &txn, version) - ledger := logictest.MakeLedger( + now := defaultEvalParamsWithVersion(&txn, version) + ledger := MakeLedger( map[basics.Address]uint64{ txn.Txn.Sender: 1, }, @@ -881,7 +900,7 @@ func testAssetsByVersion(t *testing.T, assetsTestProgram string, version uint64) // it wasn't legal to use a direct ref for account testProg(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 54; asset_holding_get AssetBalance`, - directRefEnabledVersion-1, expect{3, "asset_holding_get AssetBalance arg 0 wanted type uint64..."}) + directRefEnabledVersion-1, Expect{3, "asset_holding_get AssetBalance arg 0 wanted type uint64..."}) // but it is now (empty asset yields 0,0 on stack) testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 55; asset_holding_get AssetBalance; ==`, now) // This is receiver, who is in Assets array @@ -924,7 +943,7 @@ func testAssetsByVersion(t *testing.T, assetsTestProgram string, version uint64) testApp(t, strings.Replace(assetsTestProgram, "int 55", "int 0", -1), now) // but old code cannot - testProg(t, strings.Replace(assetsTestProgram, "int 0//account", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), directRefEnabledVersion-1, expect{3, "asset_holding_get AssetBalance arg 0 wanted type uint64..."}) + testProg(t, strings.Replace(assetsTestProgram, "int 0//account", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), directRefEnabledVersion-1, Expect{3, "asset_holding_get AssetBalance arg 0 wanted type uint64..."}) if version < 5 { // Can't run these with AppCreator anyway @@ -954,7 +973,7 @@ intc_2 // 1 ops := testProg(t, source, version) require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_holding_get"].Opcode, ops.Program[8]) ops.Program[9] = 0x02 - _, err := EvalStateful(ops.Program, now) + _, err := EvalApp(ops.Program, 0, 0, now) require.Error(t, err) require.Contains(t, err.Error(), "invalid asset_holding_get field 2") @@ -979,7 +998,7 @@ intc_1 ops = testProg(t, source, version) require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_params_get"].Opcode, ops.Program[6]) ops.Program[7] = 0x20 - _, err = EvalStateful(ops.Program, now) + _, err = EvalApp(ops.Program, 0, 0, now) require.Error(t, err) require.Contains(t, err.Error(), "invalid asset_params_get field 32") @@ -1041,9 +1060,9 @@ intc_1 func TestAppParams(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - ep, ledger := makeSampleEnv() - ledger.NewAccount(ep.Txn.Txn.Sender, 1) - ledger.NewApp(ep.Txn.Txn.Sender, 100, basics.AppParams{}) + ep, tx, ledger := makeSampleEnv() + ledger.NewAccount(tx.Sender, 1) + ledger.NewApp(tx.Sender, 100, basics.AppParams{}) /* app id is in ForeignApps, but does not exist */ source := "int 56; app_params_get AppExtraProgramPages; int 0; ==; assert; int 0; ==" @@ -1053,6 +1072,29 @@ func TestAppParams(t *testing.T) { testApp(t, source, ep) } +func TestAcctParams(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + ep, tx, ledger := makeSampleEnv() + + source := "int 0; acct_params_get AcctBalance; !; assert; int 0; ==" + testApp(t, source, ep) + + source = "int 0; acct_params_get AcctMinBalance; !; assert; int 1001; ==" + testApp(t, source, ep) + + ledger.NewAccount(tx.Sender, 42) + + source = "int 0; acct_params_get AcctBalance; assert; int 42; ==" + testApp(t, source, ep) + + source = "int 0; acct_params_get AcctMinBalance; assert; int 1001; ==" + testApp(t, source, ep) + + source = "int 0; acct_params_get AcctAuthAddr; assert; global ZeroAddress; ==" + testApp(t, source, ep) +} + func TestAppLocalReadWriteDeleteErrors(t *testing.T) { partitiontest.PartitionTest(t) @@ -1116,16 +1158,12 @@ intc_1 ops := testProg(t, source, AssemblerMaxVersion) txn := makeSampleTxn() - ep := defaultEvalParams(nil, nil) - ep.Txn = &txn - ep.Txn.Txn.ApplicationID = 100 - err := CheckStateful(ops.Program, ep) + txn.Txn.ApplicationID = 100 + ep := defaultEvalParams(&txn) + err := CheckContract(ops.Program, ep) require.NoError(t, err) - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "ledger not available") - ledger := logictest.MakeLedger( + ledger := MakeLedger( map[basics.Address]uint64{ txn.Txn.Sender: 1, }, @@ -1135,19 +1173,20 @@ intc_1 saved := ops.Program[firstCmdOffset] require.Equal(t, OpsByName[0]["intc_0"].Opcode, saved) ops.Program[firstCmdOffset] = OpsByName[0]["intc_1"].Opcode - _, err = EvalStateful(ops.Program, ep) + _, err = EvalApp(ops.Program, 0, 100, ep) require.Error(t, err) require.Contains(t, err.Error(), "invalid Account reference 100") ops.Program[firstCmdOffset] = saved - _, err = EvalStateful(ops.Program, ep) + _, err = EvalApp(ops.Program, 0, 100, ep) require.Error(t, err) require.Contains(t, err.Error(), "no app for account") ledger.NewApp(txn.Txn.Sender, 100, basics.AppParams{}) + ledger.NewLocals(txn.Txn.Sender, 100) if name == "read" { - _, err = EvalStateful(ops.Program, ep) + _, err = EvalApp(ops.Program, 0, 100, ep) require.Error(t, err) require.Contains(t, err.Error(), "err opcode") // no such key } @@ -1156,11 +1195,10 @@ intc_1 ledger.NewLocal(txn.Txn.Sender, 100, "ALGOA", basics.TealValue{Type: basics.TealUintType, Uint: 1}) ledger.Reset() - pass, err := EvalStateful(ops.Program, ep) + pass, err := EvalApp(ops.Program, 0, 100, ep) require.NoError(t, err) require.True(t, pass) - delta, err := ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta := ep.TxnGroup[0].EvalDelta require.Empty(t, delta.GlobalDelta) expLocal := 1 if name == "read" { @@ -1176,17 +1214,17 @@ func TestAppLocalStateReadWrite(t *testing.T) { t.Parallel() - ep := defaultEvalParams(nil, nil) txn := makeSampleTxn() txn.Txn.ApplicationID = 100 - ep.Txn = &txn - ledger := logictest.MakeLedger( + ep := defaultEvalParams(&txn) + ledger := MakeLedger( map[basics.Address]uint64{ txn.Txn.Sender: 1, }, ) ep.Ledger = ledger ledger.NewApp(txn.Txn.Sender, 100, basics.AppParams{}) + ledger.NewLocals(txn.Txn.Sender, 100) // write int and bytes values source := `int 0 // account @@ -1217,15 +1255,7 @@ int 0x77 == && ` - ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion) - require.NoError(t, err) - err = CheckStateful(ops.Program, ep) - require.NoError(t, err) - pass, err := EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err := ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta := testApp(t, source, ep) require.Empty(t, 0, delta.GlobalDelta) require.Len(t, delta.LocalDeltas, 1) @@ -1260,14 +1290,7 @@ int 0x77 algoValue := basics.TealValue{Type: basics.TealUintType, Uint: 0x77} ledger.NewLocal(txn.Txn.Sender, 100, "ALGO", algoValue) - ops = testProg(t, source, AssemblerMaxVersion) - err = CheckStateful(ops.Program, ep) - require.NoError(t, err) - pass, err = EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Empty(t, delta.GlobalDelta) require.Empty(t, delta.LocalDeltas) @@ -1296,12 +1319,7 @@ exist2: ledger.NewLocal(txn.Txn.Sender, 100, "ALGO", algoValue) ledger.NoLocal(txn.Txn.Sender, 100, "ALGOA") - ops = testProg(t, source, AssemblerMaxVersion) - pass, err = EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Empty(t, delta.GlobalDelta) require.Empty(t, delta.LocalDeltas) @@ -1316,12 +1334,7 @@ int 1 ledger.NewLocal(txn.Txn.Sender, 100, "ALGO", algoValue) ledger.NoLocal(txn.Txn.Sender, 100, "ALGOA") - ops = testProg(t, source, AssemblerMaxVersion) - pass, err = EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Empty(t, delta.GlobalDelta) require.Len(t, delta.LocalDeltas, 1) require.Len(t, delta.LocalDeltas[0], 1) @@ -1348,12 +1361,7 @@ int 0x78 ledger.NewLocal(txn.Txn.Sender, 100, "ALGO", algoValue) ledger.NoLocal(txn.Txn.Sender, 100, "ALGOA") - ops = testProg(t, source, AssemblerMaxVersion) - pass, err = EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Empty(t, delta.GlobalDelta) require.Len(t, delta.LocalDeltas, 1) require.Len(t, delta.LocalDeltas[0], 1) @@ -1378,12 +1386,7 @@ app_local_put ledger.NewLocal(txn.Txn.Sender, 100, "ALGO", algoValue) ledger.NoLocal(txn.Txn.Sender, 100, "ALGOA") - ops = testProg(t, source, AssemblerMaxVersion) - pass, err = EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Empty(t, delta.GlobalDelta) require.Len(t, delta.LocalDeltas, 1) require.Len(t, delta.LocalDeltas[0], 1) @@ -1414,17 +1417,10 @@ int 1 ledger.NewLocal(txn.Txn.Sender, 100, "ALGO", algoValue) ledger.NoLocal(txn.Txn.Sender, 100, "ALGOA") - ledger.NewAccount(ep.Txn.Txn.Receiver, 500) + ledger.NewAccount(txn.Txn.Receiver, 500) ledger.NewLocals(txn.Txn.Receiver, 100) - ops = testProg(t, source, AssemblerMaxVersion) - err = CheckStateful(ops.Program, ep) - require.NoError(t, err) - pass, err = EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Empty(t, delta.GlobalDelta) require.Len(t, delta.LocalDeltas, 2) require.Len(t, delta.LocalDeltas[0], 2) @@ -1482,42 +1478,21 @@ int 1 ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion) require.NoError(t, err) - txn := makeSampleTxn() - ep := defaultEvalParams(nil, nil) - ep.Txn = &txn - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "ledger not available") - - ledger := logictest.MakeLedger( - map[basics.Address]uint64{ - txn.Txn.Sender: 1, - }, - ) - ep.Ledger = ledger - - txn.Txn.ApplicationID = 100 - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "no such app") + ep, txn, ledger := makeSampleEnv() + txn.ApplicationID = basics.AppIndex(100) + testAppBytes(t, ops.Program, ep, "no such app") - ledger.NewApp(txn.Txn.Sender, 100, makeApp(0, 0, 1, 0)) + ledger.NewApp(txn.Sender, 100, makeApp(0, 0, 1, 0)) // a special test for read if name == "read" { - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "err opcode") // no such key + testAppBytes(t, ops.Program, ep, "err opcode") // no such key } ledger.NewGlobal(100, "ALGO", basics.TealValue{Type: basics.TealUintType, Uint: 0x77}) ledger.Reset() - pass, err := EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err := ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta := testAppBytes(t, ops.Program, ep) require.Empty(t, delta.LocalDeltas) }) } @@ -1587,12 +1562,11 @@ int 0x77 == && ` - ep := defaultEvalParams(nil, nil) txn := makeSampleTxn() txn.Txn.ApplicationID = 100 txn.Txn.ForeignApps = []basics.AppIndex{txn.Txn.ApplicationID} - ep.Txn = &txn - ledger := logictest.MakeLedger( + ep := defaultEvalParams(&txn) + ledger := MakeLedger( map[basics.Address]uint64{ txn.Txn.Sender: 1, }, @@ -1600,15 +1574,7 @@ int 0x77 ep.Ledger = ledger ledger.NewApp(txn.Txn.Sender, 100, basics.AppParams{}) - ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion) - require.NoError(t, err) - err = CheckStateful(ops.Program, ep) - require.NoError(t, err) - pass, err := EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err := ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta := testApp(t, source, ep) require.Len(t, delta.GlobalDelta, 2) require.Empty(t, delta.LocalDeltas) @@ -1637,13 +1603,7 @@ int 0x77 algoValue := basics.TealValue{Type: basics.TealUintType, Uint: 0x77} ledger.NewGlobal(100, "ALGO", algoValue) - ops = testProg(t, source, AssemblerMaxVersion) - pass, err = EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) - + delta = testApp(t, source, ep) require.Empty(t, delta.GlobalDelta) require.Empty(t, delta.LocalDeltas) @@ -1667,12 +1627,7 @@ int 0x77 ledger.NoGlobal(100, "ALGOA") ledger.NewGlobal(100, "ALGO", algoValue) - ops = testProg(t, source, AssemblerMaxVersion) - pass, err = EvalStateful(ops.Program, ep) - require.NoError(t, err) - require.True(t, pass) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Empty(t, delta.GlobalDelta) require.Empty(t, delta.LocalDeltas) @@ -1712,20 +1667,7 @@ byte 0x414c474f ledger.NoGlobal(100, "ALGOA") ledger.NewGlobal(100, "ALGO", algoValue) - ops = testProg(t, source, AssemblerMaxVersion) - sb := strings.Builder{} - ep.Trace = &sb - err = CheckStateful(ops.Program, ep) - require.NoError(t, err) - pass, err = EvalStateful(ops.Program, ep) - if !pass { - t.Log(hex.EncodeToString(ops.Program)) - t.Log(sb.String()) - } - require.NoError(t, err) - require.True(t, pass) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Len(t, delta.GlobalDelta, 2) require.Empty(t, delta.LocalDeltas) @@ -1759,12 +1701,11 @@ ok2: byte "myval" == ` - ep := defaultEvalParams(nil, nil) txn := makeSampleTxn() txn.Txn.ApplicationID = 100 txn.Txn.ForeignApps = []basics.AppIndex{txn.Txn.ApplicationID, 101} - ep.Txn = &txn - ledger := logictest.MakeLedger( + ep := defaultEvalParams(&txn) + ledger := MakeLedger( map[basics.Address]uint64{ txn.Txn.Sender: 1, }, @@ -1806,11 +1747,10 @@ app_global_get int 7 == ` - ep := defaultEvalParams(nil, nil) txn := makeSampleTxn() txn.Txn.ApplicationID = 100 - ep.Txn = &txn - ledger := logictest.MakeLedger( + ep := defaultEvalParams(&txn) + ledger := MakeLedger( map[basics.Address]uint64{ txn.Txn.Sender: 1, }, @@ -1853,11 +1793,10 @@ err ok: int 1 ` - ep := defaultEvalParams(nil, nil) txn := makeSampleTxn() txn.Txn.ApplicationID = 100 - ep.Txn = &txn - ledger := logictest.MakeLedger( + ep := defaultEvalParams(&txn) + ledger := MakeLedger( map[basics.Address]uint64{ txn.Txn.Sender: 1, }, @@ -1884,7 +1823,7 @@ byte 0x414c474f app_global_get_ex == // two zeros ` - ep.Txn.Txn.ForeignApps = []basics.AppIndex{txn.Txn.ApplicationID} + ep.TxnGroup[0].Txn.ForeignApps = []basics.AppIndex{txn.Txn.ApplicationID} delta = testApp(t, source, ep) require.Len(t, delta.GlobalDelta, 1) vd := delta.GlobalDelta["ALGO"] @@ -2020,38 +1959,33 @@ err ok: int 1 ` - ep := defaultEvalParams(nil, nil) txn := makeSampleTxn() txn.Txn.ApplicationID = 100 - ep.Txn = &txn - ledger := logictest.MakeLedger( + ep := defaultEvalParams(&txn) + ledger := MakeLedger( map[basics.Address]uint64{ txn.Txn.Sender: 1, }, ) ep.Ledger = ledger ledger.NewApp(txn.Txn.Sender, 100, basics.AppParams{}) + ledger.NewLocals(txn.Txn.Sender, 100) ledger.NewAccount(txn.Txn.Receiver, 1) ledger.NewLocals(txn.Txn.Receiver, 100) sb := strings.Builder{} ep.Trace = &sb - testApp(t, source, ep) - delta, err := ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta := testApp(t, source, ep) require.Equal(t, 0, len(delta.GlobalDelta)) require.Equal(t, 2, len(delta.LocalDeltas)) ledger.Reset() // test that app_local_put and _app_local_del can use byte addresses - testApp(t, strings.Replace(source, "int 0 // sender", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), ep) - // But won't compile in old teal + delta = testApp(t, strings.Replace(source, "int 0 // sender", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), ep) + // But won't even compile in old teal testProg(t, strings.Replace(source, "int 0 // sender", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), directRefEnabledVersion-1, - expect{4, "app_local_put arg 0 wanted..."}, expect{11, "app_local_del arg 0 wanted..."}) - - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + Expect{4, "app_local_put arg 0 wanted..."}, Expect{11, "app_local_del arg 0 wanted..."}) require.Equal(t, 0, len(delta.GlobalDelta)) require.Equal(t, 2, len(delta.LocalDeltas)) @@ -2075,9 +2009,7 @@ app_local_get_ex == // two zeros ` - testApp(t, source, ep) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Equal(t, 0, len(delta.GlobalDelta)) require.Equal(t, 1, len(delta.LocalDeltas)) vd := delta.LocalDeltas[0]["ALGO"] @@ -2105,9 +2037,7 @@ byte 0x414c474f41 int 0x78 app_local_put ` - testApp(t, source, ep) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Equal(t, 0, len(delta.GlobalDelta)) require.Equal(t, 1, len(delta.LocalDeltas)) vd = delta.LocalDeltas[0]["ALGOA"] @@ -2131,9 +2061,7 @@ int 0x78 app_local_put int 1 ` - testApp(t, source, ep) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Equal(t, 0, len(delta.GlobalDelta)) require.Equal(t, 1, len(delta.LocalDeltas)) vd = delta.LocalDeltas[0]["ALGO"] @@ -2160,9 +2088,7 @@ byte 0x414c474f app_local_del int 1 ` - testApp(t, source, ep) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Equal(t, 0, len(delta.GlobalDelta)) require.Equal(t, 1, len(delta.LocalDeltas)) vd = delta.LocalDeltas[0]["ALGO"] @@ -2189,9 +2115,7 @@ byte 0x414c474f41 app_local_del int 1 ` - testApp(t, source, ep) - delta, err = ledger.GetDelta(&ep.Txn.Txn) - require.NoError(t, err) + delta = testApp(t, source, ep) require.Equal(t, 0, len(delta.GlobalDelta)) require.Equal(t, 1, len(delta.LocalDeltas)) require.Equal(t, 1, len(delta.LocalDeltas[0])) @@ -2200,22 +2124,17 @@ int 1 func TestEnumFieldErrors(t *testing.T) { partitiontest.PartitionTest(t) - ep := defaultEvalParams(nil, nil) - source := `txn Amount` - origTxnType := TxnFieldTypes[Amount] - TxnFieldTypes[Amount] = StackBytes + origSpec := txnFieldSpecByField[Amount] + changed := origSpec + changed.ftype = StackBytes + txnFieldSpecByField[Amount] = changed defer func() { - TxnFieldTypes[Amount] = origTxnType + txnFieldSpecByField[Amount] = origSpec }() - ops := testProg(t, source, AssemblerMaxVersion) - _, err := Eval(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "Amount expected field type is []byte but got uint64") - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "Amount expected field type is []byte but got uint64") + testLogic(t, source, AssemblerMaxVersion, defaultEvalParams(nil), "Amount expected field type is []byte but got uint64") + testApp(t, source, defaultEvalParams(nil), "Amount expected field type is []byte but got uint64") source = `global MinTxnFee` @@ -2227,20 +2146,11 @@ func TestEnumFieldErrors(t *testing.T) { globalFieldSpecByField[MinTxnFee] = origMinTxnFs }() - ops = testProg(t, source, AssemblerMaxVersion) - _, err = Eval(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "MinTxnFee expected field type is []byte but got uint64") - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "MinTxnFee expected field type is []byte but got uint64") + testLogic(t, source, AssemblerMaxVersion, defaultEvalParams(nil), "MinTxnFee expected field type is []byte but got uint64") + testApp(t, source, defaultEvalParams(nil), "MinTxnFee expected field type is []byte but got uint64") - txn := makeSampleTxn() - ledger := logictest.MakeLedger( - map[basics.Address]uint64{ - txn.Txn.Sender: 1, - }, - ) + ep, tx, ledger := makeSampleEnv() + ledger.NewAccount(tx.Sender, 1) params := basics.AssetParams{ Total: 1000, Decimals: 2, @@ -2248,20 +2158,17 @@ func TestEnumFieldErrors(t *testing.T) { UnitName: "ALGO", AssetName: "", URL: string(protocol.PaymentTx), - Manager: txn.Txn.Sender, - Reserve: txn.Txn.Receiver, - Freeze: txn.Txn.Receiver, - Clawback: txn.Txn.Receiver, + Manager: tx.Sender, + Reserve: tx.Receiver, + Freeze: tx.Receiver, + Clawback: tx.Receiver, } - ledger.NewAsset(txn.Txn.Sender, 55, params) - - ep.Txn = &txn - ep.Ledger = ledger + ledger.NewAsset(tx.Sender, 55, params) source = `int 0 int 55 asset_holding_get AssetBalance -pop +assert ` origBalanceFs := assetHoldingFieldSpecByField[AssetBalance] badBalanceFs := origBalanceFs @@ -2271,14 +2178,11 @@ pop assetHoldingFieldSpecByField[AssetBalance] = origBalanceFs }() - ops = testProg(t, source, AssemblerMaxVersion) - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "AssetBalance expected field type is []byte but got uint64") + testApp(t, source, ep, "AssetBalance expected field type is []byte but got uint64") source = `int 0 asset_params_get AssetTotal -pop +assert ` origTotalFs := assetParamsFieldSpecByField[AssetTotal] badTotalFs := origTotalFs @@ -2288,44 +2192,32 @@ pop assetParamsFieldSpecByField[AssetTotal] = origTotalFs }() - ops = testProg(t, source, AssemblerMaxVersion) - _, err = EvalStateful(ops.Program, ep) - require.Error(t, err) - require.Contains(t, err.Error(), "AssetTotal expected field type is []byte but got uint64") + testApp(t, source, ep, "AssetTotal expected field type is []byte but got uint64") } func TestReturnTypes(t *testing.T) { partitiontest.PartitionTest(t) - // Ensure all opcodes return values they supposed to according to the OpSpecs table + // Ensure all opcodes return values they are supposed to according to the OpSpecs table t.Parallel() typeToArg := map[StackType]string{ StackUint64: "int 1\n", StackAny: "int 1\n", StackBytes: "byte 0x33343536\n", } - ep := defaultEvalParams(nil, nil) - txn := makeSampleTxn() - txn.Txn.Type = protocol.ApplicationCallTx - txgroup := makeSampleTxnGroup(txn) - ep.Txn = &txn - ep.TxnGroup = txgroup - ep.Txn.Txn.ApplicationID = 1 - ep.Txn.Txn.ForeignApps = []basics.AppIndex{txn.Txn.ApplicationID} - ep.Txn.Txn.ForeignAssets = []basics.AssetIndex{basics.AssetIndex(1), basics.AssetIndex(1)} - ep.GroupIndex = 1 - ep.PastSideEffects = MakePastSideEffects(len(txgroup)) - txn.Lsig.Args = [][]byte{ + ep, tx, ledger := makeSampleEnv() + tx.Type = protocol.ApplicationCallTx + tx.ApplicationID = 1 + tx.ForeignApps = []basics.AppIndex{tx.ApplicationID} + tx.ForeignAssets = []basics.AssetIndex{basics.AssetIndex(1), basics.AssetIndex(1)} + ep.TxnGroup[0].Lsig.Args = [][]byte{ []byte("aoeu"), []byte("aoeu"), []byte("aoeu2"), []byte("aoeu3"), } - ledger := logictest.MakeLedger( - map[basics.Address]uint64{ - txn.Txn.Sender: 1, - }, - ) + ep.pastScratch[0] = &scratchSpace{} // for gload + ledger.NewAccount(tx.Sender, 1) params := basics.AssetParams{ Total: 1000, Decimals: 2, @@ -2333,23 +2225,20 @@ func TestReturnTypes(t *testing.T) { UnitName: "ALGO", AssetName: "", URL: string(protocol.PaymentTx), - Manager: txn.Txn.Sender, - Reserve: txn.Txn.Receiver, - Freeze: txn.Txn.Receiver, - Clawback: txn.Txn.Receiver, + Manager: tx.Sender, + Reserve: tx.Receiver, + Freeze: tx.Receiver, + Clawback: tx.Receiver, } - ledger.NewAsset(txn.Txn.Sender, 1, params) - ledger.NewApp(txn.Txn.Sender, 1, basics.AppParams{}) - ledger.SetTrackedCreatable(0, basics.CreatableLocator{Index: 1}) - ledger.NewAccount(txn.Txn.Receiver, 1000000) - ledger.NewLocals(txn.Txn.Receiver, 1) + ledger.NewAsset(tx.Sender, 1, params) + ledger.NewApp(tx.Sender, 1, basics.AppParams{}) + ledger.NewAccount(tx.Receiver, 1000000) + ledger.NewLocals(tx.Receiver, 1) key, err := hex.DecodeString("33343536") require.NoError(t, err) algoValue := basics.TealValue{Type: basics.TealUintType, Uint: 0x77} - ledger.NewLocal(txn.Txn.Receiver, 1, string(key), algoValue) - ledger.NewAccount(basics.AppIndex(1).Address(), 1000000) - - ep.Ledger = ledger + ledger.NewLocal(tx.Receiver, 1, string(key), algoValue) + ledger.NewAccount(appAddr(1), 1000000) specialCmd := map[string]string{ "txn": "txn Sender", @@ -2362,6 +2251,7 @@ func TestReturnTypes(t *testing.T) { "store": "store 0", "gload": "gload 0 0", "gloads": "gloads 0", + "gloadss": "pop; pop; int 0; int 1; gloadss", // Needs txn index = 0 to work "gaid": "gaid 0", "dig": "dig 0", "cover": "cover 0", @@ -2380,19 +2270,26 @@ func TestReturnTypes(t *testing.T) { "asset_params_get": "asset_params_get AssetTotal", "asset_holding_get": "asset_holding_get AssetBalance", "gtxns": "gtxns Sender", - "gtxnsa": "gtxnsa ApplicationArgs 0", + "gtxnsa": "pop; int 0; gtxnsa ApplicationArgs 0", "pushint": "pushint 7272", "pushbytes": `pushbytes "jojogoodgorilla"`, "app_params_get": "app_params_get AppGlobalNumUint", + "acct_params_get": "acct_params_get AcctMinBalance", "extract": "extract 0 2", "txnas": "txnas ApplicationArgs", "gtxnas": "gtxnas 0 ApplicationArgs", "gtxnsas": "pop; pop; int 0; int 0; gtxnsas ApplicationArgs", "args": "args", "itxn": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; itxn CreatedAssetID", - // This next one is a cop out. Can't use itxna Logs until we have inner appl - "itxna": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; itxn NumLogs", - "base64_decode": `pushbytes "YWJjMTIzIT8kKiYoKSctPUB+"; base64_decode StdEncoding; pushbytes "abc123!?$*&()'-=@~"; ==; pushbytes "YWJjMTIzIT8kKiYoKSctPUB-"; base64_decode URLEncoding; pushbytes "abc123!?$*&()'-=@~"; ==; &&; assert`, + "itxna": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; itxna Accounts 0", + "gitxn": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; gitxn 0 Sender", + "gitxna": "itxn_begin; int pay; itxn_field TypeEnum; itxn_submit; gitxna 0 Accounts 0", + "base64_decode": `pushbytes "YWJjMTIzIT8kKiYoKSctPUB+"; base64_decode StdEncoding; pushbytes "abc123!?$*&()'-=@~"; ==; pushbytes "YWJjMTIzIT8kKiYoKSctPUB-"; base64_decode URLEncoding; pushbytes "abc123!?$*&()'-=@~"; ==; &&; assert`, + } + + /* Make sure the specialCmd tests the opcode in question */ + for opcode, cmd := range specialCmd { + assert.Contains(t, cmd, opcode) } // these require special input data and tested separately @@ -2423,19 +2320,23 @@ func TestReturnTypes(t *testing.T) { source := sb.String() ops := testProg(t, source, AssemblerMaxVersion) - var trace strings.Builder - ep.Trace = &trace - var cx EvalContext cx.EvalParams = ep cx.runModeFlags = m + cx.appID = 1 + + // These set conditions for some ops that examine the group. + // This convinces them all to work. Revisit. + cx.Txn = &ep.TxnGroup[0] + cx.GroupIndex = 1 + cx.TxnGroup[0].ConfigAsset = 100 eval(ops.Program, &cx) require.Equal( t, len(spec.Returns), len(cx.stack), - fmt.Sprintf("\n%s%s expected to return %d values but stack has %d", trace.String(), spec.Name, len(spec.Returns), len(cx.stack)), + fmt.Sprintf("\n%s%s expected to return %d values but stack is %v", ep.Trace.String(), spec.Name, len(spec.Returns), cx.stack), ) for i := 0; i < len(spec.Returns); i++ { sp := len(cx.stack) - 1 - i @@ -2454,7 +2355,7 @@ func TestReturnTypes(t *testing.T) { func TestRound(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - ep, _ := makeSampleEnv() + ep, _, _ := makeSampleEnv() source := "global Round; int 1; >=" testApp(t, source, ep) } @@ -2462,7 +2363,7 @@ func TestRound(t *testing.T) { func TestLatestTimestamp(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - ep, _ := makeSampleEnv() + ep, _, _ := makeSampleEnv() source := "global LatestTimestamp; int 1; >=" testApp(t, source, ep) } @@ -2470,8 +2371,8 @@ func TestLatestTimestamp(t *testing.T) { func TestCurrentApplicationID(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - ep, ledger := makeSampleEnv() - ledger.NewApp(ep.Txn.Txn.Receiver, 42, basics.AppParams{}) + ep, tx, _ := makeSampleEnv() + tx.ApplicationID = 42 source := "global CurrentApplicationID; int 42; ==" testApp(t, source, ep) } @@ -2479,7 +2380,7 @@ func TestCurrentApplicationID(t *testing.T) { func TestAppLoop(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - ep, _ := makeSampleEnv() + ep, _, _ := makeSampleEnv() stateful := "global CurrentApplicationID; pop;" @@ -2504,25 +2405,29 @@ func TestPooledAppCallsVerifyOp(t *testing.T) { pop int 1` - ep, _ := makeSampleEnv() - ep.Proto.EnableAppCostPooling = true - ep.PooledApplicationBudget = new(uint64) + ledger := MakeLedger(nil) + call := transactions.SignedTxn{Txn: transactions.Transaction{Type: protocol.ApplicationCallTx}} // Simulate test with 2 grouped txn - *ep.PooledApplicationBudget = uint64(ep.Proto.MaxAppProgramCost * 2) - testApp(t, source, ep, "pc=107 dynamic cost budget exceeded, executing ed25519verify: remaining budget is 1400 but program cost was 1905") + testApps(t, []string{source, ""}, []transactions.SignedTxn{call, call}, LogicVersion, ledger, + Expect{0, "pc=107 dynamic cost budget exceeded, executing ed25519verify: remaining budget is 1400 but program cost was 1905"}) // Simulate test with 3 grouped txn - *ep.PooledApplicationBudget = uint64(ep.Proto.MaxAppProgramCost * 3) - testApp(t, source, ep) + testApps(t, []string{source, "", ""}, []transactions.SignedTxn{call, call, call}, LogicVersion, ledger) } -func TestAppAddress(t *testing.T) { - ep, ledger := makeSampleEnv() - ledger.NewApp(ep.Txn.Txn.Receiver, 888, basics.AppParams{}) - source := fmt.Sprintf("global CurrentApplicationAddress; addr %s; ==;", basics.AppIndex(888).Address()) +func appAddr(id int) basics.Address { + return basics.AppIndex(id).Address() +} + +func TestAppInfo(t *testing.T) { + ep, tx, ledger := makeSampleEnv() + require.Equal(t, 888, int(tx.ApplicationID)) + ledger.NewApp(tx.Receiver, 888, basics.AppParams{}) + testApp(t, "global CurrentApplicationID; int 888; ==;", ep) + source := fmt.Sprintf("global CurrentApplicationAddress; addr %s; ==;", appAddr(888)) testApp(t, source, ep) - source = fmt.Sprintf("int 0; app_params_get AppAddress; assert; addr %s; ==;", basics.AppIndex(888).Address()) + source = fmt.Sprintf("int 0; app_params_get AppAddress; assert; addr %s; ==;", appAddr(888)) testApp(t, source, ep) // To document easy construction: @@ -2531,3 +2436,54 @@ func TestAppAddress(t *testing.T) { source = fmt.Sprintf("int 0; app_params_get AppAddress; assert; addr %s; ==;", a) testApp(t, source, ep) } + +func TestBudget(t *testing.T) { + ep := defaultEvalParams(nil) + source := ` +global OpcodeBudget +int 699 +== +assert +global OpcodeBudget +int 695 +== +` + testApp(t, source, ep) +} + +func TestSelfMutate(t *testing.T) { + ep, _, ledger := makeSampleEnv() + + /* In order to test the added protection of mutableAccountReference, we're + going to set up a ledger in which an app account is opted into + itself. That was impossible before v6, and indeed we did not have the + extra mutable reference check then. */ + ledger.NewLocals(basics.AppIndex(888).Address(), 888) + ledger.NewLocal(basics.AppIndex(888).Address(), 888, "hey", + basics.TealValue{Type: basics.TealUintType, Uint: 77}) + + source := ` +global CurrentApplicationAddress +byte "hey" +int 42 +app_local_put +` + testApp(t, source, ep, "invalid Account reference for mutation") + + source = ` +global CurrentApplicationAddress +byte "hey" +app_local_del +` + testApp(t, source, ep, "invalid Account reference for mutation") + + /* But let's just check normal access is working properly. */ + source = ` +global CurrentApplicationAddress +byte "hey" +app_local_get +int 77 +== +` + testApp(t, source, ep) +} |