From c152749bd5b085440debf4b2569abd0cafddc6b6 Mon Sep 17 00:00:00 2001 From: Ilan Date: Tue, 31 May 2022 11:31:05 -0400 Subject: [PATCH 01/11] intermediate commit --- data/transactions/logic/assembler.go | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 8c9d5955e1..75274ec1a7 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -279,6 +279,8 @@ type ProgramKnowledge struct { // deadcode indicates that the program is in deadcode, so no type checking // errors should be reported. deadcode bool + + scratchSpace [256]StackType } func (pgm *ProgramKnowledge) pop() StackType { @@ -1125,6 +1127,37 @@ func typeTxField(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return StackTypes{fs.ftype}, nil } +func typeStore(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { + if len(args) == 0 { + return nil, nil + } + n, err := strconv.ParseUint(args[0], 0, 64) + if err != nil { + return nil, nil + } + scratchIndex := int(n) + top := len(pgm.stack) - 1 + if top >= scratchIndex && scratchIndex < 256 && scratchIndex >= 0 { + pgm.scratchSpace[top-scratchIndex] = pgm.stack[top] + } + return nil, nil +} + +func typeLoad(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { + if len(args) == 0 { + return nil, nil + } + n, err := strconv.ParseUint(args[0], 0, 64) + if err != nil { + return nil, nil + } + scratchIndex := int(n) + if scratchIndex < 0 || scratchIndex > 255 { + return nil, nil + } + return nil, StackTypes{pgm.scratchSpace[scratchIndex]} +} + // keywords or "pseudo-ops" handle parsing and assembling special asm language // constructs like 'addr' We use an OpSpec here, but it's somewhat degenerate, // since they don't have opcodes or eval functions. But it does need a lot of From 5fb154795aa8ac2289514c7dbae688a47f65ed85 Mon Sep 17 00:00:00 2001 From: Ilan Date: Tue, 31 May 2022 15:25:47 -0400 Subject: [PATCH 02/11] intermediate commit --- data/transactions/logic/assembler.go | 25 +++++++++++++++++++++++ data/transactions/logic/assembler_test.go | 6 ++++++ data/transactions/logic/opcodes.go | 8 ++++---- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 75274ec1a7..16137548d3 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1143,6 +1143,18 @@ func typeStore(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, nil } +func typeStores(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { + second := len(pgm.stack) - 2 + if second >= 0 { + for i, _ := range pgm.scratchSpace { + if pgm.scratchSpace[i] != pgm.stack[second] { + pgm.scratchSpace[i] = StackAny + } + } + } + return nil, nil +} + func typeLoad(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { if len(args) == 0 { return nil, nil @@ -1158,6 +1170,16 @@ func typeLoad(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, StackTypes{pgm.scratchSpace[scratchIndex]} } +func typeLoads(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { + scratchType := pgm.scratchSpace[0] + for _, item := range pgm.scratchSpace { + if item != scratchType { + return nil, nil + } + } + return nil, StackTypes{scratchType} +} + // keywords or "pseudo-ops" handle parsing and assembling special asm language // constructs like 'addr' We use an OpSpec here, but it's somewhat degenerate, // since they don't have opcodes or eval functions. But it does need a lot of @@ -1335,6 +1357,9 @@ func (ops *OpStream) assemble(text string) error { return ops.errorf("Can not assemble version %d", ops.Version) } scanner := bufio.NewScanner(fin) + for i, _ := range ops.known.scratchSpace { + ops.known.scratchSpace[i] = StackAny + } for scanner.Scan() { ops.sourceLine++ line := scanner.Text() diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 8c719bbfe0..c761ccabb3 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2400,6 +2400,12 @@ func TestSetBitTypeCheck(t *testing.T) { testProg(t, "byte 0x1234; int 2; int 3; setbit; !", AssemblerMaxVersion, Expect{5, "! arg 0..."}) } +func TestScratchTypeCheck(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + testProg(t, "byte 0x01; store 0; load 0; int 1; +", AssemblerMaxVersion, Expect{5, "+ arg 0..."}) +} + func TestCoverAsm(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index 1d26d437a0..e7302a16dc 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -443,8 +443,8 @@ var OpSpecs = []OpSpec{ {0x32, "global", opGlobal, proto(":a"), 1, field("f", &GlobalFields)}, {0x33, "gtxn", opGtxn, proto(":a"), 1, immediates("t", "f").field("f", &TxnScalarFields)}, {0x33, "gtxn", opGtxn, proto(":a"), 2, immediates("t", "f").field("f", &TxnFields).assembler(asmGtxn2)}, - {0x34, "load", opLoad, proto(":a"), 1, immediates("i")}, - {0x35, "store", opStore, proto("a:"), 1, immediates("i")}, + {0x34, "load", opLoad, proto(":a"), 1, stacky(typeLoad, "i")}, + {0x35, "store", opStore, proto("a:"), 1, stacky(typeStore, "i")}, {0x36, "txna", opTxna, proto(":a"), 2, immediates("f", "i").field("f", &TxnArrayFields)}, {0x37, "gtxna", opGtxna, proto(":a"), 2, immediates("t", "f", "i").field("f", &TxnArrayFields)}, // Like gtxn, but gets txn index from stack, rather than immediate arg @@ -458,8 +458,8 @@ var OpSpecs = []OpSpec{ {0x3d, "gaids", opGaids, proto("i:i"), 4, only(modeApp)}, // Like load/store, but scratch slot taken from TOS instead of immediate - {0x3e, "loads", opLoads, proto("i:a"), 5, opDefault()}, - {0x3f, "stores", opStores, proto("ia:"), 5, opDefault()}, + {0x3e, "loads", opLoads, proto("i:a"), 5, stacky(typeLoads)}, + {0x3f, "stores", opStores, proto("ia:"), 5, stacky(typeStores)}, {0x40, "bnz", opBnz, proto("i:"), 1, opBranch()}, {0x41, "bz", opBz, proto("i:"), 2, opBranch()}, From f4596bef27906b247872ad4fabc2ac422758ded9 Mon Sep 17 00:00:00 2001 From: Ilan Date: Wed, 1 Jun 2022 15:29:42 -0400 Subject: [PATCH 03/11] All functionality complete --- data/transactions/logic/assembler.go | 47 ++++++++++++----------- data/transactions/logic/assembler_test.go | 7 ++++ data/transactions/logic/eval_test.go | 15 ++++++-- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 16137548d3..048fa52090 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -314,6 +314,9 @@ func (pgm *ProgramKnowledge) reset() { pgm.stack = nil pgm.bottom = StackAny pgm.deadcode = false + for i, _ := range pgm.scratchSpace { + pgm.scratchSpace[i] = StackAny + } } // createLabel inserts a label to point to the next instruction, reporting an @@ -336,7 +339,7 @@ func (ops *OpStream) referToLabel(pc int, label string) { ops.labelReferences = append(ops.labelReferences, labelReference{ops.sourceLine, pc, label}) } -type refineFunc func(pgm ProgramKnowledge, immediates []string) (StackTypes, StackTypes) +type refineFunc func(pgm *ProgramKnowledge, immediates []string) (StackTypes, StackTypes) // returns allows opcodes like `txn` to be specific about their return value // types, based on the field requested, rather than use Any as specified by @@ -971,7 +974,7 @@ func asmDefault(ops *OpStream, spec *OpSpec, args []string) error { return nil } -func typeSwap(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeSwap(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { topTwo := StackTypes{StackAny, StackAny} top := len(pgm.stack) - 1 if top >= 0 { @@ -984,7 +987,7 @@ func typeSwap(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, reversed } -func typeDig(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeDig(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { if len(args) == 0 { return nil, nil } @@ -1010,7 +1013,7 @@ func typeDig(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return anys, returns } -func typeEquals(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeEquals(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { top := len(pgm.stack) - 1 if top >= 0 { //Require arg0 and arg1 to have same type @@ -1019,7 +1022,7 @@ func typeEquals(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, nil } -func typeDup(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeDup(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { top := len(pgm.stack) - 1 if top >= 0 { return StackTypes{pgm.stack[top]}, StackTypes{pgm.stack[top], pgm.stack[top]} @@ -1027,7 +1030,7 @@ func typeDup(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, nil } -func typeDupTwo(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeDupTwo(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { topTwo := StackTypes{StackAny, StackAny} top := len(pgm.stack) - 1 if top >= 0 { @@ -1039,7 +1042,7 @@ func typeDupTwo(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, append(topTwo, topTwo...) } -func typeSelect(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeSelect(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { top := len(pgm.stack) - 1 if top >= 2 { if pgm.stack[top-1] == pgm.stack[top-2] { @@ -1049,7 +1052,7 @@ func typeSelect(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, nil } -func typeSetBit(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeSetBit(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { top := len(pgm.stack) - 1 if top >= 2 { return nil, StackTypes{pgm.stack[top-2]} @@ -1057,7 +1060,7 @@ func typeSetBit(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, nil } -func typeCover(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeCover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { if len(args) == 0 { return nil, nil } @@ -1088,7 +1091,7 @@ func typeCover(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return anys, returns } -func typeUncover(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeUncover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { if len(args) == 0 { return nil, nil } @@ -1116,7 +1119,7 @@ func typeUncover(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return anys, returns } -func typeTxField(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeTxField(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { if len(args) != 1 { return nil, nil } @@ -1127,7 +1130,7 @@ func typeTxField(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return StackTypes{fs.ftype}, nil } -func typeStore(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeStore(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { if len(args) == 0 { return nil, nil } @@ -1137,17 +1140,17 @@ func typeStore(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { } scratchIndex := int(n) top := len(pgm.stack) - 1 - if top >= scratchIndex && scratchIndex < 256 && scratchIndex >= 0 { - pgm.scratchSpace[top-scratchIndex] = pgm.stack[top] + if top >= 0 && scratchIndex < 256 && scratchIndex >= 0 { + pgm.scratchSpace[scratchIndex] = pgm.stack[top] } return nil, nil } -func typeStores(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { - second := len(pgm.stack) - 2 - if second >= 0 { +func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { + top := len(pgm.stack) - 1 + if top >= 0 { for i, _ := range pgm.scratchSpace { - if pgm.scratchSpace[i] != pgm.stack[second] { + if pgm.scratchSpace[i] != pgm.stack[top] { pgm.scratchSpace[i] = StackAny } } @@ -1155,7 +1158,7 @@ func typeStores(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, nil } -func typeLoad(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeLoad(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { if len(args) == 0 { return nil, nil } @@ -1170,7 +1173,7 @@ func typeLoad(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, StackTypes{pgm.scratchSpace[scratchIndex]} } -func typeLoads(pgm ProgramKnowledge, args []string) (StackTypes, StackTypes) { +func typeLoads(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { scratchType := pgm.scratchSpace[0] for _, item := range pgm.scratchSpace { if item != scratchType { @@ -1358,7 +1361,7 @@ func (ops *OpStream) assemble(text string) error { } scanner := bufio.NewScanner(fin) for i, _ := range ops.known.scratchSpace { - ops.known.scratchSpace[i] = StackAny + ops.known.scratchSpace[i] = StackUint64 } for scanner.Scan() { ops.sourceLine++ @@ -1424,7 +1427,7 @@ func (ops *OpStream) assemble(text string) error { } args, returns := spec.Arg.Types, spec.Return.Types if spec.OpDetails.refine != nil { - nargs, nreturns := spec.OpDetails.refine(ops.known, fields[1:]) + nargs, nreturns := spec.OpDetails.refine(&ops.known, fields[1:]) if nargs != nil { args = nargs } diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index c761ccabb3..ffb8b31fdf 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2403,7 +2403,14 @@ func TestSetBitTypeCheck(t *testing.T) { func TestScratchTypeCheck(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() + // All scratch slots should start as uint64 + testProg(t, "load 0; int 1; +", AssemblerMaxVersion) + // Check load and store accurately using the scratch space testProg(t, "byte 0x01; store 0; load 0; int 1; +", AssemblerMaxVersion, Expect{5, "+ arg 0..."}) + // Loads should know the type it's loading if all the slots are the same type + testProg(t, "int 0; loads; btoi", AssemblerMaxVersion, Expect{3, "btoi arg 0..."}) + // Stores should only set slots to StackAny if they are not the same type as what is being stored + testProg(t, "byte 0x01; store 0; int 3; byte 0x01; stores; load 0; int 1; +", AssemblerMaxVersion, Expect{8, "+ arg 0..."}) } func TestCoverAsm(t *testing.T) { diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 9e54e7b291..2b64be1b9d 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -4534,6 +4534,8 @@ func TestLog(t *testing.T) { source string runMode runMode errContains string + // For cases where assembly errors, we manually put in the bytes + assembledBytes []byte }{ { source: fmt.Sprintf(`byte "%s"; log; int 1`, strings.Repeat("a", maxLogSize+1)), @@ -4561,9 +4563,10 @@ func TestLog(t *testing.T) { runMode: modeApp, }, { - source: `load 0; log`, - errContains: "log arg 0 wanted []byte but got uint64", - runMode: modeApp, + source: `load 0; log`, + errContains: "log arg 0 wanted []byte but got uint64", + runMode: modeApp, + assembledBytes: []byte{byte(ep.Proto.LogicSigVersion), 0x34, 0x00, 0xb0}, }, { source: `byte "a logging message"; log; int 1`, @@ -4575,7 +4578,11 @@ func TestLog(t *testing.T) { for _, c := range failCases { switch c.runMode { case modeApp: - testApp(t, c.source, ep, c.errContains) + if c.assembledBytes == nil { + testApp(t, c.source, ep, c.errContains) + } else { + testAppBytes(t, c.assembledBytes, ep, c.errContains) + } default: testLogic(t, c.source, AssemblerMaxVersion, ep, c.errContains, c.errContains) } From d40a88eede933e2917eee0b0614b57a5876f0a94 Mon Sep 17 00:00:00 2001 From: Ilan Date: Wed, 1 Jun 2022 21:06:04 -0400 Subject: [PATCH 04/11] Linting --- data/transactions/logic/assembler.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 048fa52090..71e297bed8 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -314,7 +314,7 @@ func (pgm *ProgramKnowledge) reset() { pgm.stack = nil pgm.bottom = StackAny pgm.deadcode = false - for i, _ := range pgm.scratchSpace { + for i := range pgm.scratchSpace { pgm.scratchSpace[i] = StackAny } } @@ -1149,7 +1149,7 @@ func typeStore(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { top := len(pgm.stack) - 1 if top >= 0 { - for i, _ := range pgm.scratchSpace { + for i := range pgm.scratchSpace { if pgm.scratchSpace[i] != pgm.stack[top] { pgm.scratchSpace[i] = StackAny } @@ -1360,7 +1360,7 @@ func (ops *OpStream) assemble(text string) error { return ops.errorf("Can not assemble version %d", ops.Version) } scanner := bufio.NewScanner(fin) - for i, _ := range ops.known.scratchSpace { + for i := range ops.known.scratchSpace { ops.known.scratchSpace[i] = StackUint64 } for scanner.Scan() { From aad9fbcb08639ae8f0d6cb8b4bb9bdb3c656031f Mon Sep 17 00:00:00 2001 From: Ilan Date: Thu, 2 Jun 2022 12:48:14 -0400 Subject: [PATCH 05/11] Intermediate commit --- data/transactions/logic/assembler.go | 62 ++++++++++------------- data/transactions/logic/assembler_test.go | 2 + 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 71e297bed8..7650feb779 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -974,6 +974,17 @@ func asmDefault(ops *OpStream, spec *OpSpec, args []string) error { return nil } +func getUintImm(args []string) (n uint64, errored bool) { + if len(args) == 0 { + return 0, true + } + n, err := strconv.ParseUint(args[0], 0, 8) + if err != nil { + return 0, true + } + return n, false +} + func typeSwap(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { topTwo := StackTypes{StackAny, StackAny} top := len(pgm.stack) - 1 @@ -988,11 +999,8 @@ func typeSwap(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeDig(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - if len(args) == 0 { - return nil, nil - } - n, err := strconv.ParseUint(args[0], 0, 64) - if err != nil { + n, errored := getUintImm(args) + if errored { return nil, nil } depth := int(n) + 1 @@ -1061,11 +1069,8 @@ func typeSetBit(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeCover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - if len(args) == 0 { - return nil, nil - } - n, err := strconv.ParseUint(args[0], 0, 64) - if err != nil { + n, errored := getUintImm(args) + if errored { return nil, nil } depth := int(n) + 1 @@ -1092,11 +1097,8 @@ func typeCover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeUncover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - if len(args) == 0 { - return nil, nil - } - n, err := strconv.ParseUint(args[0], 0, 64) - if err != nil { + n, errored := getUintImm(args) + if errored { return nil, nil } depth := int(n) + 1 @@ -1131,16 +1133,13 @@ func typeTxField(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) } func typeStore(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - if len(args) == 0 { - return nil, nil - } - n, err := strconv.ParseUint(args[0], 0, 64) - if err != nil { + n, errored := getUintImm(args) + if errored { return nil, nil } scratchIndex := int(n) top := len(pgm.stack) - 1 - if top >= 0 && scratchIndex < 256 && scratchIndex >= 0 { + if top >= 0 { pgm.scratchSpace[scratchIndex] = pgm.stack[top] } return nil, nil @@ -1148,28 +1147,23 @@ func typeStore(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { top := len(pgm.stack) - 1 - if top >= 0 { - for i := range pgm.scratchSpace { - if pgm.scratchSpace[i] != pgm.stack[top] { - pgm.scratchSpace[i] = StackAny - } + if top < 0 { + return nil, nil + } + for i := range pgm.scratchSpace { + if pgm.scratchSpace[i] != pgm.stack[top] { + pgm.scratchSpace[i] = StackAny } } return nil, nil } func typeLoad(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - if len(args) == 0 { - return nil, nil - } - n, err := strconv.ParseUint(args[0], 0, 64) - if err != nil { + n, errored := getUintImm(args) + if errored { return nil, nil } scratchIndex := int(n) - if scratchIndex < 0 || scratchIndex > 255 { - return nil, nil - } return nil, StackTypes{pgm.scratchSpace[scratchIndex]} } diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index ffb8b31fdf..96efce7ef3 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2411,6 +2411,8 @@ func TestScratchTypeCheck(t *testing.T) { testProg(t, "int 0; loads; btoi", AssemblerMaxVersion, Expect{3, "btoi arg 0..."}) // Stores should only set slots to StackAny if they are not the same type as what is being stored testProg(t, "byte 0x01; store 0; int 3; byte 0x01; stores; load 0; int 1; +", AssemblerMaxVersion, Expect{8, "+ arg 0..."}) + testProg(t, "txn TypeEnum; bnz label1; byte 0x01; label1:; load 0; int 1; +", AssemblerMaxVersion) + testProg(t, "txn TypeEnum; bnz label1; byte 0x01; store 0; label1:; load 0; int 1; +", AssemblerMaxVersion) } func TestCoverAsm(t *testing.T) { From 510e1f00f5dbbf8b1ca337fedfa9e4ced721ed84 Mon Sep 17 00:00:00 2001 From: Ilan Date: Thu, 2 Jun 2022 15:59:17 -0400 Subject: [PATCH 06/11] Addressed comments --- data/transactions/logic/assembler_test.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 96efce7ef3..bf9d8e1a72 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2411,8 +2411,14 @@ func TestScratchTypeCheck(t *testing.T) { testProg(t, "int 0; loads; btoi", AssemblerMaxVersion, Expect{3, "btoi arg 0..."}) // Stores should only set slots to StackAny if they are not the same type as what is being stored testProg(t, "byte 0x01; store 0; int 3; byte 0x01; stores; load 0; int 1; +", AssemblerMaxVersion, Expect{8, "+ arg 0..."}) - testProg(t, "txn TypeEnum; bnz label1; byte 0x01; label1:; load 0; int 1; +", AssemblerMaxVersion) - testProg(t, "txn TypeEnum; bnz label1; byte 0x01; store 0; label1:; load 0; int 1; +", AssemblerMaxVersion) + // ScratchSpace should reset after hitting label in deadcode + testProg(t, "byte 0x01; store 0; b label1; label1:; load 0; int 1; +", AssemblerMaxVersion) + // But it should reset to StackAny not uint64 + testProg(t, "int 1; store 0; b label1; label1:; load 0; btoi", AssemblerMaxVersion) + // Callsubs should also reset the scratch space + testProg(t, "callsub A; load 0; btoi; return; A: byte 0x01; store 0; retsub", AssemblerMaxVersion) + // But the scratchspace should still be tracked after the callsub + testProg(t, "callsub A; int 1; store 0; load 0; btoi; return; A: retsub", AssemblerMaxVersion, Expect{5, "btoi arg 0..."}) } func TestCoverAsm(t *testing.T) { From 41ecd8dc86e9c5add8c67c68951121b7f00d66bb Mon Sep 17 00:00:00 2001 From: Ilan Date: Fri, 3 Jun 2022 10:24:48 -0400 Subject: [PATCH 07/11] Addressed comments --- data/transactions/logic/assembler.go | 37 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 7650feb779..7ae756b523 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -974,15 +974,16 @@ func asmDefault(ops *OpStream, spec *OpSpec, args []string) error { return nil } -func getUintImm(args []string) (n uint64, errored bool) { - if len(args) == 0 { - return 0, true +// Interprets the arg at index argIndex as byte-long uint immediate +func getUintImm(args []string, argIndex int) (byte, bool) { + if len(args) <= argIndex { + return 0, false } - n, err := strconv.ParseUint(args[0], 0, 8) + n, err := strconv.ParseUint(args[argIndex], 0, 8) if err != nil { - return 0, true + return 0, false } - return n, false + return byte(n), true } func typeSwap(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { @@ -999,8 +1000,8 @@ func typeSwap(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeDig(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - n, errored := getUintImm(args) - if errored { + n, ok := getUintImm(args, 0) + if !ok { return nil, nil } depth := int(n) + 1 @@ -1069,8 +1070,8 @@ func typeSetBit(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeCover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - n, errored := getUintImm(args) - if errored { + n, ok := getUintImm(args, 0) + if !ok { return nil, nil } depth := int(n) + 1 @@ -1097,8 +1098,8 @@ func typeCover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeUncover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - n, errored := getUintImm(args) - if errored { + n, ok := getUintImm(args, 0) + if !ok { return nil, nil } depth := int(n) + 1 @@ -1133,11 +1134,10 @@ func typeTxField(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) } func typeStore(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - n, errored := getUintImm(args) - if errored { + scratchIndex, ok := getUintImm(args, 0) + if !ok { return nil, nil } - scratchIndex := int(n) top := len(pgm.stack) - 1 if top >= 0 { pgm.scratchSpace[scratchIndex] = pgm.stack[top] @@ -1151,6 +1151,7 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { return nil, nil } for i := range pgm.scratchSpace { + // We can't know what slot stacktop is being stored in, but we can at least keep the slots that are the same type as stacktop if pgm.scratchSpace[i] != pgm.stack[top] { pgm.scratchSpace[i] = StackAny } @@ -1159,17 +1160,17 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeLoad(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - n, errored := getUintImm(args) - if errored { + scratchIndex, ok := getUintImm(args, 0) + if !ok { return nil, nil } - scratchIndex := int(n) return nil, StackTypes{pgm.scratchSpace[scratchIndex]} } func typeLoads(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { scratchType := pgm.scratchSpace[0] for _, item := range pgm.scratchSpace { + // If all the scratch slots are one type, then we can say we are loading that type if item != scratchType { return nil, nil } From 4654a1fde4d766db68c53e664ca8c4f4f7a1e627 Mon Sep 17 00:00:00 2001 From: Ilan Date: Fri, 3 Jun 2022 11:12:41 -0400 Subject: [PATCH 08/11] Addressed comments --- data/transactions/logic/assembler.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 7ae756b523..55313adc93 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -974,8 +974,8 @@ func asmDefault(ops *OpStream, spec *OpSpec, args []string) error { return nil } -// Interprets the arg at index argIndex as byte-long uint immediate -func getUintImm(args []string, argIndex int) (byte, bool) { +// Interprets the arg at index argIndex as byte-long immediate +func getByteImm(args []string, argIndex int) (byte, bool) { if len(args) <= argIndex { return 0, false } @@ -1000,7 +1000,7 @@ func typeSwap(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeDig(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - n, ok := getUintImm(args, 0) + n, ok := getByteImm(args, 0) if !ok { return nil, nil } @@ -1070,7 +1070,7 @@ func typeSetBit(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeCover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - n, ok := getUintImm(args, 0) + n, ok := getByteImm(args, 0) if !ok { return nil, nil } @@ -1098,7 +1098,7 @@ func typeCover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeUncover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - n, ok := getUintImm(args, 0) + n, ok := getByteImm(args, 0) if !ok { return nil, nil } @@ -1134,7 +1134,7 @@ func typeTxField(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) } func typeStore(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - scratchIndex, ok := getUintImm(args, 0) + scratchIndex, ok := getByteImm(args, 0) if !ok { return nil, nil } @@ -1160,7 +1160,7 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { } func typeLoad(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes) { - scratchIndex, ok := getUintImm(args, 0) + scratchIndex, ok := getByteImm(args, 0) if !ok { return nil, nil } From 8e0b841ff2fd3c8b371c73d7b2e899b84a8317a1 Mon Sep 17 00:00:00 2001 From: michaeldiamant Date: Thu, 9 Jun 2022 10:41:35 -0400 Subject: [PATCH 09/11] Refactor scratchSpace construction into newOpStream --- data/transactions/logic/assembler.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 55313adc93..95b8e2bc59 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -252,12 +252,18 @@ type OpStream struct { // newOpStream constructs OpStream instances ready to invoke assemble. A new // OpStream must be used for each call to assemble(). func newOpStream(version uint64) OpStream { - return OpStream{ + o := OpStream{ labels: make(map[string]int), OffsetToLine: make(map[int]int), typeTracking: true, Version: version, } + + for i, _ := range o.known.scratchSpace { + o.known.scratchSpace[i] = StackUint64 + } + + return o } // ProgramKnowledge tracks statically known information as we assemble @@ -1355,9 +1361,6 @@ func (ops *OpStream) assemble(text string) error { return ops.errorf("Can not assemble version %d", ops.Version) } scanner := bufio.NewScanner(fin) - for i := range ops.known.scratchSpace { - ops.known.scratchSpace[i] = StackUint64 - } for scanner.Scan() { ops.sourceLine++ line := scanner.Text() From efc77f7ab9aab20fa4a625b37ef316a656059704 Mon Sep 17 00:00:00 2001 From: michaeldiamant Date: Thu, 9 Jun 2022 10:45:01 -0400 Subject: [PATCH 10/11] Remove unncessary variable declaration --- data/transactions/logic/assembler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 95b8e2bc59..87b8fba6c8 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -259,7 +259,7 @@ func newOpStream(version uint64) OpStream { Version: version, } - for i, _ := range o.known.scratchSpace { + for i := range o.known.scratchSpace { o.known.scratchSpace[i] = StackUint64 } From f40fdf20740435051d3cbc156021a6d1f0ee1acc Mon Sep 17 00:00:00 2001 From: iten-alg <85889519+iten-alg@users.noreply.github.com> Date: Thu, 9 Jun 2022 11:19:54 -0400 Subject: [PATCH 11/11] Add loads test case Co-authored-by: Michael Diamant --- data/transactions/logic/assembler_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index bf9d8e1a72..2f9b9b81db 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2409,6 +2409,8 @@ func TestScratchTypeCheck(t *testing.T) { testProg(t, "byte 0x01; store 0; load 0; int 1; +", AssemblerMaxVersion, Expect{5, "+ arg 0..."}) // Loads should know the type it's loading if all the slots are the same type testProg(t, "int 0; loads; btoi", AssemblerMaxVersion, Expect{3, "btoi arg 0..."}) + // Loads doesn't know the type when slot types vary + testProg(t, "byte 0x01; store 0; int 1; loads; btoi", AssemblerMaxVersion) // Stores should only set slots to StackAny if they are not the same type as what is being stored testProg(t, "byte 0x01; store 0; int 3; byte 0x01; stores; load 0; int 1; +", AssemblerMaxVersion, Expect{8, "+ arg 0..."}) // ScratchSpace should reset after hitting label in deadcode