From b46558797598215cb1926a160d3485ab67ad06c6 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 7 Sep 2021 19:13:21 -0400 Subject: [PATCH 1/4] loads and stores opcodes that take scratch slot from stack --- cmd/goal/clerk.go | 4 +- cmd/tealdbg/local.go | 10 +- cmd/tealdbg/local_test.go | 26 ++--- daemon/algod/api/server/v2/dryrun.go | 2 +- data/transactions/logic/README.md | 10 +- data/transactions/logic/TEAL_opcodes.md | 18 ++- data/transactions/logic/assembler.go | 52 ++++----- data/transactions/logic/assembler_test.go | 6 +- data/transactions/logic/debugger.go | 2 +- data/transactions/logic/doc.go | 6 +- data/transactions/logic/eval.go | 127 +++++++++++++--------- data/transactions/logic/eval_test.go | 35 +++++- data/transactions/logic/opcodes.go | 4 + data/transactions/verify/txn.go | 10 +- ledger/eval.go | 2 +- ledger/eval_test.go | 2 +- 16 files changed, 200 insertions(+), 116 deletions(-) diff --git a/cmd/goal/clerk.go b/cmd/goal/clerk.go index 0581703c0a..571c4d1a48 100644 --- a/cmd/goal/clerk.go +++ b/cmd/goal/clerk.go @@ -1087,7 +1087,7 @@ var dryrunCmd = &cobra.Command{ if uint64(txn.Lsig.Len()) > params.LogicSigMaxSize { reportErrorf("program size too large: %d > %d", len(txn.Lsig.Logic), params.LogicSigMaxSize) } - ep := logic.EvalParams{Txn: &txn, Proto: ¶ms, GroupIndex: i, TxnGroup: txgroup} + ep := logic.EvalParams{Txn: &txn, Proto: ¶ms, GroupIndex: uint64(i), TxnGroup: txgroup} err := logic.Check(txn.Lsig.Logic, ep) if err != nil { reportErrorf("program failed Check: %s", err) @@ -1095,7 +1095,7 @@ var dryrunCmd = &cobra.Command{ sb := strings.Builder{} ep = logic.EvalParams{ Txn: &txn, - GroupIndex: i, + GroupIndex: uint64(i), Proto: ¶ms, Trace: &sb, TxnGroup: txgroup, diff --git a/cmd/tealdbg/local.go b/cmd/tealdbg/local.go index baebcea046..814afae3e4 100644 --- a/cmd/tealdbg/local.go +++ b/cmd/tealdbg/local.go @@ -209,7 +209,7 @@ type evaluation struct { source string offsetToLine map[int]int name string - groupIndex int + groupIndex uint64 pastSideEffects []logic.EvalSideEffects mode modeType aidx basics.AppIndex @@ -387,7 +387,7 @@ func (r *LocalRunner) Setup(dp *DebugParams) (err error) { r.runs[i].source = source } } - r.runs[i].groupIndex = dp.GroupIndex + r.runs[i].groupIndex = uint64(dp.GroupIndex) r.runs[i].pastSideEffects = dp.PastSideEffects r.runs[i].name = dp.ProgramNames[i] @@ -431,7 +431,7 @@ func (r *LocalRunner) Setup(dp *DebugParams) (err error) { if len(stxn.Lsig.Logic) > 0 { run := evaluation{ program: stxn.Lsig.Logic, - groupIndex: gi, + groupIndex: uint64(gi), mode: modeLogicsig, } r.runs = append(r.runs, run) @@ -452,7 +452,7 @@ func (r *LocalRunner) Setup(dp *DebugParams) (err error) { } run := evaluation{ program: stxn.Txn.ApprovalProgram, - groupIndex: gi, + groupIndex: uint64(gi), pastSideEffects: dp.PastSideEffects, mode: modeStateful, aidx: appIdx, @@ -488,7 +488,7 @@ func (r *LocalRunner) Setup(dp *DebugParams) (err error) { } run := evaluation{ program: program, - groupIndex: gi, + groupIndex: uint64(gi), pastSideEffects: dp.PastSideEffects, mode: modeStateful, aidx: appIdx, diff --git a/cmd/tealdbg/local_test.go b/cmd/tealdbg/local_test.go index 6a844963e5..f199d93da3 100644 --- a/cmd/tealdbg/local_test.go +++ b/cmd/tealdbg/local_test.go @@ -571,7 +571,7 @@ func TestDebugFromPrograms(t *testing.T) { err = l.Setup(&dp) a.NoError(err) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.Nil(l.runs[0].ba) a.Equal(modeLogicsig, l.runs[0].mode) a.Empty(l.runs[0].aidx) @@ -587,8 +587,8 @@ func TestDebugFromPrograms(t *testing.T) { err = l.Setup(&dp) a.NoError(err) a.Equal(2, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) - a.Equal(0, l.runs[1].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[1].groupIndex) a.Nil(l.runs[0].ba) a.Equal(modeLogicsig, l.runs[0].mode) a.Empty(l.runs[0].aidx) @@ -617,7 +617,7 @@ func TestRunMode(t *testing.T) { err := l.Setup(&dp) a.NoError(err) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.NotNil(l.runs[0].ba) a.Equal(modeStateful, l.runs[0].mode) @@ -640,7 +640,7 @@ func TestRunMode(t *testing.T) { err = l.Setup(&dp) a.NoError(err) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.Nil(l.runs[0].ba) a.Equal(modeLogicsig, l.runs[0].mode) @@ -659,7 +659,7 @@ func TestRunMode(t *testing.T) { err = l.Setup(&dp) a.NoError(err) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.NotNil(l.runs[0].ba) a.Equal(modeStateful, l.runs[0].mode) @@ -677,7 +677,7 @@ func TestRunMode(t *testing.T) { err = l.Setup(&dp) a.NoError(err) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.Nil(l.runs[0].ba) a.Equal(modeLogicsig, l.runs[0].mode) @@ -738,7 +738,7 @@ func TestDebugFromTxn(t *testing.T) { err = l.Setup(&dp) a.NoError(err) a.Equal(1, len(l.runs)) - a.Equal(1, l.runs[0].groupIndex) + a.Equal(uint64(1), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.Equal([]byte{3}, l.runs[0].program) a.Nil(l.runs[0].ba) @@ -764,7 +764,7 @@ func TestDebugFromTxn(t *testing.T) { a.NoError(err) a.Equal(2, len(l.txnGroup)) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.Equal([]byte{1}, l.runs[0].program) a.NotNil(l.runs[0].ba) @@ -787,7 +787,7 @@ func TestDebugFromTxn(t *testing.T) { a.NoError(err) a.Equal(2, len(l.txnGroup)) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.Equal([]byte{1, 1}, l.runs[0].program) a.NotNil(l.runs[0].ba) @@ -812,7 +812,7 @@ func TestDebugFromTxn(t *testing.T) { a.NoError(err) a.Equal(1, len(l.txnGroup)) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.Equal([]byte{4}, l.runs[0].program) a.NotNil(l.runs[0].ba) @@ -949,7 +949,7 @@ func TestLocalBalanceAdapter(t *testing.T) { a.NoError(err) a.Equal(2, len(l.txnGroup)) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.Equal([]byte{1}, l.runs[0].program) a.NotNil(l.runs[0].ba) @@ -1039,7 +1039,7 @@ func TestLocalBalanceAdapterIndexer(t *testing.T) { a.NoError(err) a.Equal(2, len(l.txnGroup)) a.Equal(1, len(l.runs)) - a.Equal(0, l.runs[0].groupIndex) + a.Equal(uint64(0), l.runs[0].groupIndex) a.NotNil(l.runs[0].eval) a.Equal([]byte{1}, l.runs[0].program) a.NotNil(l.runs[0].ba) diff --git a/daemon/algod/api/server/v2/dryrun.go b/daemon/algod/api/server/v2/dryrun.go index c2dce30961..fed86ce627 100644 --- a/daemon/algod/api/server/v2/dryrun.go +++ b/daemon/algod/api/server/v2/dryrun.go @@ -420,7 +420,7 @@ func doDryrunRequest(dr *DryrunRequest, response *generated.DryrunResponse) { Txn: &stxn, Proto: &proto, TxnGroup: dr.Txns, - GroupIndex: ti, + GroupIndex: uint64(ti), PastSideEffects: pse, PooledApplicationBudget: &pooledAppBudget, } diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index d7802ca479..098641b385 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -233,19 +233,21 @@ Some of these have immediate data in the byte or bytes after the opcode. | `txn f` | push field F of current transaction to stack | | `gtxn t f` | push field F of the Tth transaction in the current group | | `txna f i` | push Ith value of the array field F of the current transaction | +| `txnas f` | push Xth value of the array field F of the current transaction | | `gtxna t f i` | push Ith value of the array field F from the Tth transaction in the current group | +| `gtxnas t f` | push Xth value of the array field F from the Tth transaction in the current group | | `gtxns f` | push field F of the Xth transaction in the current group | | `gtxnsa f i` | push Ith value of the array field F from the Xth transaction in the current group | +| `gtxnsas f` | pop an index A and an index B. push Bth value of the array field F from the Ath transaction in the current group | | `global f` | push value from globals to stack | | `load i` | copy a value from scratch space to the stack | -| `store i` | pop a value from the stack and store to scratch space | +| `loads` | copy a value from the Xth scratch space to the stack | +| `store i` | pop value X. store X to the Ith scratch space | +| `stores` | pop indexes A and B. store A to the Bth scratch space | | `gload t i` | push Ith scratch space index of the Tth transaction in the current group | | `gloads i` | push Ith scratch space index of the Xth transaction in the current group | | `gaid t` | push the ID of the asset or application created in the Tth transaction of the current group | | `gaids` | push the ID of the asset or application created in the Xth transaction of the current group | -| `txnas f` | push Xth value of the array field F of the current transaction | -| `gtxnas t f` | push Xth value of the array field F from the Tth transaction in the current group | -| `gtxnsas f` | pop an index A and an index B. push Bth value of the array field F from the Ath transaction in the current group | | `args` | push Xth LogicSig argument to stack | **Transaction Fields** diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 32747e61ef..8bc1dbc0a3 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -489,7 +489,7 @@ for notes on transaction fields available, see `txn`. If this transaction is _i_ - Opcode: 0x35 {uint8 position in scratch space to store to} - Pops: *... stack*, any - Pushes: _None_ -- pop a value from the stack and store to scratch space +- pop value X. store X to the Ith scratch space ## txna f i @@ -569,6 +569,22 @@ for notes on transaction fields available, see `txn`. If top of stack is _i_, `g `gaids` fails unless the requested transaction created an asset or application and X < GroupIndex. +## loads + +- Opcode: 0x3e +- Pops: *... stack*, uint64 +- Pushes: any +- copy a value from the Xth scratch space to the stack +- LogicSigVersion >= 5 + +## stores + +- Opcode: 0x3f +- Pops: *... stack*, {any A}, {uint64 B} +- Pushes: _None_ +- pop indexes A and B. store A to the Bth scratch space +- LogicSigVersion >= 5 + ## bnz target - Opcode: 0x40 {int16 branch offset, big endian} diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index ca953ee36c..0f3dbc15ef 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -875,16 +875,16 @@ func assembleGtxn(ops *OpStream, spec *OpSpec, args []string) error { return ops.error(err) } if slot > 255 { - return ops.errorf("gtxn transaction index beyond 255: %d", slot) + return ops.errorf("%s transaction index beyond 255: %d", spec.Name, slot) } fs, ok := txnFieldSpecByName[args[1]] if !ok { - return ops.errorf("gtxn unknown field: %#v", args[1]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[1]) } _, ok = txnaFieldSpecByField[fs.field] if ok { - return ops.errorf("found array field %#v in gtxn op", args[1]) + return ops.errorf("found array field %#v in %s op", args[1], spec.Name) } if fs.version > ops.Version { return ops.errorf("field %#v available in version %d. Missed #pragma version?", args[1], fs.version) @@ -905,38 +905,38 @@ func assembleGtxn2(ops *OpStream, spec *OpSpec, args []string) error { gtxna := OpsByName[ops.Version]["gtxna"] return assembleGtxna(ops, >xna, args) } - return ops.error("gtxn expects two or three arguments") + return ops.errorf("%s expects two or three arguments", spec.Name) } func assembleGtxna(ops *OpStream, spec *OpSpec, args []string) error { if len(args) != 3 { - return ops.error("gtxna expects three arguments") + return ops.errorf("%s expects three arguments", spec.Name) } slot, err := strconv.ParseUint(args[0], 0, 64) if err != nil { return ops.error(err) } if slot > 255 { - return ops.errorf("gtxna group index beyond 255: %d", slot) + return ops.errorf("%s group index beyond 255: %d", spec.Name, slot) } fs, ok := txnFieldSpecByName[args[1]] if !ok { - return ops.errorf("gtxna unknown field: %#v", args[1]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[1]) } _, ok = txnaFieldSpecByField[fs.field] if !ok { - return ops.errorf("gtxna unknown field: %#v", args[1]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[1]) } if fs.version > ops.Version { - return ops.errorf("gtxna %#v available in version %d. Missed #pragma version?", args[1], fs.version) + return ops.errorf("%s %#v available in version %d. Missed #pragma version?", spec.Name, args[1], fs.version) } arrayFieldIdx, err := strconv.ParseUint(args[2], 0, 64) if err != nil { return ops.error(err) } if arrayFieldIdx > 255 { - return ops.errorf("gtxna array index beyond 255: %d", arrayFieldIdx) + return ops.errorf("%s array index beyond 255: %d", spec.Name, arrayFieldIdx) } ops.pending.WriteByte(spec.Opcode) @@ -949,7 +949,7 @@ func assembleGtxna(ops *OpStream, spec *OpSpec, args []string) error { func assembleGtxnas(ops *OpStream, spec *OpSpec, args []string) error { if len(args) != 2 { - return ops.error("gtxnas expects two immediate arguments") + return ops.errorf("%s expects two immediate arguments", spec.Name) } slot, err := strconv.ParseUint(args[0], 0, 64) @@ -957,19 +957,19 @@ func assembleGtxnas(ops *OpStream, spec *OpSpec, args []string) error { return ops.error(err) } if slot > 255 { - return ops.errorf("gtxnas group index beyond 255: %d", slot) + return ops.errorf("%s group index beyond 255: %d", spec.Name, slot) } fs, ok := txnFieldSpecByName[args[1]] if !ok { - return ops.errorf("gtxnas unknown field: %#v", args[1]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[1]) } _, ok = txnaFieldSpecByField[fs.field] if !ok { - return ops.errorf("gtxnas unknown field: %#v", args[1]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[1]) } if fs.version > ops.Version { - return ops.errorf("gtxnas %#v available in version %d. Missed #pragma version?", args[1], fs.version) + return ops.errorf("%s %#v available in version %d. Missed #pragma version?", spec.Name, args[1], fs.version) } ops.pending.WriteByte(spec.Opcode) @@ -985,11 +985,11 @@ func assembleGtxns(ops *OpStream, spec *OpSpec, args []string) error { return assembleGtxnsa(ops, >xnsa, args) } if len(args) != 1 { - return ops.error("gtxns expects one or two immediate arguments") + return ops.errorf("%s expects one or two immediate arguments", spec.Name) } fs, ok := txnFieldSpecByName[args[0]] if !ok { - return ops.errorf("gtxns unknown field: %#v", args[0]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[0]) } _, ok = txnaFieldSpecByField[fs.field] if ok { @@ -1007,25 +1007,25 @@ func assembleGtxns(ops *OpStream, spec *OpSpec, args []string) error { func assembleGtxnsa(ops *OpStream, spec *OpSpec, args []string) error { if len(args) != 2 { - return ops.error("gtxnsa expects two immediate arguments") + return ops.errorf("%s expects two immediate arguments", spec.Name) } fs, ok := txnFieldSpecByName[args[0]] if !ok { - return ops.errorf("gtxnsa unknown field: %#v", args[0]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[0]) } _, ok = txnaFieldSpecByField[fs.field] if !ok { - return ops.errorf("gtxnsa unknown field: %#v", args[0]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[0]) } if fs.version > ops.Version { - return ops.errorf("gtxnsa %#v available in version %d. Missed #pragma version?", args[0], fs.version) + return ops.errorf("%s %#v available in version %d. Missed #pragma version?", spec.Name, args[0], fs.version) } arrayFieldIdx, err := strconv.ParseUint(args[1], 0, 64) if err != nil { return ops.error(err) } if arrayFieldIdx > 255 { - return ops.errorf("gtxnsa array index beyond 255: %d", arrayFieldIdx) + return ops.errorf("%s array index beyond 255: %d", spec.Name, arrayFieldIdx) } ops.pending.WriteByte(spec.Opcode) ops.pending.WriteByte(uint8(fs.field)) @@ -1036,18 +1036,18 @@ func assembleGtxnsa(ops *OpStream, spec *OpSpec, args []string) error { func assembleGtxnsas(ops *OpStream, spec *OpSpec, args []string) error { if len(args) != 1 { - return ops.error("gtxnsas expects one immediate argument") + return ops.errorf("%s expects one immediate argument", spec.Name) } fs, ok := txnFieldSpecByName[args[0]] if !ok { - return ops.errorf("gtxnsas unknown field: %#v", args[0]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[0]) } _, ok = txnaFieldSpecByField[fs.field] if !ok { - return ops.errorf("gtxnsas unknown field: %#v", args[0]) + return ops.errorf("%s unknown field: %#v", spec.Name, args[0]) } if fs.version > ops.Version { - return ops.errorf("gtxnsas %#v available in version %d. Missed #pragma version?", args[0], fs.version) + return ops.errorf("%s %#v available in version %d. Missed #pragma version?", spec.Name, args[0], fs.version) } ops.pending.WriteByte(spec.Opcode) ops.pending.WriteByte(uint8(fs.field)) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 1d8ff50973..139cbf101b 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -324,6 +324,10 @@ int 0 gtxnsas ApplicationArgs int 0 args +int 0 +loads +int 0 +stores ` var nonsense = map[uint64]string{ @@ -339,7 +343,7 @@ var compiled = map[uint64]string{ 2: "022008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f23102311231223132314181b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f", 3: "032008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f23102311231223132314181b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f4478222105531421055427042106552105082106564c4d4b02210538212106391c0081e80780046a6f686e", 4: "042004010200b7a60c26040242420c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482a50512a632223524100034200004322602261222b634848222862482864286548482228236628226724286828692422700048482471004848361c0037001a0031183119311b311d311e311f312024221e312131223123312431253126312731283129312a312b312c312d312e312f44782522531422542b2355220823564c4d4b0222382123391c0081e80780046a6f686e2281d00f24231f880003420001892223902291922394239593a0a1a2a3a4a5a6a7a8a9aaabacadae23af3a00003b003c003d8164", - 5: "052004010002b7a60c26040242420c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482a50512a632223524100034200004322602261222b634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f44782522531422542b2455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f0180070123456789abcd57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3", + 5: "052004010002b7a60c26040242420c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482a50512a632223524100034200004322602261222b634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f44782522531422542b2455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f0180070123456789abcd57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f}", } func pseudoOp(opcode string) bool { diff --git a/data/transactions/logic/debugger.go b/data/transactions/logic/debugger.go index c4477473ca..f90e7b3f3e 100644 --- a/data/transactions/logic/debugger.go +++ b/data/transactions/logic/debugger.go @@ -98,7 +98,7 @@ func makeDebugState(cx *EvalContext) DebugState { ExecID: GetProgramID(cx.program), Disassembly: disasm, PCOffset: dsInfo.pcOffset, - GroupIndex: cx.GroupIndex, + GroupIndex: int(cx.GroupIndex), TxnGroup: cx.TxnGroup, Proto: cx.Proto, } diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index 760c6a75c0..f921c70ded 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -87,7 +87,9 @@ var opDocByName = map[string]string{ "gtxnsa": "push Ith value of the array field F from the Xth transaction in the current group", "global": "push value from globals to stack", "load": "copy a value from scratch space to the stack", - "store": "pop a value from the stack and store to scratch space", + "store": "pop value X. store X to the Ith scratch space", + "loads": "copy a value from the Xth scratch space to the stack", + "stores": "pop indexes A and B. store A to the Bth scratch space", "gload": "push Ith scratch space index of the Tth transaction in the current group", "gloads": "push Ith scratch space index of the Xth transaction in the current group", "gaid": "push the ID of the asset or application created in the Tth transaction of the current group", @@ -264,7 +266,7 @@ var OpGroups = map[string][]string{ "Byte Array Slicing": {"substring", "substring3", "extract", "extract3", "extract16bits", "extract32bits", "extract64bits"}, "Byte Array Arithmetic": {"b+", "b-", "b/", "b*", "b<", "b>", "b<=", "b>=", "b==", "b!=", "b%"}, "Byte Array Logic": {"b|", "b&", "b^", "b~"}, - "Loading Values": {"intcblock", "intc", "intc_0", "intc_1", "intc_2", "intc_3", "pushint", "bytecblock", "bytec", "bytec_0", "bytec_1", "bytec_2", "bytec_3", "pushbytes", "bzero", "arg", "arg_0", "arg_1", "arg_2", "arg_3", "txn", "gtxn", "txna", "gtxna", "gtxns", "gtxnsa", "global", "load", "store", "gload", "gloads", "gaid", "gaids", "txnas", "gtxnas", "gtxnsas", "args"}, + "Loading Values": {"intcblock", "intc", "intc_0", "intc_1", "intc_2", "intc_3", "pushint", "bytecblock", "bytec", "bytec_0", "bytec_1", "bytec_2", "bytec_3", "pushbytes", "bzero", "arg", "arg_0", "arg_1", "arg_2", "arg_3", "txn", "gtxn", "txna", "txnas", "gtxna", "gtxnas", "gtxns", "gtxnsa", "gtxnsas", "global", "load", "loads", "store", "stores", "gload", "gloads", "gaid", "gaids", "args"}, "Flow Control": {"err", "bnz", "bz", "b", "return", "pop", "dup", "dup2", "dig", "cover", "uncover", "swap", "select", "assert", "callsub", "retsub"}, "State Access": {"balance", "min_balance", "app_opted_in", "app_local_get", "app_local_get_ex", "app_global_get", "app_global_get_ex", "app_local_put", "app_global_put", "app_local_del", "app_global_del", "asset_holding_get", "asset_params_get", "app_params_get", "log"}, "Inner Transactions": {"tx_begin", "tx_field", "tx_submit"}, diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 507c13e719..bea52b2c98 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -221,7 +221,7 @@ type EvalParams struct { TxnGroup []transactions.SignedTxn // GroupIndex should point to Txn within TxnGroup - GroupIndex int + GroupIndex uint64 PastSideEffects []EvalSideEffects @@ -342,7 +342,7 @@ type EvalContext struct { instructionStarts map[int]bool programHashCached crypto.Digest - txidCache map[int]transactions.Txid + txidCache map[uint64]transactions.Txid appAddrCache map[basics.AppIndex]basics.Address // Stores state & disassembly for the optional debugger @@ -1854,15 +1854,18 @@ func (cx *EvalContext) appParamsToValue(params *basics.AppParams, fs appParamsFi // TxnFieldToTealValue is a thin wrapper for txnFieldToStack for external use func TxnFieldToTealValue(txn *transactions.Transaction, groupIndex int, field TxnField, arrayFieldIdx uint64) (basics.TealValue, error) { - cx := EvalContext{EvalParams: EvalParams{GroupIndex: groupIndex}} - sv, err := cx.txnFieldToStack(txn, field, arrayFieldIdx, groupIndex) + if groupIndex < 0 { + return basics.TealValue{}, fmt.Errorf("negative groupIndex %d", groupIndex) + } + cx := EvalContext{EvalParams: EvalParams{GroupIndex: uint64(groupIndex)}} + sv, err := cx.txnFieldToStack(txn, field, arrayFieldIdx, uint64(groupIndex)) return sv.toTealValue(), err } -func (cx *EvalContext) getTxID(txn *transactions.Transaction, groupIndex int) transactions.Txid { +func (cx *EvalContext) getTxID(txn *transactions.Transaction, groupIndex uint64) transactions.Txid { // Initialize txidCache if necessary if cx.txidCache == nil { - cx.txidCache = make(map[int]transactions.Txid, len(cx.TxnGroup)) + cx.txidCache = make(map[uint64]transactions.Txid, len(cx.TxnGroup)) } // Hashes are expensive, so we cache computed TxIDs @@ -1875,7 +1878,7 @@ func (cx *EvalContext) getTxID(txn *transactions.Transaction, groupIndex int) tr return txid } -func (cx *EvalContext) txnFieldToStack(txn *transactions.Transaction, field TxnField, arrayFieldIdx uint64, groupIndex int) (sv stackValue, err error) { +func (cx *EvalContext) txnFieldToStack(txn *transactions.Transaction, field TxnField, arrayFieldIdx uint64, groupIndex uint64) (sv stackValue, err error) { err = nil switch field { case Sender: @@ -1921,7 +1924,7 @@ func (cx *EvalContext) txnFieldToStack(txn *transactions.Transaction, field TxnF case AssetCloseTo: sv.Bytes = txn.AssetCloseTo[:] case GroupIndex: - sv.Uint = uint64(groupIndex) + sv.Uint = groupIndex case TxID: txid := cx.getTxID(txn, groupIndex) sv.Bytes = txid[:] @@ -2060,7 +2063,7 @@ func opTxn(cx *EvalContext) { } func opTxna(cx *EvalContext) { - field := TxnField(uint64(cx.program[cx.pc+1])) + field := TxnField(cx.program[cx.pc+1]) fs, ok := txnFieldSpecByField[field] if !ok || fs.version > cx.version { cx.err = fmt.Errorf("invalid txn field %d", field) @@ -2104,13 +2107,13 @@ func opTxnas(cx *EvalContext) { } func opGtxn(cx *EvalContext) { - gtxid := int(uint(cx.program[cx.pc+1])) - if gtxid >= len(cx.TxnGroup) { + gtxid := cx.program[cx.pc+1] + if int(gtxid) >= len(cx.TxnGroup) { cx.err = fmt.Errorf("gtxn lookup TxnGroup[%d] but it only has %d", gtxid, len(cx.TxnGroup)) return } tx := &cx.TxnGroup[gtxid].Txn - field := TxnField(uint64(cx.program[cx.pc+2])) + field := TxnField(cx.program[cx.pc+2]) fs, ok := txnFieldSpecByField[field] if !ok || fs.version > cx.version { cx.err = fmt.Errorf("invalid txn field %d", field) @@ -2127,7 +2130,7 @@ func opGtxn(cx *EvalContext) { // GroupIndex; asking this when we just specified it is _dumb_, but oh well sv.Uint = uint64(gtxid) } else { - sv, err = cx.txnFieldToStack(tx, field, 0, gtxid) + sv, err = cx.txnFieldToStack(tx, field, 0, uint64(gtxid)) if err != nil { cx.err = err return @@ -2155,7 +2158,7 @@ func opGtxna(cx *EvalContext) { return } arrayFieldIdx := uint64(cx.program[cx.pc+3]) - sv, err := cx.txnFieldToStack(tx, field, arrayFieldIdx, gtxid) + sv, err := cx.txnFieldToStack(tx, field, arrayFieldIdx, uint64(gtxid)) if err != nil { cx.err = err return @@ -2166,13 +2169,10 @@ func opGtxna(cx *EvalContext) { func opGtxnas(cx *EvalContext) { last := len(cx.stack) - 1 - gtxid := int(cx.program[cx.pc+1]) - if gtxid >= len(cx.TxnGroup) { + gtxid := cx.program[cx.pc+1] + if int(gtxid) >= len(cx.TxnGroup) { cx.err = fmt.Errorf("gtxnas lookup TxnGroup[%d] but it only has %d", gtxid, len(cx.TxnGroup)) return - } else if gtxid < 0 { - cx.err = fmt.Errorf("gtxnas lookup %d cannot be negative", gtxid) - return } tx := &cx.TxnGroup[gtxid].Txn field := TxnField(cx.program[cx.pc+2]) @@ -2187,7 +2187,7 @@ func opGtxnas(cx *EvalContext) { return } arrayFieldIdx := cx.stack[last].Uint - sv, err := cx.txnFieldToStack(tx, field, arrayFieldIdx, gtxid) + sv, err := cx.txnFieldToStack(tx, field, arrayFieldIdx, uint64(gtxid)) if err != nil { cx.err = err return @@ -2197,13 +2197,13 @@ func opGtxnas(cx *EvalContext) { func opGtxns(cx *EvalContext) { last := len(cx.stack) - 1 - gtxid := int(cx.stack[last].Uint) - if gtxid >= len(cx.TxnGroup) { + gtxid := cx.stack[last].Uint + if gtxid >= uint64(len(cx.TxnGroup)) { cx.err = fmt.Errorf("gtxns lookup TxnGroup[%d] but it only has %d", gtxid, len(cx.TxnGroup)) return } tx := &cx.TxnGroup[gtxid].Txn - field := TxnField(uint64(cx.program[cx.pc+1])) + field := TxnField(cx.program[cx.pc+1]) fs, ok := txnFieldSpecByField[field] if !ok || fs.version > cx.version { cx.err = fmt.Errorf("invalid txn field %d", field) @@ -2216,9 +2216,9 @@ func opGtxns(cx *EvalContext) { } var sv stackValue var err error - if TxnField(field) == GroupIndex { + if field == GroupIndex { // GroupIndex; asking this when we just specified it is _dumb_, but oh well - sv.Uint = uint64(gtxid) + sv.Uint = gtxid } else { sv, err = cx.txnFieldToStack(tx, field, 0, gtxid) if err != nil { @@ -2231,13 +2231,13 @@ func opGtxns(cx *EvalContext) { func opGtxnsa(cx *EvalContext) { last := len(cx.stack) - 1 - gtxid := int(cx.stack[last].Uint) - if gtxid >= len(cx.TxnGroup) { + gtxid := cx.stack[last].Uint + if gtxid >= uint64(len(cx.TxnGroup)) { cx.err = fmt.Errorf("gtxnsa lookup TxnGroup[%d] but it only has %d", gtxid, len(cx.TxnGroup)) return } tx := &cx.TxnGroup[gtxid].Txn - field := TxnField(uint64(cx.program[cx.pc+1])) + field := TxnField(cx.program[cx.pc+1]) fs, ok := txnFieldSpecByField[field] if !ok || fs.version > cx.version { cx.err = fmt.Errorf("invalid txn field %d", field) @@ -2261,13 +2261,10 @@ func opGtxnsas(cx *EvalContext) { last := len(cx.stack) - 1 prev := last - 1 - gtxid := int(cx.stack[prev].Uint) - if gtxid >= len(cx.TxnGroup) { + gtxid := cx.stack[prev].Uint + if gtxid >= uint64(len(cx.TxnGroup)) { cx.err = fmt.Errorf("gtxnsas lookup TxnGroup[%d] but it only has %d", gtxid, len(cx.TxnGroup)) return - } else if gtxid < 0 { - cx.err = fmt.Errorf("gtxnsas lookup %d cannot be negative", gtxid) - return } tx := &cx.TxnGroup[gtxid].Txn field := TxnField(cx.program[cx.pc+1]) @@ -2291,8 +2288,8 @@ func opGtxnsas(cx *EvalContext) { cx.stack = cx.stack[:last] } -func opGaidImpl(cx *EvalContext, groupIdx int, opName string) (sv stackValue, err error) { - if groupIdx >= len(cx.TxnGroup) { +func opGaidImpl(cx *EvalContext, groupIdx uint64, opName string) (sv stackValue, err error) { + if groupIdx >= uint64(len(cx.TxnGroup)) { err = fmt.Errorf("%s lookup TxnGroup[%d] but it only has %d", opName, groupIdx, len(cx.TxnGroup)) return } else if groupIdx > cx.GroupIndex { @@ -2319,8 +2316,8 @@ func opGaidImpl(cx *EvalContext, groupIdx int, opName string) (sv stackValue, er } func opGaid(cx *EvalContext) { - groupIdx := int(uint(cx.program[cx.pc+1])) - sv, err := opGaidImpl(cx, groupIdx, "gaid") + groupIdx := cx.program[cx.pc+1] + sv, err := opGaidImpl(cx, uint64(groupIdx), "gaid") if err != nil { cx.err = err return @@ -2331,7 +2328,7 @@ func opGaid(cx *EvalContext) { func opGaids(cx *EvalContext) { last := len(cx.stack) - 1 - groupIdx := int(cx.stack[last].Uint) + groupIdx := cx.stack[last].Uint sv, err := opGaidImpl(cx, groupIdx, "gaids") if err != nil { cx.err = err @@ -2390,12 +2387,16 @@ func (cx *EvalContext) getApplicationAddress() (basics.Address, error) { return appAddr, nil } -func (cx *EvalContext) getCreatableID(groupIndex int) (cid uint64, err error) { +func (cx *EvalContext) getCreatableID(groupIndex uint64) (cid uint64, err error) { if cx.Ledger == nil { err = fmt.Errorf("ledger not available") return } - return uint64(cx.Ledger.GetCreatableID(groupIndex)), nil + gi := int(groupIndex) + if gi < 0 { + return 0, fmt.Errorf("groupIndex %d too high", groupIndex) + } + return uint64(cx.Ledger.GetCreatableID(gi)), nil } func (cx *EvalContext) getCreatorAddress() ([]byte, error) { @@ -2526,22 +2527,44 @@ func opEd25519verify(cx *EvalContext) { } func opLoad(cx *EvalContext) { - gindex := int(uint(cx.program[cx.pc+1])) - cx.stack = append(cx.stack, cx.scratch[gindex]) + n := cx.program[cx.pc+1] + cx.stack = append(cx.stack, cx.scratch[n]) +} + +func opLoads(cx *EvalContext) { + last := len(cx.stack) - 1 + n := cx.stack[last].Uint + if n >= uint64(len(cx.scratch)) { + cx.err = fmt.Errorf("invalid Scratch index %d", n) + return + } + cx.stack[last] = cx.scratch[n] } func opStore(cx *EvalContext) { - gindex := int(uint(cx.program[cx.pc+1])) + n := cx.program[cx.pc+1] last := len(cx.stack) - 1 - cx.scratch[gindex] = cx.stack[last] + cx.scratch[n] = cx.stack[last] cx.stack = cx.stack[:last] } -func opGloadImpl(cx *EvalContext, groupIdx int, scratchIdx int, opName string) (scratchValue stackValue, err error) { - if groupIdx >= len(cx.TxnGroup) { +func opStores(cx *EvalContext) { + last := len(cx.stack) - 1 + prev := last - 1 + n := cx.stack[last].Uint + if n >= uint64(len(cx.scratch)) { + cx.err = fmt.Errorf("invalid Scratch index %d", n) + return + } + cx.scratch[n] = cx.stack[prev] + cx.stack = cx.stack[:prev] +} + +func opGloadImpl(cx *EvalContext, groupIdx uint64, scratchIdx byte, opName string) (scratchValue stackValue, err error) { + if groupIdx >= uint64(len(cx.TxnGroup)) { err = fmt.Errorf("%s lookup TxnGroup[%d] but it only has %d", opName, groupIdx, len(cx.TxnGroup)) return - } else if scratchIdx >= 256 { + } else if int(scratchIdx) >= len(cx.scratch) { err = fmt.Errorf("invalid Scratch index %d", scratchIdx) return } else if txn := cx.TxnGroup[groupIdx].Txn; txn.Type != protocol.ApplicationCallTx { @@ -2555,13 +2578,13 @@ func opGloadImpl(cx *EvalContext, groupIdx int, scratchIdx int, opName string) ( return } - scratchValue = cx.PastSideEffects[groupIdx].getScratchValue(uint8(scratchIdx)) + scratchValue = cx.PastSideEffects[groupIdx].getScratchValue(scratchIdx) return } func opGload(cx *EvalContext) { - groupIdx := int(uint(cx.program[cx.pc+1])) - scratchIdx := int(uint(cx.program[cx.pc+2])) + groupIdx := uint64(cx.program[cx.pc+1]) + scratchIdx := cx.program[cx.pc+2] scratchValue, err := opGloadImpl(cx, groupIdx, scratchIdx, "gload") if err != nil { cx.err = err @@ -2573,8 +2596,8 @@ func opGload(cx *EvalContext) { func opGloads(cx *EvalContext) { last := len(cx.stack) - 1 - groupIdx := int(cx.stack[last].Uint) - scratchIdx := int(uint(cx.program[cx.pc+1])) + groupIdx := cx.stack[last].Uint + scratchIdx := cx.program[cx.pc+1] scratchValue, err := opGloadImpl(cx, groupIdx, scratchIdx, "gloads") if err != nil { cx.err = err diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 9c9b4edbfd..0993c7c44d 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -905,7 +905,7 @@ func TestArg(t *testing.T) { t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) { source := "arg 0; arg 1; ==; arg 2; arg 3; !=; &&; arg 4; len; int 9; <; &&;" if v >= 5 { - source += "int 0; args; int 1; args; ==; assert;" + source += "int 0; args; int 1; args; ==; assert; int 2; args; int 3; args; !=; assert" } ops := testProg(t, source, v) err := Check(ops.Program, defaultEvalParams(nil, nil)) @@ -2338,6 +2338,33 @@ load 1 &&`, 1) } +func TestLoadStoreStack(t *testing.T) { + partitiontest.PartitionTest(t) + + t.Parallel() + testAccepts(t, `int 37 +int 37 +int 1 +stores +byte 0xabbacafe +int 42 +stores +int 37 +== +int 0 +stores +int 42 +loads +byte 0xabbacafe +== +int 0 +loads +int 1 +loads ++ +&&`, 5) +} + func TestLoadStore2(t *testing.T) { partitiontest.PartitionTest(t) @@ -2462,7 +2489,7 @@ int 1`, Proto: &proto, Txn: &txgroup[j], TxnGroup: txgroup, - GroupIndex: j, + GroupIndex: uint64(j), PastSideEffects: pastSideEffects, } } @@ -2536,7 +2563,7 @@ int 1`, Proto: &proto, Txn: &txgroup[j], TxnGroup: txgroup, - GroupIndex: j, + GroupIndex: uint64(j), PastSideEffects: pastSideEffects, } } @@ -2609,7 +2636,7 @@ byte "txn 2" Proto: &proto, Txn: &txgroup[j], TxnGroup: txgroup, - GroupIndex: j, + GroupIndex: uint64(j), PastSideEffects: pastSideEffects, } } diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index acab24cfa9..a1eec0cd11 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -206,6 +206,10 @@ var OpSpecs = []OpSpec{ {0x3c, "gaid", opGaid, asmDefault, disDefault, nil, oneInt, 4, runModeApplication, immediates("t")}, {0x3d, "gaids", opGaids, asmDefault, disDefault, oneInt, oneInt, 4, runModeApplication, opDefault}, + // Like load/store, but scratch slot taken from TOS instead of immediate + {0x3e, "loads", opLoads, asmDefault, disDefault, oneInt, oneAny, 5, modeAny, opDefault}, + {0x3f, "stores", opStores, asmDefault, disDefault, oneAny.plus(oneInt), nil, 5, modeAny, opDefault}, + {0x40, "bnz", opBnz, assembleBranch, disBranch, oneInt, nil, 1, modeAny, opBranch}, {0x41, "bz", opBz, assembleBranch, disBranch, oneInt, nil, 2, modeAny, opBranch}, {0x42, "b", opB, assembleBranch, disBranch, nil, nil, 2, modeAny, opBranch}, diff --git a/data/transactions/verify/txn.go b/data/transactions/verify/txn.go index 4e710610dc..671b9a347c 100644 --- a/data/transactions/verify/txn.go +++ b/data/transactions/verify/txn.go @@ -286,11 +286,14 @@ func LogicSigSanityCheckBatchVerify(txn *transactions.SignedTxn, groupIndex int, return errors.New("LogicSig.Logic too long") } + if groupIndex < 0 { + return errors.New("Negative groupIndex") + } ep := logic.EvalParams{ Txn: txn, Proto: &groupCtx.consensusParams, TxnGroup: groupCtx.signedGroupTxns, - GroupIndex: groupIndex, + GroupIndex: uint64(groupIndex), MinTealVersion: &groupCtx.minTealVersion, } err := logic.Check(lsig.Logic, ep) @@ -340,11 +343,14 @@ func logicSigBatchVerify(txn *transactions.SignedTxn, groupIndex int, groupCtx * return err } + if groupIndex < 0 { + return errors.New("Negative groupIndex") + } ep := logic.EvalParams{ Txn: txn, Proto: &groupCtx.consensusParams, TxnGroup: groupCtx.signedGroupTxns, - GroupIndex: groupIndex, + GroupIndex: uint64(groupIndex), MinTealVersion: &groupCtx.minTealVersion, } pass, err := logic.Eval(txn.Lsig.Logic, ep) diff --git a/ledger/eval.go b/ledger/eval.go index 15c0d04871..20b5a3cd32 100644 --- a/ledger/eval.go +++ b/ledger/eval.go @@ -705,7 +705,7 @@ func (eval *BlockEvaluator) prepareEvalParams(txgroup []transactions.SignedTxnWi Txn: &groupNoAD[i], Proto: &eval.proto, TxnGroup: groupNoAD, - GroupIndex: i, + GroupIndex: uint64(i), PastSideEffects: pastSideEffects, MinTealVersion: &minTealVersion, PooledApplicationBudget: &pooledApplicationBudget, diff --git a/ledger/eval_test.go b/ledger/eval_test.go index b0dc43881a..cdaf600bdb 100644 --- a/ledger/eval_test.go +++ b/ledger/eval_test.go @@ -418,7 +418,7 @@ func TestPrepareEvalParams(t *testing.T) { if present { require.NotNil(t, res[k]) require.NotNil(t, res[k].PastSideEffects) - require.Equal(t, res[k].GroupIndex, k) + require.Equal(t, res[k].GroupIndex, uint64(k)) require.Equal(t, res[k].TxnGroup, expGroupNoAD) require.Equal(t, *res[k].Proto, eval.proto) require.Equal(t, *res[k].Txn, testCase.group[k].SignedTxn) From fa7c14466656146d16d2e6df17fd70b425a2ddd7 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 8 Sep 2021 14:49:59 -0400 Subject: [PATCH 2/4] Swap order of stores args --- data/transactions/logic/doc.go | 2 +- data/transactions/logic/eval.go | 4 ++-- data/transactions/logic/eval_test.go | 9 +++++++-- data/transactions/logic/opcodes.go | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index f921c70ded..e339a3f2c6 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -89,7 +89,7 @@ var opDocByName = map[string]string{ "load": "copy a value from scratch space to the stack", "store": "pop value X. store X to the Ith scratch space", "loads": "copy a value from the Xth scratch space to the stack", - "stores": "pop indexes A and B. store A to the Bth scratch space", + "stores": "pop indexes A and B. store B to the Ath scratch space", "gload": "push Ith scratch space index of the Tth transaction in the current group", "gloads": "push Ith scratch space index of the Xth transaction in the current group", "gaid": "push the ID of the asset or application created in the Tth transaction of the current group", diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index bea52b2c98..91b281ea57 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -2551,12 +2551,12 @@ func opStore(cx *EvalContext) { func opStores(cx *EvalContext) { last := len(cx.stack) - 1 prev := last - 1 - n := cx.stack[last].Uint + n := cx.stack[prev].Uint if n >= uint64(len(cx.scratch)) { cx.err = fmt.Errorf("invalid Scratch index %d", n) return } - cx.scratch[n] = cx.stack[prev] + cx.scratch[n] = cx.stack[last] cx.stack = cx.stack[:prev] } diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 0993c7c44d..bf5de97b87 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -2343,15 +2343,16 @@ func TestLoadStoreStack(t *testing.T) { t.Parallel() testAccepts(t, `int 37 -int 37 int 1 +int 37 stores -byte 0xabbacafe int 42 +byte 0xabbacafe stores int 37 == int 0 +swap stores int 42 loads @@ -4348,6 +4349,7 @@ func testEvaluation(t *testing.T, program string, introduced uint64, tester eval var outer error for v := uint64(1); v <= AssemblerMaxVersion; v++ { t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) { + t.Helper() if v < introduced { testProg(t, obfuscate(program), v, expect{0, "...was introduced..."}) return @@ -4389,17 +4391,20 @@ func testEvaluation(t *testing.T, program string, introduced uint64, tester eval } func testAccepts(t *testing.T, program string, introduced uint64) { + t.Helper() testEvaluation(t, program, introduced, func(pass bool, err error) bool { return pass && err == nil }) } func testRejects(t *testing.T, program string, introduced uint64) { + t.Helper() testEvaluation(t, program, introduced, func(pass bool, err error) bool { // Returned False, but didn't panic return !pass && err == nil }) } func testPanics(t *testing.T, program string, introduced uint64) error { + t.Helper() return testEvaluation(t, program, introduced, func(pass bool, err error) bool { // TEAL panic! not just reject at exit return !pass && err != nil diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index a1eec0cd11..8fff33aa03 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -208,7 +208,7 @@ var OpSpecs = []OpSpec{ // Like load/store, but scratch slot taken from TOS instead of immediate {0x3e, "loads", opLoads, asmDefault, disDefault, oneInt, oneAny, 5, modeAny, opDefault}, - {0x3f, "stores", opStores, asmDefault, disDefault, oneAny.plus(oneInt), nil, 5, modeAny, opDefault}, + {0x3f, "stores", opStores, asmDefault, disDefault, oneInt.plus(oneAny), nil, 5, modeAny, opDefault}, {0x40, "bnz", opBnz, assembleBranch, disBranch, oneInt, nil, 1, modeAny, opBranch}, {0x41, "bz", opBz, assembleBranch, disBranch, oneInt, nil, 2, modeAny, opBranch}, From b162c42afa661399f31c9a275002f68febe099c3 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 8 Sep 2021 14:59:29 -0400 Subject: [PATCH 3/4] tiny coverage improvement --- data/transactions/logic/eval.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 91b281ea57..410a942473 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -1231,12 +1231,12 @@ func opExpImpl(base uint64, exp uint64) (uint64, error) { if exp == 0 || base == 1 { return 1, nil } - // base is now at least 2, so exp can not be over 64 - if exp > 64 { + // base is now at least 2, so exp can not be 64 + if exp >= 64 { return 0, fmt.Errorf("%d^%d overflow", base, exp) } answer := base - // safe to cast exp, because it is known to fit in int (it's <= 64) + // safe to cast exp, because it is known to fit in int (it's < 64) for i := 1; i < int(exp); i++ { next := answer * base if next/answer != base { @@ -1274,14 +1274,14 @@ func opExpwImpl(base uint64, exp uint64) (*big.Int, error) { if exp == 0 || base == 1 { return new(big.Int).SetUint64(1), nil } - // base is now at least 2, so exp can not be over 128 - if exp > 128 { + // base is now at least 2, so exp can not be 128 + if exp >= 128 { return &big.Int{}, fmt.Errorf("%d^%d overflow", base, exp) } answer := new(big.Int).SetUint64(base) bigbase := new(big.Int).SetUint64(base) - // safe to cast exp, because it is known to fit in int (it's <= 128) + // safe to cast exp, because it is known to fit in int (it's < 128) for i := 1; i < int(exp); i++ { next := answer.Mul(answer, bigbase) answer = next From 52d6c86cb79bf79e5872d870c4eb69b61c991864 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 8 Sep 2021 16:22:45 -0400 Subject: [PATCH 4/4] Generate spec --- data/transactions/logic/README.md | 2 +- data/transactions/logic/TEAL_opcodes.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index 098641b385..8af04b0b44 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -243,7 +243,7 @@ Some of these have immediate data in the byte or bytes after the opcode. | `load i` | copy a value from scratch space to the stack | | `loads` | copy a value from the Xth scratch space to the stack | | `store i` | pop value X. store X to the Ith scratch space | -| `stores` | pop indexes A and B. store A to the Bth scratch space | +| `stores` | pop indexes A and B. store B to the Ath scratch space | | `gload t i` | push Ith scratch space index of the Tth transaction in the current group | | `gloads i` | push Ith scratch space index of the Xth transaction in the current group | | `gaid t` | push the ID of the asset or application created in the Tth transaction of the current group | diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 8bc1dbc0a3..8e60809037 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -580,9 +580,9 @@ for notes on transaction fields available, see `txn`. If top of stack is _i_, `g ## stores - Opcode: 0x3f -- Pops: *... stack*, {any A}, {uint64 B} +- Pops: *... stack*, {uint64 A}, {any B} - Pushes: _None_ -- pop indexes A and B. store A to the Bth scratch space +- pop indexes A and B. store B to the Ath scratch space - LogicSigVersion >= 5 ## bnz target