Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AVM: Narrow types and check assignability using StackType #12

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion data/transactions/logic/assembler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,20 @@ func typePushInts(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes,
return nil, types, nil
}

func typeBzero(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) {
// Bzero should only allow its input int to be up to maxStringSize bytes
return StackTypes{StackUint64.narrowed(bound(0, maxStringSize))}, StackTypes{StackBytes}, nil
}

func typeByte(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) {
if len(args) == 0 {
return nil, StackTypes{StackBytes}, nil
}
val, _, _ := parseBinaryArgs(args)
l := uint64(len(val))
return nil, StackTypes{NewStackType(avmBytes, static(l), fmt.Sprintf("[%d]byte", l))}, nil
}

func joinIntsOnOr(singularTerminator string, list ...int) string {
if len(list) == 1 {
switch list[0] {
Expand Down Expand Up @@ -1605,7 +1619,7 @@ const anyImmediates = -1

var pseudoOps = map[string]map[int]OpSpec{
"int": {anyImmediates: OpSpec{Name: "int", Proto: proto(":i"), OpDetails: assembler(asmInt)}},
"byte": {anyImmediates: OpSpec{Name: "byte", Proto: proto(":b"), OpDetails: assembler(asmByte)}},
"byte": {anyImmediates: OpSpec{Name: "byte", Proto: proto(":b"), OpDetails: assembler(asmByte).typed(typeByte)}},
// parse basics.Address, actually just another []byte constant
"addr": {anyImmediates: OpSpec{Name: "addr", Proto: proto(":b"), OpDetails: assembler(asmAddr)}},
// take a signature, hash it, and take first 4 bytes, actually just another []byte constant
Expand Down
15 changes: 8 additions & 7 deletions data/transactions/logic/assembler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ int 1
+
// comment
`
testProg(t, source, AssemblerMaxVersion, Expect{3, "+ arg 0 wanted type uint64 got []byte"})
testProg(t, source, AssemblerMaxVersion, Expect{3, "+ arg 0 wanted type uint64 got [5]byte"})
}

// mutateProgVersion replaces version (first two symbols) in hex-encoded program
Expand Down Expand Up @@ -1858,7 +1858,7 @@ balance
int 1
==`
for v := uint64(2); v < directRefEnabledVersion; v++ {
testProg(t, source, v, Expect{2, "balance arg 0 wanted type uint64 got []byte"})
testProg(t, source, v, Expect{2, "balance arg 0 wanted type uint64 got [1]byte"})
}
for v := uint64(directRefEnabledVersion); v <= AssemblerMaxVersion; v++ {
testProg(t, source, v)
Expand All @@ -1874,7 +1874,7 @@ min_balance
int 1
==`
for v := uint64(3); v < directRefEnabledVersion; v++ {
testProg(t, source, v, Expect{2, "min_balance arg 0 wanted type uint64 got []byte"})
testProg(t, source, v, Expect{2, "min_balance arg 0 wanted type uint64 got [1]byte"})
}
for v := uint64(directRefEnabledVersion); v <= AssemblerMaxVersion; v++ {
testProg(t, source, v)
Expand Down Expand Up @@ -2673,10 +2673,11 @@ func TestTxTypes(t *testing.T) {
t.Parallel()
testProg(t, "itxn_begin; itxn_field Sender", 5, Expect{1, "itxn_field Sender expects 1 stack argument..."})
testProg(t, "itxn_begin; int 1; itxn_field Sender", 5, Expect{1, "...wanted type addr got uint64"})
testProg(t, "itxn_begin; byte 0x56127823; itxn_field Sender", 5)
testProg(t, "itxn_begin; byte 0x56127823; itxn_field Sender", 5, Expect{1, "...wanted type addr got [4]byte"})
testProg(t, "itxn_begin; global ZeroAddress; itxn_field Sender", 5)

testProg(t, "itxn_begin; itxn_field Amount", 5, Expect{1, "itxn_field Amount expects 1 stack argument..."})
testProg(t, "itxn_begin; byte 0x87123376; itxn_field Amount", 5, Expect{1, "...wanted type uint64 got []byte"})
testProg(t, "itxn_begin; byte 0x87123376; itxn_field Amount", 5, Expect{1, "...wanted type uint64 got [4]byte"})
testProg(t, "itxn_begin; int 1; itxn_field Amount", 5)
}

Expand All @@ -2688,14 +2689,14 @@ func TestBadInnerFields(t *testing.T) {
testProg(t, "itxn_begin; int 1000; itxn_field LastValid", 5, Expect{1, "...is not allowed."})
testProg(t, "itxn_begin; int 32; bzero; itxn_field Lease", 5, Expect{1, "...is not allowed."})
testProg(t, "itxn_begin; byte 0x7263; itxn_field Note", 5, Expect{1, "...Note field was introduced in v6..."})
testProg(t, "itxn_begin; byte 0x7263; itxn_field VotePK", 5, Expect{1, "...VotePK field was introduced in v6..."})
testProg(t, "itxn_begin; global ZeroAddress; itxn_field VotePK", 5, Expect{1, "...VotePK field was introduced in v6..."})
testProg(t, "itxn_begin; int 32; bzero; itxn_field TxID", 5, Expect{1, "...is not allowed."})

testProg(t, "itxn_begin; int 1000; itxn_field FirstValid", 6, Expect{1, "...is not allowed."})
testProg(t, "itxn_begin; int 1000; itxn_field LastValid", 6, Expect{1, "...is not allowed."})
testProg(t, "itxn_begin; int 32; bzero; itxn_field Lease", 6, Expect{1, "...is not allowed."})
testProg(t, "itxn_begin; byte 0x7263; itxn_field Note", 6)
testProg(t, "itxn_begin; byte 0x7263; itxn_field VotePK", 6)
testProg(t, "itxn_begin; global ZeroAddress; itxn_field VotePK", 6)
testProg(t, "itxn_begin; int 32; bzero; itxn_field TxID", 6, Expect{1, "...is not allowed."})
}

Expand Down
17 changes: 17 additions & 0 deletions data/transactions/logic/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,23 @@ func (at avmType) String() string {
return "internal error, unknown type"
}

// stackType lifts the avmType to a StackType
// it can do this because the base StackTypes
// are a superset of avmType
func (at avmType) stackType() StackType {
switch at {
case avmNone:
return StackNone
case avmAny:
return StackAny
case avmUint64:
return StackUint64
case avmBytes:
return StackBytes
}
return StackNone
}

var (
// StackUint64 is any valid uint64
StackUint64 = NewStackType(avmUint64, bound(0, math.MaxUint64))
Expand Down
5 changes: 3 additions & 2 deletions data/transactions/logic/evalAppTxn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,16 @@ func TestFieldTypes(t *testing.T) {
t.Parallel()

ep, _, _ := MakeSampleEnv()
TestApp(t, "itxn_begin; byte \"pay\"; itxn_field Sender;", ep, "not an address")
// Use NoTrack to skip assembly errors
TestApp(t, NoTrack("itxn_begin; byte \"pay\"; itxn_field Sender;"), ep, "not an address")
TestApp(t, NoTrack("itxn_begin; int 7; itxn_field Receiver;"), ep, "not an address")
TestApp(t, "itxn_begin; byte \"\"; itxn_field CloseRemainderTo;", ep, "not an address")
TestApp(t, "itxn_begin; byte \"\"; itxn_field AssetSender;", ep, "not an address")
// can't really tell if it's an addres, so 32 bytes gets further
TestApp(t, "itxn_begin; byte \"01234567890123456789012345678901\"; itxn_field AssetReceiver; int 1",
ep, "invalid Account reference")
// but a b32 string rep is not an account
TestApp(t, "itxn_begin; byte \"GAYTEMZUGU3DOOBZGAYTEMZUGU3DOOBZGAYTEMZUGU3DOOBZGAYZIZD42E\"; itxn_field AssetCloseTo;",
TestApp(t, NoTrack("itxn_begin; byte \"GAYTEMZUGU3DOOBZGAYTEMZUGU3DOOBZGAYTEMZUGU3DOOBZGAYZIZD42E\"; itxn_field AssetCloseTo;"),
ep, "not an address")

TestApp(t, NoTrack("itxn_begin; byte \"pay\"; itxn_field Fee;"), ep, "not a uint64")
Expand Down
2 changes: 1 addition & 1 deletion data/transactions/logic/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4545,7 +4545,7 @@ func TestBits(t *testing.T) {

testAccepts(t, "byte 0xfffff0; int 21; int 1; setbit; byte 0xfffff4; ==", 3)
testAccepts(t, "byte 0xfffff4; int 1; int 0; setbit; byte 0xbffff4; ==", 3)
testPanics(t, "byte 0xfffff4; int 24; int 0; setbit; byte 0xbf; ==", 3)
testPanics(t, "byte 0xfffff4; int 24; int 0; setbit; byte 0xbffff4; ==", 3)

testAccepts(t, "byte 0x0000; int 3; int 1; setbit; byte 0x1000; ==", 3)
testAccepts(t, "byte 0x0000; int 15; int 1; setbit; byte 0x0001; ==", 3)
Expand Down
2 changes: 1 addition & 1 deletion data/transactions/logic/opcodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ var OpSpecs = []OpSpec{
{0xac, "b&", opBytesBitAnd, proto("bb:b"), 4, costly(6)},
{0xad, "b^", opBytesBitXor, proto("bb:b"), 4, costly(6)},
{0xae, "b~", opBytesBitNot, proto("b:b"), 4, costly(4)},
{0xaf, "bzero", opBytesZero, proto("i:b"), 4, detDefault()},
{0xaf, "bzero", opBytesZero, proto("i:b"), 4, detDefault().typed(typeBzero)},

// AVM "effects"
{0xb0, "log", opLog, proto("b:"), 5, only(ModeApp)},
Expand Down