Skip to content

Commit dd7f7b6

Browse files
authored
Add additional functions to improve type tracking of AVM stack manipulation opcodes (#2710)
* TypeFuncs added with tests * Fixed dup test and edited code to use more literals
1 parent fb3f9a9 commit dd7f7b6

File tree

4 files changed

+92
-8
lines changed

4 files changed

+92
-8
lines changed

data/transactions/logic/assembler.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,56 @@ func typeDig(ops *OpStream, args []string) (StackTypes, StackTypes) {
11011101
return anys, returns
11021102
}
11031103

1104+
func typeEquals(ops *OpStream, args []string) (StackTypes, StackTypes) {
1105+
top := len(ops.typeStack) - 1
1106+
if top >= 0 {
1107+
//Require arg0 and arg1 to have same type
1108+
return StackTypes{ops.typeStack[top], ops.typeStack[top]}, oneInt
1109+
}
1110+
return oneAny.plus(oneAny), oneInt
1111+
}
1112+
1113+
func typeDup(ops *OpStream, args []string) (StackTypes, StackTypes) {
1114+
top := len(ops.typeStack) - 1
1115+
if top >= 0 {
1116+
return StackTypes{ops.typeStack[top]}, StackTypes{ops.typeStack[top], ops.typeStack[top]}
1117+
}
1118+
return StackTypes{StackAny}, oneAny.plus(oneAny)
1119+
}
1120+
1121+
func typeDupTwo(ops *OpStream, args []string) (StackTypes, StackTypes) {
1122+
topTwo := oneAny.plus(oneAny)
1123+
top := len(ops.typeStack) - 1
1124+
if top >= 0 {
1125+
topTwo[1] = ops.typeStack[top]
1126+
if top >= 1 {
1127+
topTwo[0] = ops.typeStack[top-1]
1128+
}
1129+
}
1130+
result := topTwo.plus(topTwo)
1131+
return topTwo, result
1132+
}
1133+
1134+
func typeSelect(ops *OpStream, args []string) (StackTypes, StackTypes) {
1135+
selectArgs := twoAny.plus(oneInt)
1136+
top := len(ops.typeStack) - 1
1137+
if top >= 2 {
1138+
if ops.typeStack[top-1] == ops.typeStack[top-2] {
1139+
return selectArgs, StackTypes{ops.typeStack[top-1]}
1140+
}
1141+
}
1142+
return selectArgs, StackTypes{StackAny}
1143+
}
1144+
1145+
func typeSetBit(ops *OpStream, args []string) (StackTypes, StackTypes) {
1146+
setBitArgs := oneAny.plus(twoInts)
1147+
top := len(ops.typeStack) - 1
1148+
if top >= 2 {
1149+
return setBitArgs, StackTypes{ops.typeStack[top-2]}
1150+
}
1151+
return setBitArgs, StackTypes{StackAny}
1152+
}
1153+
11041154
func typeCover(ops *OpStream, args []string) (StackTypes, StackTypes) {
11051155
if len(args) == 0 {
11061156
return oneAny, oneAny

data/transactions/logic/assembler_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2165,6 +2165,40 @@ func TestDigAsm(t *testing.T) {
21652165

21662166
}
21672167

2168+
func TestEqualsTypeCheck(t *testing.T) {
2169+
t.Parallel()
2170+
testProg(t, "int 1; byte 0x1234; ==", AssemblerMaxVersion, expect{3, "== arg 0..."})
2171+
testProg(t, "int 1; byte 0x1234; !=", AssemblerMaxVersion, expect{3, "!= arg 0..."})
2172+
testProg(t, "byte 0x1234; int 1; ==", AssemblerMaxVersion, expect{3, "== arg 0..."})
2173+
testProg(t, "byte 0x1234; int 1; !=", AssemblerMaxVersion, expect{3, "!= arg 0..."})
2174+
}
2175+
2176+
func TestDupTypeCheck(t *testing.T) {
2177+
t.Parallel()
2178+
testProg(t, "byte 0x1234; dup; int 1; +", AssemblerMaxVersion, expect{4, "+ arg 0..."})
2179+
testProg(t, "byte 0x1234; int 1; dup; +", AssemblerMaxVersion)
2180+
testProg(t, "byte 0x1234; int 1; dup2; +", AssemblerMaxVersion, expect{4, "+ arg 0..."})
2181+
testProg(t, "int 1; byte 0x1234; dup2; +", AssemblerMaxVersion, expect{4, "+ arg 1..."})
2182+
2183+
testProg(t, "byte 0x1234; int 1; dup; dig 1; len", AssemblerMaxVersion, expect{5, "len arg 0..."})
2184+
testProg(t, "int 1; byte 0x1234; dup; dig 1; !", AssemblerMaxVersion, expect{5, "! arg 0..."})
2185+
2186+
testProg(t, "byte 0x1234; int 1; dup2; dig 2; len", AssemblerMaxVersion, expect{5, "len arg 0..."})
2187+
testProg(t, "int 1; byte 0x1234; dup2; dig 2; !", AssemblerMaxVersion, expect{5, "! arg 0..."})
2188+
}
2189+
2190+
func TestSelectTypeCheck(t *testing.T) {
2191+
t.Parallel()
2192+
testProg(t, "int 1; int 2; int 3; select; len", AssemblerMaxVersion, expect{5, "len arg 0..."})
2193+
testProg(t, "byte 0x1234; byte 0x5678; int 3; select; !", AssemblerMaxVersion, expect{5, "! arg 0..."})
2194+
}
2195+
2196+
func TestSetBitTypeCheck(t *testing.T) {
2197+
t.Parallel()
2198+
testProg(t, "int 1; int 2; int 3; setbit; len", AssemblerMaxVersion, expect{5, "len arg 0..."})
2199+
testProg(t, "byte 0x1234; int 2; int 3; setbit; !", AssemblerMaxVersion, expect{5, "! arg 0..."})
2200+
}
2201+
21682202
func TestCoverAsm(t *testing.T) {
21692203
t.Parallel()
21702204
testProg(t, `int 4; byte "john"; int 5; cover 2; pop; +`, AssemblerMaxVersion)

data/transactions/logic/eval_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4064,14 +4064,14 @@ func TestAllowedOpcodesV2(t *testing.T) {
40644064
"dup2": "int 1; int 2; dup2",
40654065
"concat": "byte 0x41; dup; concat",
40664066
"substring": "byte 0x41; substring 0 1",
4067-
"substring3": "byte 0x41; dup; dup; substring3",
4067+
"substring3": "byte 0x41; int 0; int 1; substring3",
40684068
"balance": "int 1; balance",
40694069
"app_opted_in": "int 0; dup; app_opted_in",
40704070
"app_local_get": "int 0; byte 0x41; app_local_get",
40714071
"app_local_get_ex": "int 0; dup; byte 0x41; app_local_get_ex",
40724072
"app_global_get": "int 0; byte 0x41; app_global_get",
40734073
"app_global_get_ex": "int 0; byte 0x41; app_global_get_ex",
4074-
"app_local_put": "int 0; dup; byte 0x41; app_local_put",
4074+
"app_local_put": "int 0; byte 0x41; dup; app_local_put",
40754075
"app_global_put": "byte 0x41; dup; app_global_put",
40764076
"app_local_del": "int 0; byte 0x41; app_local_del",
40774077
"app_global_del": "byte 0x41; app_global_del",

data/transactions/logic/opcodes.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ var OpSpecs = []OpSpec{
152152
{0x0f, ">=", opGe, asmDefault, disDefault, twoInts, oneInt, 1, modeAny, opDefault},
153153
{0x10, "&&", opAnd, asmDefault, disDefault, twoInts, oneInt, 1, modeAny, opDefault},
154154
{0x11, "||", opOr, asmDefault, disDefault, twoInts, oneInt, 1, modeAny, opDefault},
155-
{0x12, "==", opEq, asmDefault, disDefault, twoAny, oneInt, 1, modeAny, opDefault},
156-
{0x13, "!=", opNeq, asmDefault, disDefault, twoAny, oneInt, 1, modeAny, opDefault},
155+
{0x12, "==", opEq, asmDefault, disDefault, twoAny, oneInt, 1, modeAny, stacky(typeEquals)},
156+
{0x13, "!=", opNeq, asmDefault, disDefault, twoAny, oneInt, 1, modeAny, stacky(typeEquals)},
157157
{0x14, "!", opNot, asmDefault, disDefault, oneInt, oneInt, 1, modeAny, opDefault},
158158
{0x15, "len", opLen, asmDefault, disDefault, oneBytes, oneInt, 1, modeAny, opDefault},
159159
{0x16, "itob", opItob, asmDefault, disDefault, oneInt, oneBytes, 1, modeAny, opDefault},
@@ -212,21 +212,21 @@ var OpSpecs = []OpSpec{
212212
{0x43, "return", opReturn, asmDefault, disDefault, oneInt, nil, 2, modeAny, opDefault},
213213
{0x44, "assert", opAssert, asmDefault, disDefault, oneInt, nil, 3, modeAny, opDefault},
214214
{0x48, "pop", opPop, asmDefault, disDefault, oneAny, nil, 1, modeAny, opDefault},
215-
{0x49, "dup", opDup, asmDefault, disDefault, oneAny, twoAny, 1, modeAny, opDefault},
216-
{0x4a, "dup2", opDup2, asmDefault, disDefault, twoAny, twoAny.plus(twoAny), 2, modeAny, opDefault},
215+
{0x49, "dup", opDup, asmDefault, disDefault, oneAny, twoAny, 1, modeAny, stacky(typeDup)},
216+
{0x4a, "dup2", opDup2, asmDefault, disDefault, twoAny, twoAny.plus(twoAny), 2, modeAny, stacky(typeDupTwo)},
217217
// There must be at least one thing on the stack for dig, but
218218
// it would be nice if we did better checking than that.
219219
{0x4b, "dig", opDig, asmDefault, disDefault, oneAny, twoAny, 3, modeAny, stacky(typeDig, "n")},
220220
{0x4c, "swap", opSwap, asmDefault, disDefault, twoAny, twoAny, 3, modeAny, stacky(typeSwap)},
221-
{0x4d, "select", opSelect, asmDefault, disDefault, twoAny.plus(oneInt), oneAny, 3, modeAny, opDefault},
221+
{0x4d, "select", opSelect, asmDefault, disDefault, twoAny.plus(oneInt), oneAny, 3, modeAny, stacky(typeSelect)},
222222
{0x4e, "cover", opCover, asmDefault, disDefault, oneAny, oneAny, 5, modeAny, stacky(typeCover, "n")},
223223
{0x4f, "uncover", opUncover, asmDefault, disDefault, oneAny, oneAny, 5, modeAny, stacky(typeUncover, "n")},
224224

225225
{0x50, "concat", opConcat, asmDefault, disDefault, twoBytes, oneBytes, 2, modeAny, opDefault},
226226
{0x51, "substring", opSubstring, assembleSubstring, disDefault, oneBytes, oneBytes, 2, modeAny, immediates("s", "e")},
227227
{0x52, "substring3", opSubstring3, asmDefault, disDefault, byteIntInt, oneBytes, 2, modeAny, opDefault},
228228
{0x53, "getbit", opGetBit, asmDefault, disDefault, anyInt, oneInt, 3, modeAny, opDefault},
229-
{0x54, "setbit", opSetBit, asmDefault, disDefault, anyIntInt, oneAny, 3, modeAny, opDefault},
229+
{0x54, "setbit", opSetBit, asmDefault, disDefault, anyIntInt, oneAny, 3, modeAny, stacky(typeSetBit)},
230230
{0x55, "getbyte", opGetByte, asmDefault, disDefault, byteInt, oneInt, 3, modeAny, opDefault},
231231
{0x56, "setbyte", opSetByte, asmDefault, disDefault, byteIntInt, oneBytes, 3, modeAny, opDefault},
232232
{0x57, "extract", opExtract, asmDefault, disDefault, oneBytes, oneBytes, 5, modeAny, immediates("s", "l")},

0 commit comments

Comments
 (0)