From e54d4e758fcc17769a6572e8895f2a83809e3f9c Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 10 Feb 2023 07:52:59 -0500 Subject: [PATCH 01/41] initial addition of new StackType --- data/transactions/logic/assembler.go | 22 +- data/transactions/logic/eval.go | 322 +++++++++++++++---- data/transactions/logic/evalStateful_test.go | 2 +- data/transactions/logic/eval_test.go | 26 +- 4 files changed, 293 insertions(+), 79 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index f7979e6828..8bf1529d01 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -312,7 +312,7 @@ type ProgramKnowledge struct { func (pgm *ProgramKnowledge) top() (StackType, bool) { if len(pgm.stack) == 0 { - return pgm.bottom, pgm.bottom != StackNone + return pgm.bottom, pgm.bottom.AVMType != avmNone } last := len(pgm.stack) - 1 return pgm.stack[last], true @@ -387,7 +387,7 @@ func (ops *OpStream) returns(spec *OpSpec, replacement StackType) { end := len(ops.known.stack) tip := ops.known.stack[end-len(spec.Return.Types):] for i := range tip { - if tip[i] == StackAny { + if tip[i].AVMType == avmAny { tip[i] = replacement return } @@ -1226,7 +1226,7 @@ func typeBury(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, err idx := top - n if idx < 0 { - if pgm.bottom == StackNone { + if pgm.bottom.AVMType == avmNone { // By demanding n+1 elements, we'll trigger an error return anyTypes(n + 1), nil, nil } @@ -1335,7 +1335,7 @@ func typeDupTwo(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e func typeSelect(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { top := len(pgm.stack) - 1 if top >= 2 { - if pgm.stack[top-1] == pgm.stack[top-2] { + if pgm.stack[top-1].AVMType == pgm.stack[top-2].AVMType { return nil, StackTypes{pgm.stack[top-1]}, nil } } @@ -1421,7 +1421,7 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e 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 + pgm.scratchSpace[i].AVMType = avmAny } } return nil, nil, nil @@ -1442,7 +1442,7 @@ func typeProto(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, er return nil, nil, nil } - if len(pgm.stack) != 0 || pgm.bottom != StackAny { + if len(pgm.stack) != 0 || pgm.bottom.AVMType != avmAny { return nil, nil, fmt.Errorf("proto must be unreachable from previous PC") } pgm.stack = anyTypes(a) @@ -1693,13 +1693,13 @@ func (le lineError) Unwrap() error { return le.Err } -func typecheck(expected, got StackType) bool { +func typecheck(expected, got avmType) bool { // Some ops push 'any' and we wait for run time to see what it is. // Some of those 'any' are based on fields that we _could_ know now but haven't written a more detailed system of typecheck for (yet). - if expected == StackAny && got == StackNone { // Any is lenient, but stack can't be empty + if expected == avmAny && got == avmNone { // Any is lenient, but stack can't be empty return false } - if (expected == StackAny) || (got == StackAny) { + if (expected == avmAny) || (got == avmAny) { return true } return expected == got @@ -1813,7 +1813,7 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction return } argcount := len(args) - if argcount > len(ops.known.stack) && ops.known.bottom == StackNone { + if argcount > len(ops.known.stack) && ops.known.bottom.AVMType == avmNone { ops.typeErrorf("%s expects %d stack arguments but stack height is %d", strings.Join(instruction, " "), argcount, len(ops.known.stack)) } else { @@ -1827,7 +1827,7 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction } else { ops.trace(", %s", argType) } - if !typecheck(argType, stype) { + if !typecheck(argType.AVMType, stype.AVMType) { ops.typeErrorf("%s arg %d wanted type %s got %s", strings.Join(instruction, " "), i, argType, stype) } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 158ca6e08b..f9dd0f7396 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -82,11 +82,11 @@ type stackValue struct { Bytes []byte } -func (sv stackValue) argType() StackType { +func (sv stackValue) argType() avmType { if sv.Bytes != nil { - return StackBytes + return avmBytes } - return StackUint64 + return avmUint64 } func (sv stackValue) typeName() string { @@ -154,7 +154,7 @@ func (sv stackValue) string(limit int) (string, error) { } func (sv stackValue) toTealValue() (tv basics.TealValue) { - if sv.argType() == StackBytes { + if sv.argType() == avmBytes { return basics.TealValue{Type: basics.TealBytesType, Bytes: string(sv.Bytes)} } return basics.TealValue{Type: basics.TealUintType, Uint: sv.Uint} @@ -601,26 +601,249 @@ func (cx *EvalContext) RunMode() RunMode { return cx.runModeFlags } -// StackType describes the type of a value on the operand stack -type StackType byte +// avmType describes the type of a value on the operand stack +type avmType byte const ( - // StackNone in an OpSpec shows that the op pops or yields nothing - StackNone StackType = iota + // avmNone in an OpSpec shows that the op pops or yields nothing + avmNone avmType = iota - // StackAny in an OpSpec shows that the op pops or yield any type - StackAny + // avmAny in an OpSpec shows that the op pops or yield any type + avmAny - // StackUint64 in an OpSpec shows that the op pops or yields a uint64 - StackUint64 + // avmUint64 in an OpSpec shows that the op pops or yields a uint64 + avmUint64 - // StackBytes in an OpSpec shows that the op pops or yields a []byte - StackBytes + // avmBytes in an OpSpec shows that the op pops or yields a []byte + avmBytes ) +func (at avmType) String() string { + switch at { + case avmNone: + return "none" + case avmAny: + return "any" + case avmUint64: + return "uint64" + case avmBytes: + return "[]byte" + } + return "internal error, unknown type" +} + +func (at avmType) stackType() StackType { + switch at { + case avmNone: + return StackNone + case avmAny: + return StackAny + case avmUint64: + return StackUint64 + case avmBytes: + return StackBytes + default: + panic(fmt.Sprintf("no stack type matching: %s", at)) + } +} + +var ( + // + // Base stack types the avm knows about + // + + // StackUint64 is any valid uint64 + StackUint64 = NewStackType(avmUint64, bound(0, math.MaxUint64)) + // StackBytes is any valid bytestring + StackBytes = NewStackType(avmBytes, bound(0, maxStringSize)) + // StackAny could be Bytes or Uint64 + StackAny = StackType{ + Name: avmAny.String(), + AVMType: avmAny, + ValueBound: StackUint64.ValueBound, + LengthBound: StackBytes.LengthBound, + } + // StackNone is used when there is no input or output to + // an opcode + StackNone = StackType{ + Name: avmNone.String(), + AVMType: avmNone, + } + + // + // Higher level types + // + + // StackBoolean constrains the int to 1 or 0, representing True or False + StackBoolean = NewStackType(avmUint64, bound(0, 1), "bool") + // StackHash represents output from a hash function or a field that returns a hash + StackHash = NewStackType(avmBytes, static(32), "hash") + // StackAddress represents a public key or address for an account + StackAddress = NewStackType(avmBytes, static(32), "addr") + // StackBigInt represents a bytestring that should be treated like an int + StackBigInt = NewStackType(avmBytes, bound(0, maxByteMathSize), "bigint") + // StackMethodSelector represents a bytestring that should be treated like a method selector + StackMethodSelector = NewStackType(avmBytes, static(4), "method") + // StackStorageKey represents a bytestring that can be used as a key to some storage (global/local/box) + StackStorageKey = NewStackType(avmBytes, bound(0, 64), "key") + + // AllStackTypes is a list of all the stack types we recognize + // so that we can iterate over them in doc prep + AllStackTypes = []StackType{ + StackUint64, + StackBytes, + StackAny, + StackNone, + StackBoolean, + StackHash, + StackAddress, + StackBigInt, + StackMethodSelector, + StackStorageKey, + } +) + +// StackType describes the type of a value on the operand stack +type StackType struct { + Name string + AVMType avmType + LengthBound [2]uint64 + ValueBound [2]uint64 +} + +// NewStackType Initializes a new StackType with fields passed +func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { + name := at.String() + if len(stname) > 0 { + name = stname[0] + } + + st := StackType{Name: name, AVMType: at} + + switch at { + case avmBytes: + st.LengthBound = bounds + case avmUint64: + st.ValueBound = bounds + } + + return st +} + +func (st StackType) narrowed(bounds [2]uint64) StackType { + // It's static, set the name to show + // the static value + if bounds[0] == bounds[1] { + switch st.AVMType { + case avmBytes: + return NewStackType(st.AVMType, bounds, fmt.Sprintf("[%d]byte", bounds[0])) + case avmUint64: + return NewStackType(st.AVMType, bounds, fmt.Sprintf("%d", bounds[0])) + } + } + return NewStackType(st.AVMType, bounds) +} + +// AssignableTo returns a bool indicating whether the receiver can be +// assigned to some other type that is expected by the next operation +func (st StackType) AssignableTo(other StackType) bool { + if st.AVMType == avmNone || other.AVMType == avmNone { + return false + } + + if st.AVMType == avmAny || other.AVMType == avmAny { + return true + } + + // By now, both are either uint or bytes + // and must match + if st.AVMType != other.AVMType { + return false + } + + // Same type now + + // Check if our constraints will be satisfied by + // the other type + switch st.AVMType { + case avmBytes: + smin, smax := st.LengthBound[0], st.LengthBound[1] + omin, omax := other.LengthBound[0], other.LengthBound[1] + + // yes definitely + // [32,32] => [0..4k] + // [32,32] => [32,32] + + // yes, maybe determined at runtime + // [0..4k] => [32,32] + + // no, cant fit + // [64,64] => [32,32] + // no, makes no sense + // [32,32] => [64,64] + + // we only have 0-N and [N,N] (static) and only + // those that are both not static and have different lengths + // can be assigned + return !(smin == smax && omin == omax && smin != omin) + + case avmUint64: + // No static values at compile + // time so hard to do any typechecks for assembler, + // dont use this for avm runtime + return true + default: + panic("no stack type match in AssignableTo check") + } +} + +func (st StackType) String() string { + return st.Name +} + +// Typed tells whether the StackType is a specific concrete type. +func (st StackType) Typed() bool { + switch st.AVMType { + case avmUint64, avmBytes: + return true + } + return false +} + // StackTypes is an alias for a list of StackType with syntactic sugar type StackTypes []StackType +// Reverse returns the StackTypes in reverse order +// useful for displaying the stack as an op sees it +func (st StackTypes) Reverse() StackTypes { + nst := make(StackTypes, len(st)) + for idx := 0; idx < len(st); idx++ { + nst[idx] = st[len(st)-1-idx] + } + return nst +} + +func (st StackTypes) String() string { + // Note this reverses the stack so top appears first + return fmt.Sprintf("(%s)", strings.Join(st.strings(), ", ")) +} + +func (st StackTypes) strings() []string { + var strs = make([]string, len(st)) + for idx, s := range st { + strs[idx] = s.String() + } + return strs +} + +func bound(min, max uint64) [2]uint64 { + return [2]uint64{min, max} +} + +func static(size uint64) [2]uint64 { + return bound(size, size) +} + func parseStackTypes(spec string) StackTypes { if spec == "" { return nil @@ -636,6 +859,18 @@ func parseStackTypes(spec string) StackTypes { types[i] = StackUint64 case 'x': types[i] = StackNone + case 'A': + types[i] = StackAddress + case 'N': + types[i] = StackBigInt + case 'B': + types[i] = StackBoolean + case 'H': + types[i] = StackHash + case 'M': + types[i] = StackMethodSelector + case 'K': + types[i] = StackStorageKey default: panic(spec) } @@ -643,29 +878,6 @@ func parseStackTypes(spec string) StackTypes { return types } -func (st StackType) String() string { - switch st { - case StackNone: - return "None" - case StackAny: - return "any" - case StackUint64: - return "uint64" - case StackBytes: - return "[]byte" - } - return "internal error, unknown type" -} - -// Typed tells whether the StackType is a specific concrete type. -func (st StackType) Typed() bool { - switch st { - case StackUint64, StackBytes: - return true - } - return false -} - // PanicError wraps a recover() catching a panic() type PanicError struct { PanicValue interface{} @@ -982,8 +1194,8 @@ func versionCheck(program []byte, params *EvalParams) (uint64, int, error) { return version, vlen, nil } -func opCompat(expected, got StackType) bool { - if expected == StackAny { +func opCompat(expected, got avmType) bool { + if expected == avmAny { return true } return expected == got @@ -1061,7 +1273,7 @@ func (cx *EvalContext) step() error { } first := len(cx.stack) - len(spec.Arg.Types) for i, argType := range spec.Arg.Types { - if !opCompat(argType, cx.stack[first+i].argType()) { + if !opCompat(argType.AVMType, cx.stack[first+i].argType()) { return fmt.Errorf("%s arg %d wanted %s but got %s", spec.Name, i, argType, cx.stack[first+i].typeName()) } } @@ -1110,13 +1322,13 @@ func (cx *EvalContext) step() error { first = postheight - len(spec.Return.Types) for i, argType := range spec.Return.Types { stackType := cx.stack[first+i].argType() - if !opCompat(argType, stackType) { + if !opCompat(argType.AVMType, stackType) { if spec.AlwaysExits() { // We test in the loop because it's the uncommon case. break } return fmt.Errorf("%s produced %s but intended %s", spec.Name, cx.stack[first+i].typeName(), argType) } - if stackType == StackBytes && len(cx.stack[first+i].Bytes) > maxStringSize { + if stackType == avmBytes && len(cx.stack[first+i].Bytes) > maxStringSize { return fmt.Errorf("%s produced a too big (%d) byte-array", spec.Name, len(cx.stack[first+i].Bytes)) } } @@ -1512,7 +1724,7 @@ func opEq(cx *EvalContext) error { return fmt.Errorf("cannot compare (%s to %s)", cx.stack[prev].typeName(), cx.stack[last].typeName()) } var cond bool - if ta == StackBytes { + if ta == avmBytes { cond = bytes.Equal(cx.stack[prev].Bytes, cx.stack[last].Bytes) } else { cond = cx.stack[prev].Uint == cx.stack[last].Uint @@ -1651,7 +1863,7 @@ func opSqrt(cx *EvalContext) error { func opBitLen(cx *EvalContext) error { last := len(cx.stack) - 1 - if cx.stack[last].argType() == StackUint64 { + if cx.stack[last].argType() == avmUint64 { cx.stack[last].Uint = uint64(bits.Len64(cx.stack[last].Uint)) return nil } @@ -2309,10 +2521,10 @@ func opMatch(cx *EvalContext) error { continue } - if matchVal.argType() == StackBytes && bytes.Equal(matchVal.Bytes, stackArg.Bytes) { + if matchVal.argType() == avmBytes && bytes.Equal(matchVal.Bytes, stackArg.Bytes) { matchedIdx = i break - } else if matchVal.argType() == StackUint64 && matchVal.Uint == stackArg.Uint { + } else if matchVal.argType() == avmUint64 && matchVal.Uint == stackArg.Uint { matchedIdx = i break } @@ -2450,7 +2662,7 @@ func (cx *EvalContext) assetHoldingToValue(holding *basics.AssetHolding, fs asse return sv, fmt.Errorf("invalid asset_holding_get field %d", fs.field) } - if fs.ftype != sv.argType() { + if fs.ftype.AVMType != sv.argType() { return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) } return sv, nil @@ -2486,7 +2698,7 @@ func (cx *EvalContext) assetParamsToValue(params *basics.AssetParams, creator ba return sv, fmt.Errorf("invalid asset_params_get field %d", fs.field) } - if fs.ftype != sv.argType() { + if fs.ftype.AVMType != sv.argType() { return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) } return sv, nil @@ -2513,7 +2725,7 @@ func (cx *EvalContext) appParamsToValue(params *basics.AppParams, fs appParamsFi return sv, fmt.Errorf("invalid app_params_get field %d", fs.field) } - if fs.ftype != sv.argType() { + if fs.ftype.AVMType != sv.argType() { return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) } return sv, nil @@ -2847,7 +3059,7 @@ func (cx *EvalContext) txnFieldToStack(stxn *transactions.SignedTxnWithAD, fs *t return sv, fmt.Errorf("invalid txn field %s", fs.field) } - if fs.ftype != sv.argType() { + if fs.ftype.AVMType != sv.argType() { return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) } return sv, nil @@ -3295,7 +3507,7 @@ func (cx *EvalContext) globalFieldToValue(fs globalFieldSpec) (sv stackValue, er err = fmt.Errorf("invalid global field %d", fs.field) } - if fs.ftype != sv.argType() { + if fs.ftype.AVMType != sv.argType() { return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) } @@ -3721,7 +3933,7 @@ func opGetBit(cx *EvalContext) error { target := cx.stack[prev] var bit uint64 - if target.argType() == StackUint64 { + if target.argType() == avmUint64 { if idx > 63 { return errors.New("getbit index > 63 with with Uint") } @@ -3763,7 +3975,7 @@ func opSetBit(cx *EvalContext) error { return errors.New("setbit value > 1") } - if target.argType() == StackUint64 { + if target.argType() == avmUint64 { if idx > 63 { return errors.New("setbit index > 63 with Uint") } @@ -3984,7 +4196,7 @@ func opExtract64Bits(cx *EvalContext) error { // than by index into txn.Accounts. func (cx *EvalContext) accountReference(account stackValue) (basics.Address, uint64, error) { - if account.argType() == StackUint64 { + if account.argType() == avmUint64 { addr, err := cx.txn.Txn.AddressByIndex(account.Uint, cx.txn.Txn.Sender) return addr, account.Uint, err } @@ -4697,7 +4909,7 @@ func opItxnNext(cx *EvalContext) error { // that don't need (or want!) to allow low numbers to represent the account at // that index in Accounts array. func (cx *EvalContext) availableAccount(sv stackValue) (basics.Address, error) { - if sv.argType() != StackBytes || len(sv.Bytes) != crypto.DigestSize { + if sv.argType() != avmBytes || len(sv.Bytes) != crypto.DigestSize { return basics.Address{}, fmt.Errorf("not an address") } diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index a2f33cc909..a640ec9c28 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -2577,7 +2577,7 @@ func TestReturnTypes(t *testing.T) { stackType := cx.stack[i].argType() retType := spec.Return.Types[i] require.True( - t, typecheck(retType, stackType), + t, typecheck(retType.AVMType, stackType), "%s expected to return %s but actual is %s", spec.Name, retType, stackType, ) } diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 6731c19824..b89a6cd0da 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -3332,10 +3332,11 @@ intc_1 import random def foo(): - for i in range(64): - print('int {}'.format(random.randint(0,0x01ffffffffffffff))) - for i in range(63): - print('+') + + for i in range(64): + print('int {}'.format(random.randint(0,0x01ffffffffffffff))) + for i in range(63): + print('+') */ const addBenchmarkSource = `int 20472989571761113 int 80135167795737348 @@ -3470,10 +3471,11 @@ int 28939890412103745 import random def foo(): - print('int {}'.format(random.randint(0,0x01ffffffffffffff))) - for i in range(63): - print('int {}'.format(random.randint(0,0x01ffffffffffffff))) - print('+') + + print('int {}'.format(random.randint(0,0x01ffffffffffffff))) + for i in range(63): + print('int {}'.format(random.randint(0,0x01ffffffffffffff))) + print('+') */ const addBenchmark2Source = `int 8371863094338737 int 29595196041051360 @@ -4126,13 +4128,13 @@ func TestArgType(t *testing.T) { t.Parallel() var sv stackValue - require.Equal(t, StackUint64, sv.argType()) + require.Equal(t, avmUint64, sv.argType()) sv.Bytes = []byte("") - require.Equal(t, StackBytes, sv.argType()) + require.Equal(t, avmBytes, sv.argType()) sv.Uint = 1 - require.Equal(t, StackBytes, sv.argType()) + require.Equal(t, avmBytes, sv.argType()) sv.Bytes = nil - require.Equal(t, StackUint64, sv.argType()) + require.Equal(t, avmUint64, sv.argType()) } func TestApplicationsDisallowOldTeal(t *testing.T) { From 2f836a14f45329a410df8039fb4c6d3565f66cf0 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 10 Feb 2023 10:15:18 -0500 Subject: [PATCH 02/41] add more specific types to opspec proto --- data/transactions/logic/eval.go | 41 +++-------- data/transactions/logic/opcodes.go | 112 ++++++++++++++--------------- 2 files changed, 65 insertions(+), 88 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index f9dd0f7396..86263c3226 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -632,26 +632,7 @@ func (at avmType) String() string { return "internal error, unknown type" } -func (at avmType) stackType() StackType { - switch at { - case avmNone: - return StackNone - case avmAny: - return StackAny - case avmUint64: - return StackUint64 - case avmBytes: - return StackBytes - default: - panic(fmt.Sprintf("no stack type matching: %s", at)) - } -} - var ( - // - // Base stack types the avm knows about - // - // StackUint64 is any valid uint64 StackUint64 = NewStackType(avmUint64, bound(0, math.MaxUint64)) // StackBytes is any valid bytestring @@ -670,10 +651,6 @@ var ( AVMType: avmNone, } - // - // Higher level types - // - // StackBoolean constrains the int to 1 or 0, representing True or False StackBoolean = NewStackType(avmUint64, bound(0, 1), "bool") // StackHash represents output from a hash function or a field that returns a hash @@ -703,6 +680,14 @@ var ( } ) +func bound(min, max uint64) [2]uint64 { + return [2]uint64{min, max} +} + +func static(size uint64) [2]uint64 { + return bound(size, size) +} + // StackType describes the type of a value on the operand stack type StackType struct { Name string @@ -836,14 +821,6 @@ func (st StackTypes) strings() []string { return strs } -func bound(min, max uint64) [2]uint64 { - return [2]uint64{min, max} -} - -func static(size uint64) [2]uint64 { - return bound(size, size) -} - func parseStackTypes(spec string) StackTypes { if spec == "" { return nil @@ -863,7 +840,7 @@ func parseStackTypes(spec string) StackTypes { types[i] = StackAddress case 'N': types[i] = StackBigInt - case 'B': + case 'T': types[i] = StackBoolean case 'H': types[i] = StackHash diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index 5dd6f20d12..91d186198f 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -397,17 +397,17 @@ func (spec *OpSpec) deadens() bool { // assembly-time, with ops.returns() var OpSpecs = []OpSpec{ {0x00, "err", opErr, proto(":x"), 1, detDefault()}, - {0x01, "sha256", opSHA256, proto("b:b"), 1, costly(7)}, - {0x02, "keccak256", opKeccak256, proto("b:b"), 1, costly(26)}, - {0x03, "sha512_256", opSHA512_256, proto("b:b"), 1, costly(9)}, + {0x01, "sha256", opSHA256, proto("b:H"), 1, costly(7)}, + {0x02, "keccak256", opKeccak256, proto("b:H"), 1, costly(26)}, + {0x03, "sha512_256", opSHA512_256, proto("b:H"), 1, costly(9)}, // Cost of these opcodes increases in AVM version 2 based on measured // performance. Should be able to run max hashes during stateful TEAL // and achieve reasonable TPS. Same opcode for different versions // is OK. - {0x01, "sha256", opSHA256, proto("b:b"), 2, costly(35)}, - {0x02, "keccak256", opKeccak256, proto("b:b"), 2, costly(130)}, - {0x03, "sha512_256", opSHA512_256, proto("b:b"), 2, costly(45)}, + {0x01, "sha256", opSHA256, proto("b:H"), 2, costly(35)}, + {0x02, "keccak256", opKeccak256, proto("b:H"), 2, costly(130)}, + {0x03, "sha512_256", opSHA512_256, proto("b:H"), 2, costly(45)}, /* Tabling these changes until we offer unlimited global storage as there @@ -419,10 +419,10 @@ var OpSpecs = []OpSpec{ {0x03, "sha512_256", opSHA512_256, proto("b:b"), 7, unlimitedStorage, costByLength(17, 5, 8)}, */ - {0x04, "ed25519verify", opEd25519Verify, proto("bbb:i"), 1, costly(1900).only(ModeSig)}, - {0x04, "ed25519verify", opEd25519Verify, proto("bbb:i"), 5, costly(1900)}, + {0x04, "ed25519verify", opEd25519Verify, proto("bbb:T"), 1, costly(1900).only(ModeSig)}, + {0x04, "ed25519verify", opEd25519Verify, proto("bbb:T"), 5, costly(1900)}, - {0x05, "ecdsa_verify", opEcdsaVerify, proto("bbbbb:i"), 5, costByField("v", &EcdsaCurves, ecdsaVerifyCosts)}, + {0x05, "ecdsa_verify", opEcdsaVerify, proto("bbbbb:T"), 5, costByField("v", &EcdsaCurves, ecdsaVerifyCosts)}, {0x06, "ecdsa_pk_decompress", opEcdsaPkDecompress, proto("b:bb"), 5, costByField("v", &EcdsaCurves, ecdsaDecompressCosts)}, {0x07, "ecdsa_pk_recover", opEcdsaPkRecover, proto("bibb:bb"), 5, field("v", &EcdsaCurves).costs(2000)}, @@ -430,14 +430,14 @@ var OpSpecs = []OpSpec{ {0x09, "-", opMinus, proto("ii:i"), 1, detDefault()}, {0x0a, "/", opDiv, proto("ii:i"), 1, detDefault()}, {0x0b, "*", opMul, proto("ii:i"), 1, detDefault()}, - {0x0c, "<", opLt, proto("ii:i"), 1, detDefault()}, - {0x0d, ">", opGt, proto("ii:i"), 1, detDefault()}, - {0x0e, "<=", opLe, proto("ii:i"), 1, detDefault()}, - {0x0f, ">=", opGe, proto("ii:i"), 1, detDefault()}, - {0x10, "&&", opAnd, proto("ii:i"), 1, detDefault()}, - {0x11, "||", opOr, proto("ii:i"), 1, detDefault()}, - {0x12, "==", opEq, proto("aa:i"), 1, typed(typeEquals)}, - {0x13, "!=", opNeq, proto("aa:i"), 1, typed(typeEquals)}, + {0x0c, "<", opLt, proto("ii:T"), 1, detDefault()}, + {0x0d, ">", opGt, proto("ii:T"), 1, detDefault()}, + {0x0e, "<=", opLe, proto("ii:T"), 1, detDefault()}, + {0x0f, ">=", opGe, proto("ii:T"), 1, detDefault()}, + {0x10, "&&", opAnd, proto("ii:T"), 1, detDefault()}, + {0x11, "||", opOr, proto("ii:T"), 1, detDefault()}, + {0x12, "==", opEq, proto("aa:T"), 1, typed(typeEquals)}, + {0x13, "!=", opNeq, proto("aa:T"), 1, typed(typeEquals)}, {0x14, "!", opNot, proto("i:i"), 1, detDefault()}, {0x15, "len", opLen, proto("b:i"), 1, detDefault()}, {0x16, "itob", opItob, proto("i:b"), 1, detDefault()}, @@ -528,26 +528,26 @@ var OpSpecs = []OpSpec{ {0x60, "balance", opBalance, proto("i:i"), 2, only(ModeApp)}, {0x60, "balance", opBalance, proto("a:i"), directRefEnabledVersion, only(ModeApp)}, - {0x61, "app_opted_in", opAppOptedIn, proto("ii:i"), 2, only(ModeApp)}, - {0x61, "app_opted_in", opAppOptedIn, proto("ai:i"), directRefEnabledVersion, only(ModeApp)}, - {0x62, "app_local_get", opAppLocalGet, proto("ib:a"), 2, only(ModeApp)}, - {0x62, "app_local_get", opAppLocalGet, proto("ab:a"), directRefEnabledVersion, only(ModeApp)}, - {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iib:ai"), 2, only(ModeApp)}, - {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aib:ai"), directRefEnabledVersion, only(ModeApp)}, - {0x64, "app_global_get", opAppGlobalGet, proto("b:a"), 2, only(ModeApp)}, - {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("ib:ai"), 2, only(ModeApp)}, - {0x66, "app_local_put", opAppLocalPut, proto("iba:"), 2, only(ModeApp)}, - {0x66, "app_local_put", opAppLocalPut, proto("aba:"), directRefEnabledVersion, only(ModeApp)}, - {0x67, "app_global_put", opAppGlobalPut, proto("ba:"), 2, only(ModeApp)}, - {0x68, "app_local_del", opAppLocalDel, proto("ib:"), 2, only(ModeApp)}, - {0x68, "app_local_del", opAppLocalDel, proto("ab:"), directRefEnabledVersion, only(ModeApp)}, - {0x69, "app_global_del", opAppGlobalDel, proto("b:"), 2, only(ModeApp)}, - - {0x70, "asset_holding_get", opAssetHoldingGet, proto("ii:ai"), 2, field("f", &AssetHoldingFields).only(ModeApp)}, - {0x70, "asset_holding_get", opAssetHoldingGet, proto("ai:ai"), directRefEnabledVersion, field("f", &AssetHoldingFields).only(ModeApp)}, - {0x71, "asset_params_get", opAssetParamsGet, proto("i:ai"), 2, field("f", &AssetParamsFields).only(ModeApp)}, - {0x72, "app_params_get", opAppParamsGet, proto("i:ai"), 5, field("f", &AppParamsFields).only(ModeApp)}, - {0x73, "acct_params_get", opAcctParamsGet, proto("a:ai"), 6, field("f", &AcctParamsFields).only(ModeApp)}, + {0x61, "app_opted_in", opAppOptedIn, proto("ii:T"), 2, only(ModeApp)}, + {0x61, "app_opted_in", opAppOptedIn, proto("ai:T"), directRefEnabledVersion, only(ModeApp)}, + {0x62, "app_local_get", opAppLocalGet, proto("iK:a"), 2, only(ModeApp)}, + {0x62, "app_local_get", opAppLocalGet, proto("aK:a"), directRefEnabledVersion, only(ModeApp)}, + {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iiK:aT"), 2, only(ModeApp)}, + {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aiK:aT"), directRefEnabledVersion, only(ModeApp)}, + {0x64, "app_global_get", opAppGlobalGet, proto("K:a"), 2, only(ModeApp)}, + {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("iK:aT"), 2, only(ModeApp)}, + {0x66, "app_local_put", opAppLocalPut, proto("iKa:"), 2, only(ModeApp)}, + {0x66, "app_local_put", opAppLocalPut, proto("aKa:"), directRefEnabledVersion, only(ModeApp)}, + {0x67, "app_global_put", opAppGlobalPut, proto("Ka:"), 2, only(ModeApp)}, + {0x68, "app_local_del", opAppLocalDel, proto("iK:"), 2, only(ModeApp)}, + {0x68, "app_local_del", opAppLocalDel, proto("aK:"), directRefEnabledVersion, only(ModeApp)}, + {0x69, "app_global_del", opAppGlobalDel, proto("K:"), 2, only(ModeApp)}, + + {0x70, "asset_holding_get", opAssetHoldingGet, proto("ii:aT"), 2, field("f", &AssetHoldingFields).only(ModeApp)}, + {0x70, "asset_holding_get", opAssetHoldingGet, proto("ai:aT"), directRefEnabledVersion, field("f", &AssetHoldingFields).only(ModeApp)}, + {0x71, "asset_params_get", opAssetParamsGet, proto("i:aT"), 2, field("f", &AssetParamsFields).only(ModeApp)}, + {0x72, "app_params_get", opAppParamsGet, proto("i:aT"), 5, field("f", &AppParamsFields).only(ModeApp)}, + {0x73, "acct_params_get", opAcctParamsGet, proto("a:aT"), 6, field("f", &AcctParamsFields).only(ModeApp)}, {0x78, "min_balance", opMinBalance, proto("i:i"), 3, only(ModeApp)}, {0x78, "min_balance", opMinBalance, proto("a:i"), directRefEnabledVersion, only(ModeApp)}, @@ -558,7 +558,7 @@ var OpSpecs = []OpSpec{ {0x82, "pushbytess", opPushBytess, proto(":", "", "[N items]"), 8, constants(asmPushBytess, checkByteImmArgs, "bytes ...", immBytess).typed(typePushBytess).trust()}, {0x83, "pushints", opPushInts, proto(":", "", "[N items]"), 8, constants(asmPushInts, checkIntImmArgs, "uint ...", immInts).typed(typePushInts).trust()}, - {0x84, "ed25519verify_bare", opEd25519VerifyBare, proto("bbb:i"), 7, costly(1900)}, + {0x84, "ed25519verify_bare", opEd25519VerifyBare, proto("bbb:T"), 7, costly(1900)}, // "Function oriented" {0x88, "callsub", opCallSub, proto(":"), 4, detBranch()}, @@ -589,16 +589,16 @@ var OpSpecs = []OpSpec{ {0x9b, "bn256_pairing", opBn256Pairing, proto("bb:i"), pairingVersion, costly(8700)}, // Byteslice math. - {0xa0, "b+", opBytesPlus, proto("bb:b"), 4, costly(10)}, - {0xa1, "b-", opBytesMinus, proto("bb:b"), 4, costly(10)}, - {0xa2, "b/", opBytesDiv, proto("bb:b"), 4, costly(20)}, - {0xa3, "b*", opBytesMul, proto("bb:b"), 4, costly(20)}, - {0xa4, "b<", opBytesLt, proto("bb:i"), 4, detDefault()}, - {0xa5, "b>", opBytesGt, proto("bb:i"), 4, detDefault()}, - {0xa6, "b<=", opBytesLe, proto("bb:i"), 4, detDefault()}, - {0xa7, "b>=", opBytesGe, proto("bb:i"), 4, detDefault()}, - {0xa8, "b==", opBytesEq, proto("bb:i"), 4, detDefault()}, - {0xa9, "b!=", opBytesNeq, proto("bb:i"), 4, detDefault()}, + {0xa0, "b+", opBytesPlus, proto("NN:N"), 4, costly(10)}, + {0xa1, "b-", opBytesMinus, proto("NN:N"), 4, costly(10)}, + {0xa2, "b/", opBytesDiv, proto("NN:N"), 4, costly(20)}, + {0xa3, "b*", opBytesMul, proto("NN:N"), 4, costly(20)}, + {0xa4, "b<", opBytesLt, proto("NN:T"), 4, detDefault()}, + {0xa5, "b>", opBytesGt, proto("NN:T"), 4, detDefault()}, + {0xa6, "b<=", opBytesLe, proto("NN:T"), 4, detDefault()}, + {0xa7, "b>=", opBytesGe, proto("NN:T"), 4, detDefault()}, + {0xa8, "b==", opBytesEq, proto("NN:T"), 4, detDefault()}, + {0xa9, "b!=", opBytesNeq, proto("NN:T"), 4, detDefault()}, {0xaa, "b%", opBytesModulo, proto("bb:b"), 4, costly(20)}, {0xab, "b|", opBytesBitOr, proto("bb:b"), 4, costly(6)}, {0xac, "b&", opBytesBitAnd, proto("bb:b"), 4, costly(6)}, @@ -618,13 +618,13 @@ var OpSpecs = []OpSpec{ {0xb8, "gitxna", opGitxna, proto(":a"), 6, immediates("t", "f", "i").field("f", &TxnArrayFields).only(ModeApp)}, // Unlimited Global Storage - Boxes - {0xb9, "box_create", opBoxCreate, proto("bi:i"), boxVersion, only(ModeApp)}, - {0xba, "box_extract", opBoxExtract, proto("bii:b"), boxVersion, only(ModeApp)}, - {0xbb, "box_replace", opBoxReplace, proto("bib:"), boxVersion, only(ModeApp)}, - {0xbc, "box_del", opBoxDel, proto("b:i"), boxVersion, only(ModeApp)}, - {0xbd, "box_len", opBoxLen, proto("b:ii"), boxVersion, only(ModeApp)}, - {0xbe, "box_get", opBoxGet, proto("b:bi"), boxVersion, only(ModeApp)}, - {0xbf, "box_put", opBoxPut, proto("bb:"), boxVersion, only(ModeApp)}, + {0xb9, "box_create", opBoxCreate, proto("Ki:T"), boxVersion, only(ModeApp)}, + {0xba, "box_extract", opBoxExtract, proto("Kii:b"), boxVersion, only(ModeApp)}, + {0xbb, "box_replace", opBoxReplace, proto("Kib:"), boxVersion, only(ModeApp)}, + {0xbc, "box_del", opBoxDel, proto("K:T"), boxVersion, only(ModeApp)}, + {0xbd, "box_len", opBoxLen, proto("K:iT"), boxVersion, only(ModeApp)}, + {0xbe, "box_get", opBoxGet, proto("K:bT"), boxVersion, only(ModeApp)}, + {0xbf, "box_put", opBoxPut, proto("Kb:"), boxVersion, only(ModeApp)}, // Dynamic indexing {0xc0, "txnas", opTxnas, proto("i:a"), 5, field("f", &TxnArrayFields)}, @@ -636,7 +636,7 @@ var OpSpecs = []OpSpec{ {0xc6, "gitxnas", opGitxnas, proto("i:a"), 6, immediates("t", "f").field("f", &TxnArrayFields).only(ModeApp)}, // randomness support - {0xd0, "vrf_verify", opVrfVerify, proto("bbb:bi"), randomnessVersion, field("s", &VrfStandards).costs(5700)}, + {0xd0, "vrf_verify", opVrfVerify, proto("bbb:bT"), randomnessVersion, field("s", &VrfStandards).costs(5700)}, {0xd1, "block", opBlock, proto("i:a"), randomnessVersion, field("f", &BlockFields)}, } From d6513c242a6fa3867587553cb2977c5c862039ad Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 10 Feb 2023 10:31:45 -0500 Subject: [PATCH 03/41] more specific stack type for Fields --- data/transactions/logic/assembler_test.go | 2 +- data/transactions/logic/fields.go | 74 +++++++++++------------ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 665741e96e..64512be95e 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2655,7 +2655,7 @@ func TestTxTypes(t *testing.T) { partitiontest.PartitionTest(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 []byte got uint64"}) + 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; itxn_field Amount", 5, Expect{1, "itxn_field Amount expects 1 stack argument..."}) diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index 0af2079a35..bbe3b4149d 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -267,18 +267,18 @@ func (fs txnFieldSpec) Note() string { } var txnFieldSpecs = [...]txnFieldSpec{ - {Sender, StackBytes, false, 0, 5, false, "32 byte address"}, + {Sender, StackAddress, false, 0, 5, false, "32 byte address"}, {Fee, StackUint64, false, 0, 5, false, "microalgos"}, {FirstValid, StackUint64, false, 0, 0, false, "round number"}, {FirstValidTime, StackUint64, false, randomnessVersion, 0, false, "UNIX timestamp of block before txn.FirstValid. Fails if negative"}, {LastValid, StackUint64, false, 0, 0, false, "round number"}, {Note, StackBytes, false, 0, 6, false, "Any data up to 1024 bytes"}, - {Lease, StackBytes, false, 0, 0, false, "32 byte lease value"}, - {Receiver, StackBytes, false, 0, 5, false, "32 byte address"}, + {Lease, StackHash, false, 0, 0, false, "32 byte lease value"}, + {Receiver, StackAddress, false, 0, 5, false, "32 byte address"}, {Amount, StackUint64, false, 0, 5, false, "microalgos"}, - {CloseRemainderTo, StackBytes, false, 0, 5, false, "32 byte address"}, - {VotePK, StackBytes, false, 0, 6, false, "32 byte address"}, - {SelectionPK, StackBytes, false, 0, 6, false, "32 byte address"}, + {CloseRemainderTo, StackAddress, false, 0, 5, false, "32 byte address"}, + {VotePK, StackAddress, false, 0, 6, false, "32 byte address"}, + {SelectionPK, StackAddress, false, 0, 6, false, "32 byte address"}, {VoteFirst, StackUint64, false, 0, 6, false, "The first round that the participation key is valid."}, {VoteLast, StackUint64, false, 0, 6, false, "The last round that the participation key is valid."}, {VoteKeyDilution, StackUint64, false, 0, 6, false, "Dilution for the 2-level participation key"}, @@ -286,42 +286,42 @@ var txnFieldSpecs = [...]txnFieldSpec{ {TypeEnum, StackUint64, false, 0, 5, false, "Transaction type as integer"}, {XferAsset, StackUint64, false, 0, 5, false, "Asset ID"}, {AssetAmount, StackUint64, false, 0, 5, false, "value in Asset's units"}, - {AssetSender, StackBytes, false, 0, 5, false, + {AssetSender, StackAddress, false, 0, 5, false, "32 byte address. Source of assets if Sender is the Asset's Clawback address."}, - {AssetReceiver, StackBytes, false, 0, 5, false, "32 byte address"}, - {AssetCloseTo, StackBytes, false, 0, 5, false, "32 byte address"}, + {AssetReceiver, StackAddress, false, 0, 5, false, "32 byte address"}, + {AssetCloseTo, StackAddress, false, 0, 5, false, "32 byte address"}, {GroupIndex, StackUint64, false, 0, 0, false, "Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1"}, - {TxID, StackBytes, false, 0, 0, false, "The computed ID for this transaction. 32 bytes."}, + {TxID, StackHash, false, 0, 0, false, "The computed ID for this transaction. 32 bytes."}, {ApplicationID, StackUint64, false, 2, 6, false, "ApplicationID from ApplicationCall transaction"}, {OnCompletion, StackUint64, false, 2, 6, false, "ApplicationCall transaction on completion action"}, {ApplicationArgs, StackBytes, true, 2, 6, false, "Arguments passed to the application in the ApplicationCall transaction"}, {NumAppArgs, StackUint64, false, 2, 0, false, "Number of ApplicationArgs"}, - {Accounts, StackBytes, true, 2, 6, false, "Accounts listed in the ApplicationCall transaction"}, + {Accounts, StackAddress, true, 2, 6, false, "Accounts listed in the ApplicationCall transaction"}, {NumAccounts, StackUint64, false, 2, 0, false, "Number of Accounts"}, {ApprovalProgram, StackBytes, false, 2, 6, false, "Approval program"}, {ClearStateProgram, StackBytes, false, 2, 6, false, "Clear state program"}, - {RekeyTo, StackBytes, false, 2, 6, false, "32 byte Sender's new AuthAddr"}, + {RekeyTo, StackAddress, false, 2, 6, false, "32 byte Sender's new AuthAddr"}, {ConfigAsset, StackUint64, false, 2, 5, false, "Asset ID in asset config transaction"}, {ConfigAssetTotal, StackUint64, false, 2, 5, false, "Total number of units of this asset created"}, {ConfigAssetDecimals, StackUint64, false, 2, 5, false, "Number of digits to display after the decimal place when displaying the asset"}, - {ConfigAssetDefaultFrozen, StackUint64, false, 2, 5, false, + {ConfigAssetDefaultFrozen, StackBoolean, false, 2, 5, false, "Whether the asset's slots are frozen by default or not, 0 or 1"}, {ConfigAssetUnitName, StackBytes, false, 2, 5, false, "Unit name of the asset"}, {ConfigAssetName, StackBytes, false, 2, 5, false, "The asset name"}, {ConfigAssetURL, StackBytes, false, 2, 5, false, "URL"}, - {ConfigAssetMetadataHash, StackBytes, false, 2, 5, false, + {ConfigAssetMetadataHash, StackHash, false, 2, 5, false, "32 byte commitment to unspecified asset metadata"}, - {ConfigAssetManager, StackBytes, false, 2, 5, false, "32 byte address"}, - {ConfigAssetReserve, StackBytes, false, 2, 5, false, "32 byte address"}, - {ConfigAssetFreeze, StackBytes, false, 2, 5, false, "32 byte address"}, - {ConfigAssetClawback, StackBytes, false, 2, 5, false, "32 byte address"}, + {ConfigAssetManager, StackAddress, false, 2, 5, false, "32 byte address"}, + {ConfigAssetReserve, StackAddress, false, 2, 5, false, "32 byte address"}, + {ConfigAssetFreeze, StackAddress, false, 2, 5, false, "32 byte address"}, + {ConfigAssetClawback, StackAddress, false, 2, 5, false, "32 byte address"}, {FreezeAsset, StackUint64, false, 2, 5, false, "Asset ID being frozen or un-frozen"}, - {FreezeAssetAccount, StackBytes, false, 2, 5, false, + {FreezeAssetAccount, StackAddress, false, 2, 5, false, "32 byte address of the account whose asset slot is being frozen or un-frozen"}, - {FreezeAssetFrozen, StackUint64, false, 2, 5, false, "The new frozen value, 0 or 1"}, + {FreezeAssetFrozen, StackBoolean, false, 2, 5, false, "The new frozen value, 0 or 1"}, {Assets, StackUint64, true, 3, 6, false, "Foreign Assets listed in the ApplicationCall transaction"}, {NumAssets, StackUint64, false, 3, 0, false, "Number of Assets"}, {Applications, StackUint64, true, 3, 6, false, "Foreign Apps listed in the ApplicationCall transaction"}, @@ -332,7 +332,7 @@ var txnFieldSpecs = [...]txnFieldSpec{ {LocalNumByteSlice, StackUint64, false, 3, 6, false, "Number of local state byteslices in ApplicationCall"}, {ExtraProgramPages, StackUint64, false, 4, 6, false, "Number of additional pages for each of the application's approval and clear state programs. An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program."}, - {Nonparticipation, StackUint64, false, 5, 6, false, "Marks an account nonparticipating for rewards"}, + {Nonparticipation, StackBoolean, false, 5, 6, false, "Marks an account nonparticipating for rewards"}, // "Effects" Last two things are always going to: 0, true {Logs, StackBytes, true, 5, 0, true, "Log messages emitted by an application call (only with `itxn` in v5)"}, @@ -568,7 +568,7 @@ var globalFieldSpecs = [...]globalFieldSpec{ {MinTxnFee, StackUint64, modeAny, 0, "microalgos"}, {MinBalance, StackUint64, modeAny, 0, "microalgos"}, {MaxTxnLife, StackUint64, modeAny, 0, "rounds"}, - {ZeroAddress, StackBytes, modeAny, 0, "32 byte address of all zero bytes"}, + {ZeroAddress, StackAddress, modeAny, 0, "32 byte address of all zero bytes"}, {GroupSize, StackUint64, modeAny, 0, "Number of transactions in this atomic transaction group. At least 1"}, {LogicSigVersion, StackUint64, modeAny, 2, "Maximum supported version"}, @@ -576,17 +576,17 @@ var globalFieldSpecs = [...]globalFieldSpec{ {LatestTimestamp, StackUint64, ModeApp, 2, "Last confirmed block UNIX timestamp. Fails if negative"}, {CurrentApplicationID, StackUint64, ModeApp, 2, "ID of current application executing"}, - {CreatorAddress, StackBytes, ModeApp, 3, + {CreatorAddress, StackAddress, ModeApp, 3, "Address of the creator of the current application"}, - {CurrentApplicationAddress, StackBytes, ModeApp, 5, + {CurrentApplicationAddress, StackAddress, ModeApp, 5, "Address that the current application controls"}, - {GroupID, StackBytes, modeAny, 5, + {GroupID, StackHash, modeAny, 5, "ID of the transaction group. 32 zero bytes if the transaction is not part of a group."}, {OpcodeBudget, StackUint64, modeAny, 6, "The remaining cost that can be spent by opcodes in this program."}, {CallerApplicationID, StackUint64, ModeApp, 6, "The application ID of the application that called this application. 0 if this application is at the top-level."}, - {CallerApplicationAddress, StackBytes, ModeApp, 6, + {CallerApplicationAddress, StackAddress, ModeApp, 6, "The application address of the application that called this application. ZeroAddress if this application is at the top-level."}, } @@ -983,7 +983,7 @@ func (fs assetHoldingFieldSpec) Note() string { var assetHoldingFieldSpecs = [...]assetHoldingFieldSpec{ {AssetBalance, StackUint64, 2, "Amount of the asset unit held by this account"}, - {AssetFrozen, StackUint64, 2, "Is the asset frozen or not"}, + {AssetFrozen, StackBoolean, 2, "Is the asset frozen or not"}, } func assetHoldingFieldSpecByField(f AssetHoldingField) (assetHoldingFieldSpec, bool) { @@ -1070,16 +1070,16 @@ func (fs assetParamsFieldSpec) Note() string { var assetParamsFieldSpecs = [...]assetParamsFieldSpec{ {AssetTotal, StackUint64, 2, "Total number of units of this asset"}, {AssetDecimals, StackUint64, 2, "See AssetParams.Decimals"}, - {AssetDefaultFrozen, StackUint64, 2, "Frozen by default or not"}, + {AssetDefaultFrozen, StackBoolean, 2, "Frozen by default or not"}, {AssetUnitName, StackBytes, 2, "Asset unit name"}, {AssetName, StackBytes, 2, "Asset name"}, {AssetURL, StackBytes, 2, "URL with additional info about the asset"}, - {AssetMetadataHash, StackBytes, 2, "Arbitrary commitment"}, - {AssetManager, StackBytes, 2, "Manager address"}, - {AssetReserve, StackBytes, 2, "Reserve address"}, - {AssetFreeze, StackBytes, 2, "Freeze address"}, - {AssetClawback, StackBytes, 2, "Clawback address"}, - {AssetCreator, StackBytes, 5, "Creator address"}, + {AssetMetadataHash, StackHash, 2, "Arbitrary commitment"}, + {AssetManager, StackAddress, 2, "Manager address"}, + {AssetReserve, StackAddress, 2, "Reserve address"}, + {AssetFreeze, StackAddress, 2, "Freeze address"}, + {AssetClawback, StackAddress, 2, "Clawback address"}, + {AssetCreator, StackAddress, 5, "Creator address"}, } func assetParamsFieldSpecByField(f AssetParamsField) (assetParamsFieldSpec, bool) { @@ -1166,8 +1166,8 @@ var appParamsFieldSpecs = [...]appParamsFieldSpec{ {AppLocalNumUint, StackUint64, 5, "Number of uint64 values allowed in Local State"}, {AppLocalNumByteSlice, StackUint64, 5, "Number of byte array values allowed in Local State"}, {AppExtraProgramPages, StackUint64, 5, "Number of Extra Program Pages of code space"}, - {AppCreator, StackBytes, 5, "Creator address"}, - {AppAddress, StackBytes, 5, "Address for which this application has authority"}, + {AppCreator, StackAddress, 5, "Creator address"}, + {AppAddress, StackAddress, 5, "Address for which this application has authority"}, } func appParamsFieldSpecByField(f AppParamsField) (appParamsFieldSpec, bool) { @@ -1259,7 +1259,7 @@ func (fs acctParamsFieldSpec) Note() string { var acctParamsFieldSpecs = [...]acctParamsFieldSpec{ {AcctBalance, StackUint64, 6, "Account balance in microalgos"}, {AcctMinBalance, StackUint64, 6, "Minimum required balance for account, in microalgos"}, - {AcctAuthAddr, StackBytes, 6, "Address the account is rekeyed to."}, + {AcctAuthAddr, StackAddress, 6, "Address the account is rekeyed to."}, {AcctTotalNumUint, StackUint64, 8, "The total number of uint64 values allocated by this account in Global and Local States."}, {AcctTotalNumByteSlice, StackUint64, 8, "The total number of byte array values allocated by this account in Global and Local States."}, From ee56f940a6f3eb6a97c6e998c7c4946e6d7609e6 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 10 Feb 2023 11:19:54 -0500 Subject: [PATCH 04/41] make opdoc handle the new StackType and provide array of types in Args/Returns, fix some tests to use avm type for comparison --- cmd/opdoc/opdoc.go | 45 +- data/transactions/logic/README.md | 74 +- data/transactions/logic/TEAL_opcodes.md | 164 +- data/transactions/logic/evalStateful_test.go | 10 +- data/transactions/logic/fields_test.go | 6 +- data/transactions/logic/langspec.json | 1785 +++++++++++++++--- data/transactions/logic/opcodes.go | 2 +- 7 files changed, 1635 insertions(+), 451 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index a7f1894018..2fa9a7b2eb 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -238,12 +238,12 @@ func opsToMarkdown(out io.Writer) (err error) { type OpRecord struct { Opcode byte Name string - Args string `json:",omitempty"` - Returns string `json:",omitempty"` + Args []string `json:",omitempty"` + Returns []string `json:",omitempty"` Size int ArgEnum []string `json:",omitempty"` - ArgEnumTypes string `json:",omitempty"` + ArgEnumTypes []string `json:",omitempty"` Doc string DocExtra string `json:",omitempty"` @@ -259,35 +259,16 @@ type LanguageSpec struct { Ops []OpRecord } -func typeString(types []logic.StackType) string { - out := make([]byte, len(types)) +func typeStrings(types []logic.StackType) []string { + out := make([]string, len(types)) for i, t := range types { - switch t { - case logic.StackUint64: - out[i] = 'U' - case logic.StackBytes: - out[i] = 'B' - case logic.StackAny: - out[i] = '.' - case logic.StackNone: - out[i] = '_' - default: - panic("unexpected type in opdoc typeString") - } - } - - // Cant return None and !None from same op - if strings.Contains(string(out), "_") { - if strings.ContainsAny(string(out), "UB.") { - panic("unexpected StackNone in opdoc typeString") - } - return "" + out[i] = t.String() } - return string(out) + return out } -func fieldsAndTypes(group logic.FieldGroup) ([]string, string) { +func fieldsAndTypes(group logic.FieldGroup) ([]string, []string) { // reminder: group.Names can be "sparse" See: logic.TxnaFields fields := make([]string, 0, len(group.Names)) types := make([]logic.StackType, 0, len(group.Names)) @@ -297,10 +278,10 @@ func fieldsAndTypes(group logic.FieldGroup) ([]string, string) { types = append(types, spec.Type()) } } - return fields, typeString(types) + return fields, typeStrings(types) } -func argEnums(name string) ([]string, string) { +func argEnums(name string) ([]string, []string) { // reminder: this needs to be manually updated every time // a new opcode is added with an associated FieldGroup // it'd be nice to have this auto-update @@ -334,7 +315,7 @@ func argEnums(name string) ([]string, string) { case "ecdsa_pk_recover", "ecdsa_verify", "ecdsa_pk_decompress": return fieldsAndTypes(logic.EcdsaCurves) default: - return nil, "" + return nil, nil } } @@ -344,8 +325,8 @@ func buildLanguageSpec(opGroups map[string][]string) *LanguageSpec { for i, spec := range opSpecs { records[i].Opcode = spec.Opcode records[i].Name = spec.Name - records[i].Args = typeString(spec.Arg.Types) - records[i].Returns = typeString(spec.Return.Types) + records[i].Args = typeStrings(spec.Arg.Types) + records[i].Returns = typeStrings(spec.Return.Types) records[i].Size = spec.OpDetails.Size records[i].ArgEnum, records[i].ArgEnumTypes = argEnums(spec.Name) records[i].Doc = strings.ReplaceAll(logic.OpDoc(spec.Name), "
", "\n") diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index 324cd7d536..03a1fddc18 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -444,18 +444,18 @@ Some of these have immediate data in the byte or bytes after the opcode. ##### Scalar Fields | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | -| 0 | Sender | []byte | | 32 byte address | +| 0 | Sender | addr | | 32 byte address | | 1 | Fee | uint64 | | microalgos | | 2 | FirstValid | uint64 | | round number | | 3 | FirstValidTime | uint64 | v7 | UNIX timestamp of block before txn.FirstValid. Fails if negative | | 4 | LastValid | uint64 | | round number | | 5 | Note | []byte | | Any data up to 1024 bytes | -| 6 | Lease | []byte | | 32 byte lease value | -| 7 | Receiver | []byte | | 32 byte address | +| 6 | Lease | hash | | 32 byte lease value | +| 7 | Receiver | addr | | 32 byte address | | 8 | Amount | uint64 | | microalgos | -| 9 | CloseRemainderTo | []byte | | 32 byte address | -| 10 | VotePK | []byte | | 32 byte address | -| 11 | SelectionPK | []byte | | 32 byte address | +| 9 | CloseRemainderTo | addr | | 32 byte address | +| 10 | VotePK | addr | | 32 byte address | +| 11 | SelectionPK | addr | | 32 byte address | | 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | | 13 | VoteLast | uint64 | | The last round that the participation key is valid. | | 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | @@ -463,33 +463,33 @@ Some of these have immediate data in the byte or bytes after the opcode. | 16 | TypeEnum | uint64 | | Transaction type as integer | | 17 | XferAsset | uint64 | | Asset ID | | 18 | AssetAmount | uint64 | | value in Asset's units | -| 19 | AssetSender | []byte | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | -| 20 | AssetReceiver | []byte | | 32 byte address | -| 21 | AssetCloseTo | []byte | | 32 byte address | +| 19 | AssetSender | addr | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | addr | | 32 byte address | +| 21 | AssetCloseTo | addr | | 32 byte address | | 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | -| 23 | TxID | []byte | | The computed ID for this transaction. 32 bytes. | +| 23 | TxID | hash | | The computed ID for this transaction. 32 bytes. | | 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | | 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | | 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | | 29 | NumAccounts | uint64 | v2 | Number of Accounts | | 30 | ApprovalProgram | []byte | v2 | Approval program | | 31 | ClearStateProgram | []byte | v2 | Clear state program | -| 32 | RekeyTo | []byte | v2 | 32 byte Sender's new AuthAddr | +| 32 | RekeyTo | addr | v2 | 32 byte Sender's new AuthAddr | | 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | | 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | | 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | -| 36 | ConfigAssetDefaultFrozen | uint64 | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | | 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | | 38 | ConfigAssetName | []byte | v2 | The asset name | | 39 | ConfigAssetURL | []byte | v2 | URL | -| 40 | ConfigAssetMetadataHash | []byte | v2 | 32 byte commitment to unspecified asset metadata | -| 41 | ConfigAssetManager | []byte | v2 | 32 byte address | -| 42 | ConfigAssetReserve | []byte | v2 | 32 byte address | -| 43 | ConfigAssetFreeze | []byte | v2 | 32 byte address | -| 44 | ConfigAssetClawback | []byte | v2 | 32 byte address | +| 40 | ConfigAssetMetadataHash | hash | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | addr | v2 | 32 byte address | +| 42 | ConfigAssetReserve | addr | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | addr | v2 | 32 byte address | +| 44 | ConfigAssetClawback | addr | v2 | 32 byte address | | 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | -| 46 | FreezeAssetAccount | []byte | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | -| 47 | FreezeAssetFrozen | uint64 | v2 | The new frozen value, 0 or 1 | +| 46 | FreezeAssetAccount | addr | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | | 49 | NumAssets | uint64 | v3 | Number of Assets | | 51 | NumApplications | uint64 | v3 | Number of Applications | | 52 | GlobalNumUint | uint64 | v3 | Number of global state integers in ApplicationCall | @@ -497,7 +497,7 @@ Some of these have immediate data in the byte or bytes after the opcode. | 54 | LocalNumUint | uint64 | v3 | Number of local state integers in ApplicationCall | | 55 | LocalNumByteSlice | uint64 | v3 | Number of local state byteslices in ApplicationCall | | 56 | ExtraProgramPages | uint64 | v4 | Number of additional pages for each of the application's approval and clear state programs. An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program. | -| 57 | Nonparticipation | uint64 | v5 | Marks an account nonparticipating for rewards | +| 57 | Nonparticipation | bool | v5 | Marks an account nonparticipating for rewards | | 59 | NumLogs | uint64 | v5 | Number of Logs (only with `itxn` in v5). Application mode only | | 60 | CreatedAssetID | uint64 | v5 | Asset ID allocated by the creation of an ASA (only with `itxn` in v5). Application mode only | | 61 | CreatedApplicationID | uint64 | v5 | ApplicationID allocated by the creation of an application (only with `itxn` in v5). Application mode only | @@ -510,7 +510,7 @@ Some of these have immediate data in the byte or bytes after the opcode. | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | | 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | -| 28 | Accounts | []byte | v2 | Accounts listed in the ApplicationCall transaction | +| 28 | Accounts | addr | v2 | Accounts listed in the ApplicationCall transaction | | 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | | 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | | 58 | Logs | []byte | v5 | Log messages emitted by an application call (only with `itxn` in v5). Application mode only | @@ -529,18 +529,18 @@ Global fields are fields that are common to all the transactions in the group. I | 0 | MinTxnFee | uint64 | | microalgos | | 1 | MinBalance | uint64 | | microalgos | | 2 | MaxTxnLife | uint64 | | rounds | -| 3 | ZeroAddress | []byte | | 32 byte address of all zero bytes | +| 3 | ZeroAddress | addr | | 32 byte address of all zero bytes | | 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | | 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | | 6 | Round | uint64 | v2 | Current round number. Application mode only. | | 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | | 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | -| 9 | CreatorAddress | []byte | v3 | Address of the creator of the current application. Application mode only. | -| 10 | CurrentApplicationAddress | []byte | v5 | Address that the current application controls. Application mode only. | -| 11 | GroupID | []byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | +| 9 | CreatorAddress | addr | v3 | Address of the creator of the current application. Application mode only. | +| 10 | CurrentApplicationAddress | addr | v5 | Address that the current application controls. Application mode only. | +| 11 | GroupID | hash | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | | 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | | 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | -| 14 | CallerApplicationAddress | []byte | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | +| 14 | CallerApplicationAddress | addr | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | **Asset Fields** @@ -550,23 +550,23 @@ Asset fields include `AssetHolding` and `AssetParam` fields that are used in the | Index | Name | Type | Notes | | - | ------ | -- | --------- | | 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | -| 1 | AssetFrozen | uint64 | Is the asset frozen or not | +| 1 | AssetFrozen | bool | Is the asset frozen or not | | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | | 0 | AssetTotal | uint64 | | Total number of units of this asset | | 1 | AssetDecimals | uint64 | | See AssetParams.Decimals | -| 2 | AssetDefaultFrozen | uint64 | | Frozen by default or not | +| 2 | AssetDefaultFrozen | bool | | Frozen by default or not | | 3 | AssetUnitName | []byte | | Asset unit name | | 4 | AssetName | []byte | | Asset name | | 5 | AssetURL | []byte | | URL with additional info about the asset | -| 6 | AssetMetadataHash | []byte | | Arbitrary commitment | -| 7 | AssetManager | []byte | | Manager address | -| 8 | AssetReserve | []byte | | Reserve address | -| 9 | AssetFreeze | []byte | | Freeze address | -| 10 | AssetClawback | []byte | | Clawback address | -| 11 | AssetCreator | []byte | v5 | Creator address | +| 6 | AssetMetadataHash | hash | | Arbitrary commitment | +| 7 | AssetManager | addr | | Manager address | +| 8 | AssetReserve | addr | | Reserve address | +| 9 | AssetFreeze | addr | | Freeze address | +| 10 | AssetClawback | addr | | Clawback address | +| 11 | AssetCreator | addr | v5 | Creator address | **App Fields** @@ -582,8 +582,8 @@ App fields used in the `app_params_get` opcode. | 4 | AppLocalNumUint | uint64 | Number of uint64 values allowed in Local State | | 5 | AppLocalNumByteSlice | uint64 | Number of byte array values allowed in Local State | | 6 | AppExtraProgramPages | uint64 | Number of Extra Program Pages of code space | -| 7 | AppCreator | []byte | Creator address | -| 8 | AppAddress | []byte | Address for which this application has authority | +| 7 | AppCreator | addr | Creator address | +| 8 | AppAddress | addr | Address for which this application has authority | **Account Fields** @@ -594,7 +594,7 @@ Account fields used in the `acct_params_get` opcode. | - | ------ | -- | - | --------- | | 0 | AcctBalance | uint64 | | Account balance in microalgos | | 1 | AcctMinBalance | uint64 | | Minimum required balance for account, in microalgos | -| 2 | AcctAuthAddr | []byte | | Address the account is rekeyed to. | +| 2 | AcctAuthAddr | addr | | Address the account is rekeyed to. | | 3 | AcctTotalNumUint | uint64 | v8 | The total number of uint64 values allocated by this account in Global and Local States. | | 4 | AcctTotalNumByteSlice | uint64 | v8 | The total number of byte array values allocated by this account in Global and Local States. | | 5 | AcctTotalExtraAppPages | uint64 | v8 | The number of extra app code pages used by this account. | diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 54a80db8f9..f1124e4626 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -12,7 +12,7 @@ Ops have a 'cost' of 1 unless otherwise specified. ## sha256 - Opcode: 0x01 -- Stack: ..., A: []byte → ..., []byte +- Stack: ..., A: []byte → ..., hash - SHA256 hash of value A, yields [32]byte - **Cost**: - 7 (v1) @@ -21,7 +21,7 @@ Ops have a 'cost' of 1 unless otherwise specified. ## keccak256 - Opcode: 0x02 -- Stack: ..., A: []byte → ..., []byte +- Stack: ..., A: []byte → ..., hash - Keccak256 hash of value A, yields [32]byte - **Cost**: - 26 (v1) @@ -30,7 +30,7 @@ Ops have a 'cost' of 1 unless otherwise specified. ## sha512_256 - Opcode: 0x03 -- Stack: ..., A: []byte → ..., []byte +- Stack: ..., A: []byte → ..., hash - SHA512_256 hash of value A, yields [32]byte - **Cost**: - 9 (v1) @@ -39,7 +39,7 @@ Ops have a 'cost' of 1 unless otherwise specified. ## ed25519verify - Opcode: 0x04 -- Stack: ..., A: []byte, B: []byte, C: []byte → ..., uint64 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool - for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} - **Cost**: 1900 @@ -48,7 +48,7 @@ The 32 byte public key is the last element on the stack, preceded by the 64 byte ## ecdsa_verify v - Opcode: 0x05 {uint8 curve index} -- Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte → ..., uint64 +- Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte → ..., bool - for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} - **Cost**: Secp256k1=1700 Secp256r1=2500 - Availability: v5 @@ -116,49 +116,49 @@ Overflow is an error condition which halts execution and fails the transaction. ## < - Opcode: 0x0c -- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- Stack: ..., A: uint64, B: uint64 → ..., bool - A less than B => {0 or 1} ## > - Opcode: 0x0d -- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- Stack: ..., A: uint64, B: uint64 → ..., bool - A greater than B => {0 or 1} ## <= - Opcode: 0x0e -- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- Stack: ..., A: uint64, B: uint64 → ..., bool - A less than or equal to B => {0 or 1} ## >= - Opcode: 0x0f -- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- Stack: ..., A: uint64, B: uint64 → ..., bool - A greater than or equal to B => {0 or 1} ## && - Opcode: 0x10 -- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- Stack: ..., A: uint64, B: uint64 → ..., bool - A is not zero and B is not zero => {0 or 1} ## || - Opcode: 0x11 -- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- Stack: ..., A: uint64, B: uint64 → ..., bool - A is not zero or B is not zero => {0 or 1} ## == - Opcode: 0x12 -- Stack: ..., A, B → ..., uint64 +- Stack: ..., A, B → ..., bool - A is equal to B => {0 or 1} ## != - Opcode: 0x13 -- Stack: ..., A, B → ..., uint64 +- Stack: ..., A, B → ..., bool - A is not equal to B => {0 or 1} ## ! @@ -361,18 +361,18 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | -| 0 | Sender | []byte | | 32 byte address | +| 0 | Sender | addr | | 32 byte address | | 1 | Fee | uint64 | | microalgos | | 2 | FirstValid | uint64 | | round number | | 3 | FirstValidTime | uint64 | v7 | UNIX timestamp of block before txn.FirstValid. Fails if negative | | 4 | LastValid | uint64 | | round number | | 5 | Note | []byte | | Any data up to 1024 bytes | -| 6 | Lease | []byte | | 32 byte lease value | -| 7 | Receiver | []byte | | 32 byte address | +| 6 | Lease | hash | | 32 byte lease value | +| 7 | Receiver | addr | | 32 byte address | | 8 | Amount | uint64 | | microalgos | -| 9 | CloseRemainderTo | []byte | | 32 byte address | -| 10 | VotePK | []byte | | 32 byte address | -| 11 | SelectionPK | []byte | | 32 byte address | +| 9 | CloseRemainderTo | addr | | 32 byte address | +| 10 | VotePK | addr | | 32 byte address | +| 11 | SelectionPK | addr | | 32 byte address | | 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | | 13 | VoteLast | uint64 | | The last round that the participation key is valid. | | 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | @@ -380,33 +380,33 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 16 | TypeEnum | uint64 | | Transaction type as integer | | 17 | XferAsset | uint64 | | Asset ID | | 18 | AssetAmount | uint64 | | value in Asset's units | -| 19 | AssetSender | []byte | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | -| 20 | AssetReceiver | []byte | | 32 byte address | -| 21 | AssetCloseTo | []byte | | 32 byte address | +| 19 | AssetSender | addr | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | addr | | 32 byte address | +| 21 | AssetCloseTo | addr | | 32 byte address | | 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | -| 23 | TxID | []byte | | The computed ID for this transaction. 32 bytes. | +| 23 | TxID | hash | | The computed ID for this transaction. 32 bytes. | | 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | | 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | | 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | | 29 | NumAccounts | uint64 | v2 | Number of Accounts | | 30 | ApprovalProgram | []byte | v2 | Approval program | | 31 | ClearStateProgram | []byte | v2 | Clear state program | -| 32 | RekeyTo | []byte | v2 | 32 byte Sender's new AuthAddr | +| 32 | RekeyTo | addr | v2 | 32 byte Sender's new AuthAddr | | 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | | 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | | 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | -| 36 | ConfigAssetDefaultFrozen | uint64 | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | | 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | | 38 | ConfigAssetName | []byte | v2 | The asset name | | 39 | ConfigAssetURL | []byte | v2 | URL | -| 40 | ConfigAssetMetadataHash | []byte | v2 | 32 byte commitment to unspecified asset metadata | -| 41 | ConfigAssetManager | []byte | v2 | 32 byte address | -| 42 | ConfigAssetReserve | []byte | v2 | 32 byte address | -| 43 | ConfigAssetFreeze | []byte | v2 | 32 byte address | -| 44 | ConfigAssetClawback | []byte | v2 | 32 byte address | +| 40 | ConfigAssetMetadataHash | hash | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | addr | v2 | 32 byte address | +| 42 | ConfigAssetReserve | addr | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | addr | v2 | 32 byte address | +| 44 | ConfigAssetClawback | addr | v2 | 32 byte address | | 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | -| 46 | FreezeAssetAccount | []byte | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | -| 47 | FreezeAssetFrozen | uint64 | v2 | The new frozen value, 0 or 1 | +| 46 | FreezeAssetAccount | addr | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | | 49 | NumAssets | uint64 | v3 | Number of Assets | | 51 | NumApplications | uint64 | v3 | Number of Applications | | 52 | GlobalNumUint | uint64 | v3 | Number of global state integers in ApplicationCall | @@ -414,7 +414,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 54 | LocalNumUint | uint64 | v3 | Number of local state integers in ApplicationCall | | 55 | LocalNumByteSlice | uint64 | v3 | Number of local state byteslices in ApplicationCall | | 56 | ExtraProgramPages | uint64 | v4 | Number of additional pages for each of the application's approval and clear state programs. An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program. | -| 57 | Nonparticipation | uint64 | v5 | Marks an account nonparticipating for rewards | +| 57 | Nonparticipation | bool | v5 | Marks an account nonparticipating for rewards | | 59 | NumLogs | uint64 | v5 | Number of Logs (only with `itxn` in v5). Application mode only | | 60 | CreatedAssetID | uint64 | v5 | Asset ID allocated by the creation of an ASA (only with `itxn` in v5). Application mode only | | 61 | CreatedApplicationID | uint64 | v5 | ApplicationID allocated by the creation of an application (only with `itxn` in v5). Application mode only | @@ -437,18 +437,18 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 0 | MinTxnFee | uint64 | | microalgos | | 1 | MinBalance | uint64 | | microalgos | | 2 | MaxTxnLife | uint64 | | rounds | -| 3 | ZeroAddress | []byte | | 32 byte address of all zero bytes | +| 3 | ZeroAddress | addr | | 32 byte address of all zero bytes | | 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | | 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | | 6 | Round | uint64 | v2 | Current round number. Application mode only. | | 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | | 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | -| 9 | CreatorAddress | []byte | v3 | Address of the creator of the current application. Application mode only. | -| 10 | CurrentApplicationAddress | []byte | v5 | Address that the current application controls. Application mode only. | -| 11 | GroupID | []byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | +| 9 | CreatorAddress | addr | v3 | Address of the creator of the current application. Application mode only. | +| 10 | CurrentApplicationAddress | addr | v5 | Address that the current application controls. Application mode only. | +| 11 | GroupID | hash | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | | 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | | 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | -| 14 | CallerApplicationAddress | []byte | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | +| 14 | CallerApplicationAddress | addr | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | ## gtxn t f @@ -483,7 +483,7 @@ for notes on transaction fields available, see `txn`. If this transaction is _i_ | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | | 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | -| 28 | Accounts | []byte | v2 | Accounts listed in the ApplicationCall transaction | +| 28 | Accounts | addr | v2 | Accounts listed in the ApplicationCall transaction | | 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | | 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | | 58 | Logs | []byte | v5 | Log messages emitted by an application call (only with `itxn` in v5). Application mode only | @@ -843,7 +843,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), _ava ## app_opted_in - Opcode: 0x61 -- Stack: ..., A, B: uint64 → ..., uint64 +- Stack: ..., A, B: uint64 → ..., bool - 1 if account A is opted in to application B, else 0 - Availability: v2 - Mode: Application @@ -853,7 +853,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), _ava ## app_local_get - Opcode: 0x62 -- Stack: ..., A, B: []byte → ..., any +- Stack: ..., A, B: key → ..., any - local state of the key B in the current application in account A - Availability: v2 - Mode: Application @@ -863,7 +863,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_local_get_ex - Opcode: 0x63 -- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: uint64 +- Stack: ..., A, B: uint64, C: key → ..., X: any, Y: bool - X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 - Availability: v2 - Mode: Application @@ -873,7 +873,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), _ava ## app_global_get - Opcode: 0x64 -- Stack: ..., A: []byte → ..., any +- Stack: ..., A: key → ..., any - global state of the key A in the current application - Availability: v2 - Mode: Application @@ -883,7 +883,7 @@ params: state key. Return: value. The value is zero (of type uint64) if the key ## app_global_get_ex - Opcode: 0x65 -- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: uint64 +- Stack: ..., A: uint64, B: key → ..., X: any, Y: bool - X is the global state of application A, key B. Y is 1 if key existed, else 0 - Availability: v2 - Mode: Application @@ -893,7 +893,7 @@ params: Txn.ForeignApps offset (or, since v4, an _available_ application id), st ## app_local_put - Opcode: 0x66 -- Stack: ..., A, B: []byte, C → ... +- Stack: ..., A, B: key, C → ... - write C to key B in account A's local state of the current application - Availability: v2 - Mode: Application @@ -903,7 +903,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_global_put - Opcode: 0x67 -- Stack: ..., A: []byte, B → ... +- Stack: ..., A: key, B → ... - write B to key A in the global state of the current application - Availability: v2 - Mode: Application @@ -911,7 +911,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_local_del - Opcode: 0x68 -- Stack: ..., A, B: []byte → ... +- Stack: ..., A, B: key → ... - delete key B from account A's local state of the current application - Availability: v2 - Mode: Application @@ -923,7 +923,7 @@ Deleting a key which is already absent has no effect on the application local st ## app_global_del - Opcode: 0x69 -- Stack: ..., A: []byte → ... +- Stack: ..., A: key → ... - delete key A from the global state of the current application - Availability: v2 - Mode: Application @@ -935,7 +935,7 @@ Deleting a key which is already absent has no effect on the application global s ## asset_holding_get f - Opcode: 0x70 {uint8 asset holding field index} -- Stack: ..., A, B: uint64 → ..., X: any, Y: uint64 +- Stack: ..., A, B: uint64 → ..., X: any, Y: bool - X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 - Availability: v2 - Mode: Application @@ -945,7 +945,7 @@ Deleting a key which is already absent has no effect on the application global s | Index | Name | Type | Notes | | - | ------ | -- | --------- | | 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | -| 1 | AssetFrozen | uint64 | Is the asset frozen or not | +| 1 | AssetFrozen | bool | Is the asset frozen or not | params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. @@ -953,7 +953,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or ## asset_params_get f - Opcode: 0x71 {uint8 asset params field index} -- Stack: ..., A: uint64 → ..., X: any, Y: uint64 +- Stack: ..., A: uint64 → ..., X: any, Y: bool - X is field F from asset A. Y is 1 if A exists, else 0 - Availability: v2 - Mode: Application @@ -964,16 +964,16 @@ params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or | - | ------ | -- | - | --------- | | 0 | AssetTotal | uint64 | | Total number of units of this asset | | 1 | AssetDecimals | uint64 | | See AssetParams.Decimals | -| 2 | AssetDefaultFrozen | uint64 | | Frozen by default or not | +| 2 | AssetDefaultFrozen | bool | | Frozen by default or not | | 3 | AssetUnitName | []byte | | Asset unit name | | 4 | AssetName | []byte | | Asset name | | 5 | AssetURL | []byte | | URL with additional info about the asset | -| 6 | AssetMetadataHash | []byte | | Arbitrary commitment | -| 7 | AssetManager | []byte | | Manager address | -| 8 | AssetReserve | []byte | | Reserve address | -| 9 | AssetFreeze | []byte | | Freeze address | -| 10 | AssetClawback | []byte | | Clawback address | -| 11 | AssetCreator | []byte | v5 | Creator address | +| 6 | AssetMetadataHash | hash | | Arbitrary commitment | +| 7 | AssetManager | addr | | Manager address | +| 8 | AssetReserve | addr | | Reserve address | +| 9 | AssetFreeze | addr | | Freeze address | +| 10 | AssetClawback | addr | | Clawback address | +| 11 | AssetCreator | addr | v5 | Creator address | params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. @@ -981,7 +981,7 @@ params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: ## app_params_get f - Opcode: 0x72 {uint8 app params field index} -- Stack: ..., A: uint64 → ..., X: any, Y: uint64 +- Stack: ..., A: uint64 → ..., X: any, Y: bool - X is field F from app A. Y is 1 if A exists, else 0 - Availability: v5 - Mode: Application @@ -997,8 +997,8 @@ params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: | 4 | AppLocalNumUint | uint64 | Number of uint64 values allowed in Local State | | 5 | AppLocalNumByteSlice | uint64 | Number of byte array values allowed in Local State | | 6 | AppExtraProgramPages | uint64 | Number of Extra Program Pages of code space | -| 7 | AppCreator | []byte | Creator address | -| 8 | AppAddress | []byte | Address for which this application has authority | +| 7 | AppCreator | addr | Creator address | +| 8 | AppAddress | addr | Address for which this application has authority | params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value. @@ -1006,7 +1006,7 @@ params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag ## acct_params_get f - Opcode: 0x73 {uint8 account params field index} -- Stack: ..., A → ..., X: any, Y: uint64 +- Stack: ..., A → ..., X: any, Y: bool - X is field F from account A. Y is 1 if A owns positive algos, else 0 - Availability: v6 - Mode: Application @@ -1017,7 +1017,7 @@ params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag | - | ------ | -- | - | --------- | | 0 | AcctBalance | uint64 | | Account balance in microalgos | | 1 | AcctMinBalance | uint64 | | Minimum required balance for account, in microalgos | -| 2 | AcctAuthAddr | []byte | | Address the account is rekeyed to. | +| 2 | AcctAuthAddr | addr | | Address the account is rekeyed to. | | 3 | AcctTotalNumUint | uint64 | v8 | The total number of uint64 values allocated by this account in Global and Local States. | | 4 | AcctTotalNumByteSlice | uint64 | v8 | The total number of byte array values allocated by this account in Global and Local States. | | 5 | AcctTotalExtraAppPages | uint64 | v8 | The number of extra app code pages used by this account. | @@ -1078,7 +1078,7 @@ pushints args are not added to the intcblock during assembly processes ## ed25519verify_bare - Opcode: 0x84 -- Stack: ..., A: []byte, B: []byte, C: []byte → ..., uint64 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool - for (data A, signature B, pubkey C) verify the signature of the data against the pubkey => {0 or 1} - **Cost**: 1900 - Availability: v7 @@ -1214,7 +1214,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b+ - Opcode: 0xa0 -- Stack: ..., A: []byte, B: []byte → ..., []byte +- Stack: ..., A: bigint, B: bigint → ..., bigint - A plus B. A and B are interpreted as big-endian unsigned integers - **Cost**: 10 - Availability: v4 @@ -1222,7 +1222,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b- - Opcode: 0xa1 -- Stack: ..., A: []byte, B: []byte → ..., []byte +- Stack: ..., A: bigint, B: bigint → ..., bigint - A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow. - **Cost**: 10 - Availability: v4 @@ -1230,7 +1230,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b/ - Opcode: 0xa2 -- Stack: ..., A: []byte, B: []byte → ..., []byte +- Stack: ..., A: bigint, B: bigint → ..., bigint - A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero. - **Cost**: 20 - Availability: v4 @@ -1238,7 +1238,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b* - Opcode: 0xa3 -- Stack: ..., A: []byte, B: []byte → ..., []byte +- Stack: ..., A: bigint, B: bigint → ..., bigint - A times B. A and B are interpreted as big-endian unsigned integers. - **Cost**: 20 - Availability: v4 @@ -1246,42 +1246,42 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b< - Opcode: 0xa4 -- Stack: ..., A: []byte, B: []byte → ..., uint64 +- Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b> - Opcode: 0xa5 -- Stack: ..., A: []byte, B: []byte → ..., uint64 +- Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b<= - Opcode: 0xa6 -- Stack: ..., A: []byte, B: []byte → ..., uint64 +- Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b>= - Opcode: 0xa7 -- Stack: ..., A: []byte, B: []byte → ..., uint64 +- Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b== - Opcode: 0xa8 -- Stack: ..., A: []byte, B: []byte → ..., uint64 +- Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b!= - Opcode: 0xa9 -- Stack: ..., A: []byte, B: []byte → ..., uint64 +- Stack: ..., A: bigint, B: bigint → ..., bool - 0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers - Availability: v4 @@ -1417,7 +1417,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## box_create - Opcode: 0xb9 -- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- Stack: ..., A: key, B: uint64 → ..., bool - create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1 - Availability: v8 - Mode: Application @@ -1427,7 +1427,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_extract - Opcode: 0xba -- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- Stack: ..., A: key, B: uint64, C: uint64 → ..., []byte - read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. - Availability: v8 - Mode: Application @@ -1435,7 +1435,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_replace - Opcode: 0xbb -- Stack: ..., A: []byte, B: uint64, C: []byte → ... +- Stack: ..., A: key, B: uint64, C: []byte → ... - write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. - Availability: v8 - Mode: Application @@ -1443,7 +1443,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_del - Opcode: 0xbc -- Stack: ..., A: []byte → ..., uint64 +- Stack: ..., A: key → ..., bool - delete box named A if it exists. Return 1 if A existed, 0 otherwise - Availability: v8 - Mode: Application @@ -1451,7 +1451,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_len - Opcode: 0xbd -- Stack: ..., A: []byte → ..., X: uint64, Y: uint64 +- Stack: ..., A: key → ..., X: uint64, Y: bool - X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0. - Availability: v8 - Mode: Application @@ -1459,7 +1459,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_get - Opcode: 0xbe -- Stack: ..., A: []byte → ..., X: []byte, Y: uint64 +- Stack: ..., A: key → ..., X: []byte, Y: bool - X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0. - Availability: v8 - Mode: Application @@ -1469,7 +1469,7 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo ## box_put - Opcode: 0xbf -- Stack: ..., A: []byte, B: []byte → ... +- Stack: ..., A: key, B: []byte → ... - replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist - Availability: v8 - Mode: Application @@ -1532,7 +1532,7 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo ## vrf_verify s - Opcode: 0xd0 {uint8 parameters index} -- Stack: ..., A: []byte, B: []byte, C: []byte → ..., X: []byte, Y: uint64 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., X: []byte, Y: bool - Verify the proof B of message A against pubkey C. Returns vrf output and verification flag. - **Cost**: 5700 - Availability: v7 diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index a640ec9c28..638cd8e358 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -2342,10 +2342,10 @@ func TestReturnTypes(t *testing.T) { t.Parallel() // Ensure all opcodes return values they are supposed to according to the OpSpecs table - typeToArg := map[StackType]string{ - StackUint64: "int 1\n", - StackAny: "int 1\n", - StackBytes: "byte 0x33343536\n", // Which is the string "3456" + typeToArg := map[avmType]string{ + avmUint64: "int 1\n", + avmAny: "int 1\n", + avmBytes: "byte 0x33343536\n", // Which is the string "3456" } // We try to form a snippet that will test every opcode, by sandwiching it @@ -2491,7 +2491,7 @@ func TestReturnTypes(t *testing.T) { var sb strings.Builder if provideStackInput { for _, t := range spec.Arg.Types { - sb.WriteString(typeToArg[t]) + sb.WriteString(typeToArg[t.AVMType]) } } sb.WriteString(cmd + "\n") diff --git a/data/transactions/logic/fields_test.go b/data/transactions/logic/fields_test.go index cbe425d5f4..fa59b9b0c5 100644 --- a/data/transactions/logic/fields_test.go +++ b/data/transactions/logic/fields_test.go @@ -220,10 +220,10 @@ func TestAssetParamsFieldsVersions(t *testing.T) { // Need to use intc so we can "backversion" the // program and not have it fail because of pushint. text := fmt.Sprintf("intcblock 0 1; intc_0; asset_params_get %s; bnz ok; err; ok: ", field.field.String()) - switch field.ftype { - case StackUint64: // ensure the return type is uint64 by adding + switch field.ftype.AVMType { + case avmUint64: // ensure the return type is uint64 by adding text += " intc_1; +" - case StackBytes: // ensure the return type is bytes by using len + case avmBytes: // ensure the return type is bytes by using len text += " len" // also happens to ensure that we get non empty - the params fields are fixed width } // check assembler fails if version before introduction diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index cbfb4b0d95..7d20a868ef 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -5,6 +5,9 @@ { "Opcode": 0, "Name": "err", + "Returns": [ + "none" + ], "Size": 1, "Doc": "Fail immediately.", "IntroducedVersion": 1, @@ -15,8 +18,12 @@ { "Opcode": 1, "Name": "sha256", - "Args": "B", - "Returns": "B", + "Args": [ + "[]byte" + ], + "Returns": [ + "hash" + ], "Size": 1, "Doc": "SHA256 hash of value A, yields [32]byte", "IntroducedVersion": 1, @@ -27,8 +34,12 @@ { "Opcode": 2, "Name": "keccak256", - "Args": "B", - "Returns": "B", + "Args": [ + "[]byte" + ], + "Returns": [ + "hash" + ], "Size": 1, "Doc": "Keccak256 hash of value A, yields [32]byte", "IntroducedVersion": 1, @@ -39,8 +50,12 @@ { "Opcode": 3, "Name": "sha512_256", - "Args": "B", - "Returns": "B", + "Args": [ + "[]byte" + ], + "Returns": [ + "hash" + ], "Size": 1, "Doc": "SHA512_256 hash of value A, yields [32]byte", "IntroducedVersion": 1, @@ -51,8 +66,14 @@ { "Opcode": 4, "Name": "ed25519verify", - "Args": "BBB", - "Returns": "U", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", @@ -64,13 +85,25 @@ { "Opcode": 5, "Name": "ecdsa_verify", - "Args": "BBBBB", - "Returns": "U", + "Args": [ + "[]byte", + "[]byte", + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], "Size": 2, "ArgEnum": [ "Secp256k1", "Secp256r1" ], + "ArgEnumTypes": [ + "none", + "none" + ], "Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}", "DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", "ImmediateNote": "{uint8 curve index}", @@ -82,13 +115,22 @@ { "Opcode": 6, "Name": "ecdsa_pk_decompress", - "Args": "B", - "Returns": "BB", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], "Size": 2, "ArgEnum": [ "Secp256k1", "Secp256r1" ], + "ArgEnumTypes": [ + "none", + "none" + ], "Doc": "decompress pubkey A into components X, Y", "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", "ImmediateNote": "{uint8 curve index}", @@ -100,13 +142,25 @@ { "Opcode": 7, "Name": "ecdsa_pk_recover", - "Args": "BUBB", - "Returns": "BB", + "Args": [ + "[]byte", + "uint64", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], "Size": 2, "ArgEnum": [ "Secp256k1", "Secp256r1" ], + "ArgEnumTypes": [ + "none", + "none" + ], "Doc": "for (data A, recovery id B, signature C, D) recover a public key", "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", "ImmediateNote": "{uint8 curve index}", @@ -118,8 +172,13 @@ { "Opcode": 8, "Name": "+", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A plus B. Fail on overflow.", "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", @@ -131,8 +190,13 @@ { "Opcode": 9, "Name": "-", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A minus B. Fail if B \u003e A.", "IntroducedVersion": 1, @@ -143,8 +207,13 @@ { "Opcode": 10, "Name": "/", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A divided by B (truncated division). Fail if B == 0.", "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", @@ -156,8 +225,13 @@ { "Opcode": 11, "Name": "*", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A times B. Fail on overflow.", "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", @@ -169,8 +243,13 @@ { "Opcode": 12, "Name": "\u003c", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "A less than B =\u003e {0 or 1}", "IntroducedVersion": 1, @@ -181,8 +260,13 @@ { "Opcode": 13, "Name": "\u003e", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "A greater than B =\u003e {0 or 1}", "IntroducedVersion": 1, @@ -193,8 +277,13 @@ { "Opcode": 14, "Name": "\u003c=", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "A less than or equal to B =\u003e {0 or 1}", "IntroducedVersion": 1, @@ -205,8 +294,13 @@ { "Opcode": 15, "Name": "\u003e=", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "A greater than or equal to B =\u003e {0 or 1}", "IntroducedVersion": 1, @@ -217,8 +311,13 @@ { "Opcode": 16, "Name": "\u0026\u0026", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", "IntroducedVersion": 1, @@ -229,8 +328,13 @@ { "Opcode": 17, "Name": "||", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", "IntroducedVersion": 1, @@ -241,8 +345,13 @@ { "Opcode": 18, "Name": "==", - "Args": "..", - "Returns": "U", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "A is equal to B =\u003e {0 or 1}", "IntroducedVersion": 1, @@ -253,8 +362,13 @@ { "Opcode": 19, "Name": "!=", - "Args": "..", - "Returns": "U", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "A is not equal to B =\u003e {0 or 1}", "IntroducedVersion": 1, @@ -265,8 +379,12 @@ { "Opcode": 20, "Name": "!", - "Args": "U", - "Returns": "U", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A == 0 yields 1; else 0", "IntroducedVersion": 1, @@ -277,8 +395,12 @@ { "Opcode": 21, "Name": "len", - "Args": "B", - "Returns": "U", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "yields length of byte value A", "IntroducedVersion": 1, @@ -289,8 +411,12 @@ { "Opcode": 22, "Name": "itob", - "Args": "U", - "Returns": "B", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "converts uint64 A to big-endian byte array, always of length 8", "IntroducedVersion": 1, @@ -301,8 +427,12 @@ { "Opcode": 23, "Name": "btoi", - "Args": "B", - "Returns": "U", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", @@ -314,8 +444,13 @@ { "Opcode": 24, "Name": "%", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A modulo B. Fail if B == 0.", "IntroducedVersion": 1, @@ -326,8 +461,13 @@ { "Opcode": 25, "Name": "|", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A bitwise-or B", "IntroducedVersion": 1, @@ -338,8 +478,13 @@ { "Opcode": 26, "Name": "\u0026", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A bitwise-and B", "IntroducedVersion": 1, @@ -350,8 +495,13 @@ { "Opcode": 27, "Name": "^", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A bitwise-xor B", "IntroducedVersion": 1, @@ -362,8 +512,12 @@ { "Opcode": 28, "Name": "~", - "Args": "U", - "Returns": "U", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "bitwise invert value A", "IntroducedVersion": 1, @@ -374,8 +528,14 @@ { "Opcode": 29, "Name": "mulw", - "Args": "UU", - "Returns": "UU", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], "Size": 1, "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", "IntroducedVersion": 1, @@ -386,8 +546,14 @@ { "Opcode": 30, "Name": "addw", - "Args": "UU", - "Returns": "UU", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], "Size": 1, "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", "IntroducedVersion": 2, @@ -398,8 +564,18 @@ { "Opcode": 31, "Name": "divmodw", - "Args": "UUUU", - "Returns": "UUUU", + "Args": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], "Size": 1, "Doc": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", "DocExtra": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", @@ -423,7 +599,9 @@ { "Opcode": 33, "Name": "intc", - "Returns": "U", + "Returns": [ + "uint64" + ], "Size": 2, "Doc": "Ith constant from intcblock", "ImmediateNote": "{uint8 int constant index}", @@ -435,7 +613,9 @@ { "Opcode": 34, "Name": "intc_0", - "Returns": "U", + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "constant 0 from intcblock", "IntroducedVersion": 1, @@ -446,7 +626,9 @@ { "Opcode": 35, "Name": "intc_1", - "Returns": "U", + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "constant 1 from intcblock", "IntroducedVersion": 1, @@ -457,7 +639,9 @@ { "Opcode": 36, "Name": "intc_2", - "Returns": "U", + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "constant 2 from intcblock", "IntroducedVersion": 1, @@ -468,7 +652,9 @@ { "Opcode": 37, "Name": "intc_3", - "Returns": "U", + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "constant 3 from intcblock", "IntroducedVersion": 1, @@ -491,7 +677,9 @@ { "Opcode": 39, "Name": "bytec", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 2, "Doc": "Ith constant from bytecblock", "ImmediateNote": "{uint8 byte constant index}", @@ -503,7 +691,9 @@ { "Opcode": 40, "Name": "bytec_0", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "constant 0 from bytecblock", "IntroducedVersion": 1, @@ -514,7 +704,9 @@ { "Opcode": 41, "Name": "bytec_1", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "constant 1 from bytecblock", "IntroducedVersion": 1, @@ -525,7 +717,9 @@ { "Opcode": 42, "Name": "bytec_2", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "constant 2 from bytecblock", "IntroducedVersion": 1, @@ -536,7 +730,9 @@ { "Opcode": 43, "Name": "bytec_3", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "constant 3 from bytecblock", "IntroducedVersion": 1, @@ -547,7 +743,9 @@ { "Opcode": 44, "Name": "arg", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 2, "Doc": "Nth LogicSig argument", "ImmediateNote": "{uint8 arg index}", @@ -559,7 +757,9 @@ { "Opcode": 45, "Name": "arg_0", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "LogicSig argument 0", "IntroducedVersion": 1, @@ -570,7 +770,9 @@ { "Opcode": 46, "Name": "arg_1", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "LogicSig argument 1", "IntroducedVersion": 1, @@ -581,7 +783,9 @@ { "Opcode": 47, "Name": "arg_2", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "LogicSig argument 2", "IntroducedVersion": 1, @@ -592,7 +796,9 @@ { "Opcode": 48, "Name": "arg_3", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "LogicSig argument 3", "IntroducedVersion": 1, @@ -603,7 +809,9 @@ { "Opcode": 49, "Name": "txn", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 2, "ArgEnum": [ "Sender", @@ -675,7 +883,76 @@ "ClearStateProgramPages", "NumClearStateProgramPages" ], - "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBBBUBU", + "ArgEnumTypes": [ + "addr", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "hash", + "addr", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "hash", + "uint64", + "uint64", + "[]byte", + "uint64", + "addr", + "uint64", + "[]byte", + "[]byte", + "addr", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "hash", + "addr", + "addr", + "addr", + "addr", + "uint64", + "addr", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], "Doc": "field F of current transaction", "ImmediateNote": "{uint8 transaction field index}", "IntroducedVersion": 1, @@ -686,7 +963,9 @@ { "Opcode": 50, "Name": "global", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 2, "ArgEnum": [ "MinTxnFee", @@ -705,7 +984,23 @@ "CallerApplicationID", "CallerApplicationAddress" ], - "ArgEnumTypes": "UUUBUUUUUBBBUUB", + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "addr", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "addr", + "addr", + "hash", + "uint64", + "uint64", + "addr" + ], "Doc": "global field F", "ImmediateNote": "{uint8 global field index}", "IntroducedVersion": 1, @@ -716,7 +1011,9 @@ { "Opcode": 51, "Name": "gtxn", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 3, "ArgEnum": [ "Sender", @@ -788,7 +1085,76 @@ "ClearStateProgramPages", "NumClearStateProgramPages" ], - "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBBBUBU", + "ArgEnumTypes": [ + "addr", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "hash", + "addr", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "hash", + "uint64", + "uint64", + "[]byte", + "uint64", + "addr", + "uint64", + "[]byte", + "[]byte", + "addr", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "hash", + "addr", + "addr", + "addr", + "addr", + "uint64", + "addr", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], "Doc": "field F of the Tth transaction in the current group", "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}", @@ -800,7 +1166,9 @@ { "Opcode": 52, "Name": "load", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 2, "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", "ImmediateNote": "{uint8 position in scratch space to load from}", @@ -812,7 +1180,9 @@ { "Opcode": 53, "Name": "store", - "Args": ".", + "Args": [ + "any" + ], "Size": 2, "Doc": "store A to the Ith scratch space", "ImmediateNote": "{uint8 position in scratch space to store to}", @@ -824,7 +1194,9 @@ { "Opcode": 54, "Name": "txna", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 3, "ArgEnum": [ "ApplicationArgs", @@ -835,7 +1207,15 @@ "ApprovalProgramPages", "ClearStateProgramPages" ], - "ArgEnumTypes": "BBUUBBB", + "ArgEnumTypes": [ + "[]byte", + "addr", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", "ImmediateNote": "{uint8 transaction field index} {uint8 transaction field array index}", "IntroducedVersion": 2, @@ -846,7 +1226,9 @@ { "Opcode": 55, "Name": "gtxna", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 4, "ArgEnum": [ "ApplicationArgs", @@ -857,7 +1239,15 @@ "ApprovalProgramPages", "ClearStateProgramPages" ], - "ArgEnumTypes": "BBUUBBB", + "ArgEnumTypes": [ + "[]byte", + "addr", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index}", "IntroducedVersion": 2, @@ -868,8 +1258,12 @@ { "Opcode": 56, "Name": "gtxns", - "Args": "U", - "Returns": ".", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], "Size": 2, "ArgEnum": [ "Sender", @@ -941,7 +1335,76 @@ "ClearStateProgramPages", "NumClearStateProgramPages" ], - "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBBBUBU", + "ArgEnumTypes": [ + "addr", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "hash", + "addr", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "hash", + "uint64", + "uint64", + "[]byte", + "uint64", + "addr", + "uint64", + "[]byte", + "[]byte", + "addr", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "hash", + "addr", + "addr", + "addr", + "addr", + "uint64", + "addr", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], "Doc": "field F of the Ath transaction in the current group", "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", "ImmediateNote": "{uint8 transaction field index}", @@ -953,8 +1416,12 @@ { "Opcode": 57, "Name": "gtxnsa", - "Args": "U", - "Returns": ".", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], "Size": 3, "ArgEnum": [ "ApplicationArgs", @@ -965,7 +1432,15 @@ "ApprovalProgramPages", "ClearStateProgramPages" ], - "ArgEnumTypes": "BBUUBBB", + "ArgEnumTypes": [ + "[]byte", + "addr", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", "ImmediateNote": "{uint8 transaction field index} {uint8 transaction field array index}", "IntroducedVersion": 3, @@ -976,7 +1451,9 @@ { "Opcode": 58, "Name": "gload", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 3, "Doc": "Ith scratch space value of the Tth transaction in the current group", "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.", @@ -989,8 +1466,12 @@ { "Opcode": 59, "Name": "gloads", - "Args": "U", - "Returns": ".", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], "Size": 2, "Doc": "Ith scratch space value of the Ath transaction in the current group", "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.", @@ -1003,7 +1484,9 @@ { "Opcode": 60, "Name": "gaid", - "Returns": "U", + "Returns": [ + "uint64" + ], "Size": 2, "Doc": "ID of the asset or application created in the Tth transaction of the current group", "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.", @@ -1016,8 +1499,12 @@ { "Opcode": 61, "Name": "gaids", - "Args": "U", - "Returns": "U", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "ID of the asset or application created in the Ath transaction of the current group", "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and A \u003c GroupIndex.", @@ -1029,8 +1516,12 @@ { "Opcode": 62, "Name": "loads", - "Args": "U", - "Returns": ".", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], "Size": 1, "Doc": "Ath scratch space value. All scratch spaces are 0 at program start.", "IntroducedVersion": 5, @@ -1041,7 +1532,10 @@ { "Opcode": 63, "Name": "stores", - "Args": "U.", + "Args": [ + "uint64", + "any" + ], "Size": 1, "Doc": "store B to the Ath scratch space", "IntroducedVersion": 5, @@ -1052,7 +1546,9 @@ { "Opcode": 64, "Name": "bnz", - "Args": "U", + "Args": [ + "uint64" + ], "Size": 3, "Doc": "branch to TARGET if value A is not zero", "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", @@ -1065,7 +1561,9 @@ { "Opcode": 65, "Name": "bz", - "Args": "U", + "Args": [ + "uint64" + ], "Size": 3, "Doc": "branch to TARGET if value A is zero", "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", @@ -1090,7 +1588,12 @@ { "Opcode": 67, "Name": "return", - "Args": "U", + "Args": [ + "uint64" + ], + "Returns": [ + "none" + ], "Size": 1, "Doc": "use A as success value; end", "IntroducedVersion": 2, @@ -1101,7 +1604,9 @@ { "Opcode": 68, "Name": "assert", - "Args": "U", + "Args": [ + "uint64" + ], "Size": 1, "Doc": "immediately fail unless A is a non-zero number", "IntroducedVersion": 3, @@ -1112,7 +1617,9 @@ { "Opcode": 69, "Name": "bury", - "Args": ".", + "Args": [ + "any" + ], "Size": 2, "Doc": "replace the Nth value from the top of the stack with A. bury 0 fails.", "ImmediateNote": "{uint8 depth}", @@ -1135,7 +1642,9 @@ { "Opcode": 71, "Name": "dupn", - "Args": ".", + "Args": [ + "any" + ], "Size": 2, "Doc": "duplicate A, N times", "ImmediateNote": "{uint8 copy count}", @@ -1147,7 +1656,9 @@ { "Opcode": 72, "Name": "pop", - "Args": ".", + "Args": [ + "any" + ], "Size": 1, "Doc": "discard A", "IntroducedVersion": 1, @@ -1158,8 +1669,13 @@ { "Opcode": 73, "Name": "dup", - "Args": ".", - "Returns": "..", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], "Size": 1, "Doc": "duplicate A", "IntroducedVersion": 1, @@ -1170,8 +1686,16 @@ { "Opcode": 74, "Name": "dup2", - "Args": "..", - "Returns": "....", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any", + "any", + "any" + ], "Size": 1, "Doc": "duplicate A and B", "IntroducedVersion": 2, @@ -1182,8 +1706,13 @@ { "Opcode": 75, "Name": "dig", - "Args": ".", - "Returns": "..", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], "Size": 2, "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", "ImmediateNote": "{uint8 depth}", @@ -1195,8 +1724,14 @@ { "Opcode": 76, "Name": "swap", - "Args": "..", - "Returns": "..", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any" + ], "Size": 1, "Doc": "swaps A and B on stack", "IntroducedVersion": 3, @@ -1207,8 +1742,14 @@ { "Opcode": 77, "Name": "select", - "Args": "..U", - "Returns": ".", + "Args": [ + "any", + "any", + "uint64" + ], + "Returns": [ + "any" + ], "Size": 1, "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A", "IntroducedVersion": 3, @@ -1219,8 +1760,12 @@ { "Opcode": 78, "Name": "cover", - "Args": ".", - "Returns": ".", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], "Size": 2, "Doc": "remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth \u003c= N.", "ImmediateNote": "{uint8 depth}", @@ -1232,8 +1777,12 @@ { "Opcode": 79, "Name": "uncover", - "Args": ".", - "Returns": ".", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], "Size": 2, "Doc": "remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth \u003c= N.", "ImmediateNote": "{uint8 depth}", @@ -1245,8 +1794,13 @@ { "Opcode": 80, "Name": "concat", - "Args": "BB", - "Returns": "B", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "join A and B", "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", @@ -1258,8 +1812,12 @@ { "Opcode": 81, "Name": "substring", - "Args": "B", - "Returns": "B", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 3, "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", "ImmediateNote": "{uint8 start position} {uint8 end position}", @@ -1271,8 +1829,14 @@ { "Opcode": 82, "Name": "substring3", - "Args": "BUU", - "Returns": "B", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", "IntroducedVersion": 2, @@ -1283,8 +1847,13 @@ { "Opcode": 83, "Name": "getbit", - "Args": ".U", - "Returns": "U", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", "DocExtra": "see explanation of bit ordering in setbit", @@ -1296,8 +1865,14 @@ { "Opcode": 84, "Name": "setbit", - "Args": ".UU", - "Returns": ".", + "Args": [ + "any", + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], "Size": 1, "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", @@ -1309,8 +1884,13 @@ { "Opcode": 85, "Name": "getbyte", - "Args": "BU", - "Returns": "U", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", "IntroducedVersion": 3, @@ -1321,8 +1901,14 @@ { "Opcode": 86, "Name": "setbyte", - "Args": "BUU", - "Returns": "B", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", "IntroducedVersion": 3, @@ -1333,8 +1919,12 @@ { "Opcode": 87, "Name": "extract", - "Args": "B", - "Returns": "B", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 3, "Doc": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", "ImmediateNote": "{uint8 start position} {uint8 length}", @@ -1346,8 +1936,14 @@ { "Opcode": 88, "Name": "extract3", - "Args": "BUU", - "Returns": "B", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails\n`extract3` can be called using `extract` with no immediates.", "IntroducedVersion": 5, @@ -1358,8 +1954,13 @@ { "Opcode": 89, "Name": "extract_uint16", - "Args": "BU", - "Returns": "U", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails", "IntroducedVersion": 5, @@ -1370,8 +1971,13 @@ { "Opcode": 90, "Name": "extract_uint32", - "Args": "BU", - "Returns": "U", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails", "IntroducedVersion": 5, @@ -1382,8 +1988,13 @@ { "Opcode": 91, "Name": "extract_uint64", - "Args": "BU", - "Returns": "U", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails", "IntroducedVersion": 5, @@ -1394,8 +2005,13 @@ { "Opcode": 92, "Name": "replace2", - "Args": "BB", - "Returns": "B", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 2, "Doc": "Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)\n`replace2` can be called using `replace` with 1 immediate.", "ImmediateNote": "{uint8 start position}", @@ -1407,8 +2023,14 @@ { "Opcode": 93, "Name": "replace3", - "Args": "BUB", - "Returns": "B", + "Args": [ + "[]byte", + "uint64", + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)\n`replace3` can be called using `replace` with no immediates.", "IntroducedVersion": 7, @@ -1419,14 +2041,21 @@ { "Opcode": 94, "Name": "base64_decode", - "Args": "B", - "Returns": "B", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 2, "ArgEnum": [ "URLEncoding", "StdEncoding" ], - "ArgEnumTypes": "..", + "ArgEnumTypes": [ + "any", + "any" + ], "Doc": "decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E", "DocExtra": "*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings.\tThis opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings.\n\n Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.", "ImmediateNote": "{uint8 encoding index}", @@ -1438,15 +2067,24 @@ { "Opcode": 95, "Name": "json_ref", - "Args": "BB", - "Returns": ".", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "any" + ], "Size": 2, "ArgEnum": [ "JSONString", "JSONUint64", "JSONObject" ], - "ArgEnumTypes": "BUB", + "ArgEnumTypes": [ + "[]byte", + "uint64", + "[]byte" + ], "Doc": "key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A", "DocExtra": "*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size.\n\nAlmost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON.", "ImmediateNote": "{uint8 return type index}", @@ -1458,8 +2096,12 @@ { "Opcode": 96, "Name": "balance", - "Args": ".", - "Returns": "U", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", @@ -1471,8 +2113,13 @@ { "Opcode": 97, "Name": "app_opted_in", - "Args": ".U", - "Returns": "U", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "1 if account A is opted in to application B, else 0", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", @@ -1484,8 +2131,13 @@ { "Opcode": 98, "Name": "app_local_get", - "Args": ".B", - "Returns": ".", + "Args": [ + "any", + "key" + ], + "Returns": [ + "any" + ], "Size": 1, "Doc": "local state of the key B in the current application in account A", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", @@ -1497,8 +2149,15 @@ { "Opcode": 99, "Name": "app_local_get_ex", - "Args": ".UB", - "Returns": ".U", + "Args": [ + "any", + "uint64", + "key" + ], + "Returns": [ + "any", + "bool" + ], "Size": 1, "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", @@ -1510,8 +2169,12 @@ { "Opcode": 100, "Name": "app_global_get", - "Args": "B", - "Returns": ".", + "Args": [ + "key" + ], + "Returns": [ + "any" + ], "Size": 1, "Doc": "global state of the key A in the current application", "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", @@ -1523,8 +2186,14 @@ { "Opcode": 101, "Name": "app_global_get_ex", - "Args": "UB", - "Returns": ".U", + "Args": [ + "uint64", + "key" + ], + "Returns": [ + "any", + "bool" + ], "Size": 1, "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", @@ -1536,7 +2205,11 @@ { "Opcode": 102, "Name": "app_local_put", - "Args": ".B.", + "Args": [ + "any", + "key", + "any" + ], "Size": 1, "Doc": "write C to key B in account A's local state of the current application", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", @@ -1548,7 +2221,10 @@ { "Opcode": 103, "Name": "app_global_put", - "Args": "B.", + "Args": [ + "key", + "any" + ], "Size": 1, "Doc": "write B to key A in the global state of the current application", "IntroducedVersion": 2, @@ -1559,7 +2235,10 @@ { "Opcode": 104, "Name": "app_local_del", - "Args": ".B", + "Args": [ + "any", + "key" + ], "Size": 1, "Doc": "delete key B from account A's local state of the current application", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", @@ -1571,7 +2250,9 @@ { "Opcode": 105, "Name": "app_global_del", - "Args": "B", + "Args": [ + "key" + ], "Size": 1, "Doc": "delete key A from the global state of the current application", "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", @@ -1583,14 +2264,23 @@ { "Opcode": 112, "Name": "asset_holding_get", - "Args": ".U", - "Returns": ".U", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "any", + "bool" + ], "Size": 2, "ArgEnum": [ "AssetBalance", "AssetFrozen" ], - "ArgEnumTypes": "UU", + "ArgEnumTypes": [ + "uint64", + "bool" + ], "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", "ImmediateNote": "{uint8 asset holding field index}", @@ -1602,8 +2292,13 @@ { "Opcode": 113, "Name": "asset_params_get", - "Args": "U", - "Returns": ".U", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], "Size": 2, "ArgEnum": [ "AssetTotal", @@ -1619,7 +2314,20 @@ "AssetClawback", "AssetCreator" ], - "ArgEnumTypes": "UUUBBBBBBBBB", + "ArgEnumTypes": [ + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "hash", + "addr", + "addr", + "addr", + "addr", + "addr" + ], "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", "ImmediateNote": "{uint8 asset params field index}", @@ -1631,8 +2339,13 @@ { "Opcode": 114, "Name": "app_params_get", - "Args": "U", - "Returns": ".U", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], "Size": 2, "ArgEnum": [ "AppApprovalProgram", @@ -1645,7 +2358,17 @@ "AppCreator", "AppAddress" ], - "ArgEnumTypes": "BBUUUUUBB", + "ArgEnumTypes": [ + "[]byte", + "[]byte", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "addr", + "addr" + ], "Doc": "X is field F from app A. Y is 1 if A exists, else 0", "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", "ImmediateNote": "{uint8 app params field index}", @@ -1657,8 +2380,13 @@ { "Opcode": 115, "Name": "acct_params_get", - "Args": ".", - "Returns": ".U", + "Args": [ + "any" + ], + "Returns": [ + "any", + "bool" + ], "Size": 2, "ArgEnum": [ "AcctBalance", @@ -1674,7 +2402,20 @@ "AcctTotalBoxes", "AcctTotalBoxBytes" ], - "ArgEnumTypes": "UUBUUUUUUUUU", + "ArgEnumTypes": [ + "uint64", + "uint64", + "addr", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], "Doc": "X is field F from account A. Y is 1 if A owns positive algos, else 0", "ImmediateNote": "{uint8 account params field index}", "IntroducedVersion": 6, @@ -1685,8 +2426,12 @@ { "Opcode": 120, "Name": "min_balance", - "Args": ".", - "Returns": "U", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", @@ -1698,7 +2443,9 @@ { "Opcode": 128, "Name": "pushbytes", - "Returns": "B", + "Returns": [ + "[]byte" + ], "Size": 0, "Doc": "immediate BYTES", "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", @@ -1711,7 +2458,9 @@ { "Opcode": 129, "Name": "pushint", - "Returns": "U", + "Returns": [ + "uint64" + ], "Size": 0, "Doc": "immediate UINT", "DocExtra": "pushint args are not added to the intcblock during assembly processes", @@ -1748,8 +2497,14 @@ { "Opcode": 132, "Name": "ed25519verify_bare", - "Args": "BBB", - "Returns": "U", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "for (data A, signature B, pubkey C) verify the signature of the data against the pubkey =\u003e {0 or 1}", "IntroducedVersion": 7, @@ -1795,7 +2550,9 @@ { "Opcode": 139, "Name": "frame_dig", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 2, "Doc": "Nth (signed) value from the frame pointer.", "ImmediateNote": "{int8 frame slot}", @@ -1807,7 +2564,9 @@ { "Opcode": 140, "Name": "frame_bury", - "Args": ".", + "Args": [ + "any" + ], "Size": 2, "Doc": "replace the Nth (signed) value from the frame pointer in the stack with A", "ImmediateNote": "{int8 frame slot}", @@ -1819,7 +2578,9 @@ { "Opcode": 141, "Name": "switch", - "Args": "U", + "Args": [ + "uint64" + ], "Size": 0, "Doc": "branch to the Ath label. Continue at following instruction if index A exceeds the number of labels.", "ImmediateNote": "{uint8 branch count} [{int16 branch offset, big-endian}, ...]", @@ -1843,8 +2604,13 @@ { "Opcode": 144, "Name": "shl", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A times 2^B, modulo 2^64", "IntroducedVersion": 4, @@ -1855,8 +2621,13 @@ { "Opcode": 145, "Name": "shr", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A divided by 2^B", "IntroducedVersion": 4, @@ -1867,8 +2638,12 @@ { "Opcode": 146, "Name": "sqrt", - "Args": "U", - "Returns": "U", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "The largest integer I such that I^2 \u003c= A", "IntroducedVersion": 4, @@ -1879,8 +2654,12 @@ { "Opcode": 147, "Name": "bitlen", - "Args": ".", - "Returns": "U", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", "DocExtra": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", @@ -1892,8 +2671,13 @@ { "Opcode": 148, "Name": "exp", - "Args": "UU", - "Returns": "U", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A raised to the Bth power. Fail if A == B == 0 and on overflow", "IntroducedVersion": 4, @@ -1904,8 +2688,14 @@ { "Opcode": 149, "Name": "expw", - "Args": "UU", - "Returns": "UU", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], "Size": 1, "Doc": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", "IntroducedVersion": 4, @@ -1916,8 +2706,12 @@ { "Opcode": 150, "Name": "bsqrt", - "Args": "B", - "Returns": "B", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "The largest integer I such that I^2 \u003c= A. A and I are interpreted as big-endian unsigned integers", "IntroducedVersion": 6, @@ -1928,8 +2722,14 @@ { "Opcode": 151, "Name": "divw", - "Args": "UUU", - "Returns": "U", + "Args": [ + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], "Size": 1, "Doc": "A,B / C. Fail if C == 0 or if result overflows.", "DocExtra": "The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low.", @@ -1941,8 +2741,12 @@ { "Opcode": 152, "Name": "sha3_256", - "Args": "B", - "Returns": "B", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "SHA3_256 hash of value A, yields [32]byte", "IntroducedVersion": 7, @@ -1953,8 +2757,13 @@ { "Opcode": 160, "Name": "b+", - "Args": "BB", - "Returns": "B", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], "Size": 1, "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, @@ -1965,8 +2774,13 @@ { "Opcode": 161, "Name": "b-", - "Args": "BB", - "Returns": "B", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], "Size": 1, "Doc": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", "IntroducedVersion": 4, @@ -1977,8 +2791,13 @@ { "Opcode": 162, "Name": "b/", - "Args": "BB", - "Returns": "B", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], "Size": 1, "Doc": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "IntroducedVersion": 4, @@ -1989,8 +2808,13 @@ { "Opcode": 163, "Name": "b*", - "Args": "BB", - "Returns": "B", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], "Size": 1, "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.", "IntroducedVersion": 4, @@ -2001,8 +2825,13 @@ { "Opcode": 164, "Name": "b\u003c", - "Args": "BB", - "Returns": "U", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, @@ -2013,8 +2842,13 @@ { "Opcode": 165, "Name": "b\u003e", - "Args": "BB", - "Returns": "U", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, @@ -2025,8 +2859,13 @@ { "Opcode": 166, "Name": "b\u003c=", - "Args": "BB", - "Returns": "U", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, @@ -2037,8 +2876,13 @@ { "Opcode": 167, "Name": "b\u003e=", - "Args": "BB", - "Returns": "U", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, @@ -2049,8 +2893,13 @@ { "Opcode": 168, "Name": "b==", - "Args": "BB", - "Returns": "U", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, @@ -2061,8 +2910,13 @@ { "Opcode": 169, "Name": "b!=", - "Args": "BB", - "Returns": "U", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, @@ -2073,8 +2927,13 @@ { "Opcode": 170, "Name": "b%", - "Args": "BB", - "Returns": "B", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "IntroducedVersion": 4, @@ -2085,8 +2944,13 @@ { "Opcode": 171, "Name": "b|", - "Args": "BB", - "Returns": "B", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths", "IntroducedVersion": 4, @@ -2097,8 +2961,13 @@ { "Opcode": 172, "Name": "b\u0026", - "Args": "BB", - "Returns": "B", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths", "IntroducedVersion": 4, @@ -2109,8 +2978,13 @@ { "Opcode": 173, "Name": "b^", - "Args": "BB", - "Returns": "B", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", "IntroducedVersion": 4, @@ -2121,8 +2995,12 @@ { "Opcode": 174, "Name": "b~", - "Args": "B", - "Returns": "B", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "A with all bits inverted", "IntroducedVersion": 4, @@ -2133,8 +3011,12 @@ { "Opcode": 175, "Name": "bzero", - "Args": "U", - "Returns": "B", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "zero filled byte-array of length A", "IntroducedVersion": 4, @@ -2145,7 +3027,9 @@ { "Opcode": 176, "Name": "log", - "Args": "B", + "Args": [ + "[]byte" + ], "Size": 1, "Doc": "write A to log state of the current application", "DocExtra": "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.", @@ -2168,7 +3052,9 @@ { "Opcode": 178, "Name": "itxn_field", - "Args": ".", + "Args": [ + "any" + ], "Size": 2, "ArgEnum": [ "Sender", @@ -2223,7 +3109,59 @@ "ApprovalProgramPages", "ClearStateProgramPages" ], - "ArgEnumTypes": "BUBBUBBBUUUBUUUBBBUUBBBBBUUUUBBBBBBBBUBUUUUUUUUUBBB", + "ArgEnumTypes": [ + "addr", + "uint64", + "[]byte", + "addr", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "uint64", + "[]byte", + "addr", + "[]byte", + "[]byte", + "addr", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "hash", + "addr", + "addr", + "addr", + "addr", + "uint64", + "addr", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte" + ], "Doc": "set field F of the current inner transaction to A", "DocExtra": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", "ImmediateNote": "{uint8 transaction field index}", @@ -2246,7 +3184,9 @@ { "Opcode": 180, "Name": "itxn", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 2, "ArgEnum": [ "Sender", @@ -2318,7 +3258,76 @@ "ClearStateProgramPages", "NumClearStateProgramPages" ], - "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBBBUBU", + "ArgEnumTypes": [ + "addr", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "hash", + "addr", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "hash", + "uint64", + "uint64", + "[]byte", + "uint64", + "addr", + "uint64", + "[]byte", + "[]byte", + "addr", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "hash", + "addr", + "addr", + "addr", + "addr", + "uint64", + "addr", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], "Doc": "field F of the last inner transaction", "ImmediateNote": "{uint8 transaction field index}", "IntroducedVersion": 5, @@ -2329,7 +3338,9 @@ { "Opcode": 181, "Name": "itxna", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 3, "ArgEnum": [ "ApplicationArgs", @@ -2340,7 +3351,15 @@ "ApprovalProgramPages", "ClearStateProgramPages" ], - "ArgEnumTypes": "BBUUBBB", + "ArgEnumTypes": [ + "[]byte", + "addr", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], "Doc": "Ith value of the array field F of the last inner transaction", "ImmediateNote": "{uint8 transaction field index} {uint8 transaction field array index}", "IntroducedVersion": 5, @@ -2362,7 +3381,9 @@ { "Opcode": 183, "Name": "gitxn", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 3, "ArgEnum": [ "Sender", @@ -2434,7 +3455,76 @@ "ClearStateProgramPages", "NumClearStateProgramPages" ], - "ArgEnumTypes": "BUUUUBBBUBBBUUUBUUUBBBUBUUBUBUBBBUUUUBBBBBBBBUBUUUUUUUUUUUBUUUBBBUBU", + "ArgEnumTypes": [ + "addr", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "hash", + "addr", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "addr", + "addr", + "addr", + "uint64", + "hash", + "uint64", + "uint64", + "[]byte", + "uint64", + "addr", + "uint64", + "[]byte", + "[]byte", + "addr", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "hash", + "addr", + "addr", + "addr", + "addr", + "uint64", + "addr", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], "Doc": "field F of the Tth transaction in the last inner group submitted", "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}", "IntroducedVersion": 6, @@ -2445,7 +3535,9 @@ { "Opcode": 184, "Name": "gitxna", - "Returns": ".", + "Returns": [ + "any" + ], "Size": 4, "ArgEnum": [ "ApplicationArgs", @@ -2456,7 +3548,15 @@ "ApprovalProgramPages", "ClearStateProgramPages" ], - "ArgEnumTypes": "BBUUBBB", + "ArgEnumTypes": [ + "[]byte", + "addr", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], "Doc": "Ith value of the array field F from the Tth transaction in the last inner group submitted", "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index}", "IntroducedVersion": 6, @@ -2467,8 +3567,13 @@ { "Opcode": 185, "Name": "box_create", - "Args": "BU", - "Returns": "U", + "Args": [ + "key", + "uint64" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1", "DocExtra": "Newly created boxes are filled with 0 bytes. `box_create` will fail if the referenced box already exists with a different size. Otherwise, existing boxes are unchanged by `box_create`.", @@ -2480,8 +3585,14 @@ { "Opcode": 186, "Name": "box_extract", - "Args": "BUU", - "Returns": "B", + "Args": [ + "key", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", "IntroducedVersion": 8, @@ -2492,7 +3603,11 @@ { "Opcode": 187, "Name": "box_replace", - "Args": "BUB", + "Args": [ + "key", + "uint64", + "[]byte" + ], "Size": 1, "Doc": "write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", "IntroducedVersion": 8, @@ -2503,8 +3618,12 @@ { "Opcode": 188, "Name": "box_del", - "Args": "B", - "Returns": "U", + "Args": [ + "key" + ], + "Returns": [ + "bool" + ], "Size": 1, "Doc": "delete box named A if it exists. Return 1 if A existed, 0 otherwise", "IntroducedVersion": 8, @@ -2515,8 +3634,13 @@ { "Opcode": 189, "Name": "box_len", - "Args": "B", - "Returns": "UU", + "Args": [ + "key" + ], + "Returns": [ + "uint64", + "bool" + ], "Size": 1, "Doc": "X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0.", "IntroducedVersion": 8, @@ -2527,8 +3651,13 @@ { "Opcode": 190, "Name": "box_get", - "Args": "B", - "Returns": "BU", + "Args": [ + "key" + ], + "Returns": [ + "[]byte", + "bool" + ], "Size": 1, "Doc": "X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0.", "DocExtra": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", @@ -2540,7 +3669,10 @@ { "Opcode": 191, "Name": "box_put", - "Args": "BB", + "Args": [ + "key", + "[]byte" + ], "Size": 1, "Doc": "replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist", "DocExtra": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", @@ -2552,8 +3684,12 @@ { "Opcode": 192, "Name": "txnas", - "Args": "U", - "Returns": ".", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], "Size": 2, "ArgEnum": [ "ApplicationArgs", @@ -2564,7 +3700,15 @@ "ApprovalProgramPages", "ClearStateProgramPages" ], - "ArgEnumTypes": "BBUUBBB", + "ArgEnumTypes": [ + "[]byte", + "addr", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], "Doc": "Ath value of the array field F of the current transaction", "ImmediateNote": "{uint8 transaction field index}", "IntroducedVersion": 5, @@ -2575,8 +3719,12 @@ { "Opcode": 193, "Name": "gtxnas", - "Args": "U", - "Returns": ".", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], "Size": 3, "ArgEnum": [ "ApplicationArgs", @@ -2587,7 +3735,15 @@ "ApprovalProgramPages", "ClearStateProgramPages" ], - "ArgEnumTypes": "BBUUBBB", + "ArgEnumTypes": [ + "[]byte", + "addr", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], "Doc": "Ath value of the array field F from the Tth transaction in the current group", "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}", "IntroducedVersion": 5, @@ -2598,8 +3754,13 @@ { "Opcode": 194, "Name": "gtxnsas", - "Args": "UU", - "Returns": ".", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], "Size": 2, "ArgEnum": [ "ApplicationArgs", @@ -2610,7 +3771,15 @@ "ApprovalProgramPages", "ClearStateProgramPages" ], - "ArgEnumTypes": "BBUUBBB", + "ArgEnumTypes": [ + "[]byte", + "addr", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], "Doc": "Bth value of the array field F from the Ath transaction in the current group", "ImmediateNote": "{uint8 transaction field index}", "IntroducedVersion": 5, @@ -2621,8 +3790,12 @@ { "Opcode": 195, "Name": "args", - "Args": "U", - "Returns": "B", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], "Size": 1, "Doc": "Ath LogicSig argument", "IntroducedVersion": 5, @@ -2633,8 +3806,13 @@ { "Opcode": 196, "Name": "gloadss", - "Args": "UU", - "Returns": ".", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], "Size": 1, "Doc": "Bth scratch space value of the Ath transaction in the current group", "IntroducedVersion": 6, @@ -2645,8 +3823,12 @@ { "Opcode": 197, "Name": "itxnas", - "Args": "U", - "Returns": ".", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], "Size": 2, "Doc": "Ath value of the array field F of the last inner transaction", "ImmediateNote": "{uint8 transaction field index}", @@ -2658,8 +3840,12 @@ { "Opcode": 198, "Name": "gitxnas", - "Args": "U", - "Returns": ".", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], "Size": 3, "Doc": "Ath value of the array field F from the Tth transaction in the last inner group submitted", "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}", @@ -2671,12 +3857,22 @@ { "Opcode": 208, "Name": "vrf_verify", - "Args": "BBB", - "Returns": "BU", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "bool" + ], "Size": 2, "ArgEnum": [ "VrfAlgorand" ], + "ArgEnumTypes": [ + "none" + ], "Doc": "Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.", "DocExtra": "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).", "ImmediateNote": "{uint8 parameters index}", @@ -2688,14 +3884,21 @@ { "Opcode": 209, "Name": "block", - "Args": "U", - "Returns": ".", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], "Size": 2, "ArgEnum": [ "BlkSeed", "BlkTimestamp" ], - "ArgEnumTypes": "BU", + "ArgEnumTypes": [ + "[]byte", + "uint64" + ], "Doc": "field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)", "ImmediateNote": "{uint8 block field index}", "IntroducedVersion": 7, diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index 91d186198f..ff0f9a2dd6 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -377,7 +377,7 @@ type OpSpec struct { // AlwaysExits is true iff the opcode always ends the program. func (spec *OpSpec) AlwaysExits() bool { - return len(spec.Return.Types) == 1 && spec.Return.Types[0] == StackNone + return len(spec.Return.Types) == 1 && spec.Return.Types[0].AVMType == avmNone } func (spec *OpSpec) deadens() bool { From 53659cb97c703e4426a750aa28a1d761f63cedd4 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 10 Feb 2023 13:34:37 -0500 Subject: [PATCH 05/41] AVM: modify immediate arguments to get their encoding from the type itself and expose the immediates by name in the langspec.json file --- cmd/opdoc/opdoc.go | 60 ++- data/transactions/logic/TEAL_opcodes.md | 574 +++++++++++++---------- data/transactions/logic/doc.go | 183 +++++--- data/transactions/logic/doc_test.go | 42 +- data/transactions/logic/langspec.json | 583 +++++++++++++++++++++--- data/transactions/logic/opcodes.go | 22 + 6 files changed, 1042 insertions(+), 422 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index 2fa9a7b2eb..f77153d80f 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -30,6 +30,38 @@ import ( var docVersion = 8 +// OpImmediateNote returns a short string about immediate data which follows the op byte +func opImmediateNoteSyntaxMarkdown(name string, oids []logic.OpImmediateDetails) string { + if len(oids) == 0 { + return "" + } + + argNames := make([]string, len(oids)) + argDocs := make([]string, len(oids)) + for idx, oid := range oids { + argNote := oid.Comment + if oid.Reference != "" { + argNote = fmt.Sprintf("[%s](#field-group-%s)", oid.Reference, strings.ToLower(oid.Reference)) + } + argNames[idx] = oid.Name + argDocs[idx] = fmt.Sprintf("%s: %s", oid.Name, argNote) + } + + return fmt.Sprintf("`%s %s` ∋ %s", name, strings.Join(argNames, " "), strings.Join(argDocs, ", ")) +} + +func opImmediateNoteEncoding(opcode byte, oids []logic.OpImmediateDetails) string { + if len(oids) == 0 { + return fmt.Sprintf("0x%02x", opcode) + } + + notes := make([]string, len(oids)) + for idx, oid := range oids { + notes[idx] = oid.Encoding + } + return fmt.Sprintf("0x%02x {%s}", opcode, strings.Join(notes, "}, {")) +} + func opGroupMarkdownTable(names []string, out io.Writer) { fmt.Fprint(out, `| Opcode | Description | | - | -- | @@ -169,14 +201,22 @@ func stackMarkdown(op *logic.OpSpec) string { } func opToMarkdown(out io.Writer, op *logic.OpSpec, groupDocWritten map[string]bool) (err error) { - ws := "" - opextra := logic.OpImmediateNote(op.Name) - if opextra != "" { - ws = " " + + deets := logic.OpImmediateDetailsFromSpec(*op) + + // Only need syntax line if there are immediates + // so it carries its own newline + syntax := "" + if opSyntax := opImmediateNoteSyntaxMarkdown(op.Name, deets); opSyntax != "" { + syntax = fmt.Sprintf("- Syntax: %s\n", opSyntax) } + + encoding := fmt.Sprintf("- Bytecode: %s", opImmediateNoteEncoding(op.Opcode, deets)) + stackEffects := stackMarkdown(op) - fmt.Fprintf(out, "\n## %s%s\n\n- Opcode: 0x%02x%s%s\n%s", - op.Name, immediateMarkdown(op), op.Opcode, ws, opextra, stackEffects) + + fmt.Fprintf(out, "\n## %s\n\n%s%s\n%s", op.Name, syntax, encoding, stackEffects) + fmt.Fprintf(out, "- %s\n", logic.OpDoc(op.Name)) // if cost changed with versions print all of them costs := logic.OpAllCosts(op.Name) @@ -209,7 +249,7 @@ func opToMarkdown(out io.Writer, op *logic.OpSpec, groupDocWritten map[string]bo for i := range op.OpDetails.Immediates { group := op.OpDetails.Immediates[i].Group if group != nil && group.Doc != "" && !groupDocWritten[group.Name] { - fmt.Fprintf(out, "\n`%s` %s:\n\n", group.Name, group.Doc) + fmt.Fprintf(out, "\n### Field Group %s\n\n%s\n\n", group.Name, group.Doc) fieldGroupMarkdown(out, group) groupDocWritten[group.Name] = true } @@ -246,8 +286,8 @@ type OpRecord struct { ArgEnumTypes []string `json:",omitempty"` Doc string - DocExtra string `json:",omitempty"` - ImmediateNote string `json:",omitempty"` + DocExtra string `json:",omitempty"` + ImmediateNote []logic.OpImmediateDetails `json:",omitempty"` IntroducedVersion uint64 Groups []string } @@ -331,7 +371,7 @@ func buildLanguageSpec(opGroups map[string][]string) *LanguageSpec { records[i].ArgEnum, records[i].ArgEnumTypes = argEnums(spec.Name) records[i].Doc = strings.ReplaceAll(logic.OpDoc(spec.Name), "
", "\n") records[i].DocExtra = logic.OpDocExtra(spec.Name) - records[i].ImmediateNote = logic.OpImmediateNote(spec.Name) + records[i].ImmediateNote = logic.OpImmediateDetailsFromSpec(spec) records[i].Groups = opGroups[spec.Name] records[i].IntroducedVersion = spec.Version } diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index f1124e4626..2aea34d52d 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -5,13 +5,13 @@ Ops have a 'cost' of 1 unless otherwise specified. ## err -- Opcode: 0x00 +- Bytecode: 0x00 - Stack: ... → _exits_ - Fail immediately. ## sha256 -- Opcode: 0x01 +- Bytecode: 0x01 - Stack: ..., A: []byte → ..., hash - SHA256 hash of value A, yields [32]byte - **Cost**: @@ -20,7 +20,7 @@ Ops have a 'cost' of 1 unless otherwise specified. ## keccak256 -- Opcode: 0x02 +- Bytecode: 0x02 - Stack: ..., A: []byte → ..., hash - Keccak256 hash of value A, yields [32]byte - **Cost**: @@ -29,7 +29,7 @@ Ops have a 'cost' of 1 unless otherwise specified. ## sha512_256 -- Opcode: 0x03 +- Bytecode: 0x03 - Stack: ..., A: []byte → ..., hash - SHA512_256 hash of value A, yields [32]byte - **Cost**: @@ -38,22 +38,25 @@ Ops have a 'cost' of 1 unless otherwise specified. ## ed25519verify -- Opcode: 0x04 +- Bytecode: 0x04 - Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool - for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} - **Cost**: 1900 The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. -## ecdsa_verify v +## ecdsa_verify -- Opcode: 0x05 {uint8 curve index} +- Syntax: `ecdsa_verify V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x05 {uint8} - Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte → ..., bool - for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} - **Cost**: Secp256k1=1700 Secp256r1=2500 - Availability: v5 -`ECDSA` Curves: +### Field Group ECDSA + +Curves | Index | Name | In | Notes | | - | ------ | - | --------- | @@ -63,9 +66,10 @@ The 32 byte public key is the last element on the stack, preceded by the 64 byte The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted. -## ecdsa_pk_decompress v +## ecdsa_pk_decompress -- Opcode: 0x06 {uint8 curve index} +- Syntax: `ecdsa_pk_decompress V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x06 {uint8} - Stack: ..., A: []byte → ..., X: []byte, Y: []byte - decompress pubkey A into components X, Y - **Cost**: Secp256k1=650 Secp256r1=2400 @@ -73,9 +77,10 @@ The 32 byte Y-component of a public key is the last element on the stack, preced The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded. -## ecdsa_pk_recover v +## ecdsa_pk_recover -- Opcode: 0x07 {uint8 curve index} +- Syntax: `ecdsa_pk_recover V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x07 {uint8} - Stack: ..., A: []byte, B: uint64, C: []byte, D: []byte → ..., X: []byte, Y: []byte - for (data A, recovery id B, signature C, D) recover a public key - **Cost**: 2000 @@ -85,7 +90,7 @@ S (top) and R elements of a signature, recovery id and data (bottom) are expecte ## + -- Opcode: 0x08 +- Bytecode: 0x08 - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A plus B. Fail on overflow. @@ -93,13 +98,13 @@ Overflow is an error condition which halts execution and fails the transaction. ## - -- Opcode: 0x09 +- Bytecode: 0x09 - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A minus B. Fail if B > A. ## / -- Opcode: 0x0a +- Bytecode: 0x0a - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A divided by B (truncated division). Fail if B == 0. @@ -107,7 +112,7 @@ Overflow is an error condition which halts execution and fails the transaction. ## * -- Opcode: 0x0b +- Bytecode: 0x0b - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A times B. Fail on overflow. @@ -115,73 +120,73 @@ Overflow is an error condition which halts execution and fails the transaction. ## < -- Opcode: 0x0c +- Bytecode: 0x0c - Stack: ..., A: uint64, B: uint64 → ..., bool - A less than B => {0 or 1} ## > -- Opcode: 0x0d +- Bytecode: 0x0d - Stack: ..., A: uint64, B: uint64 → ..., bool - A greater than B => {0 or 1} ## <= -- Opcode: 0x0e +- Bytecode: 0x0e - Stack: ..., A: uint64, B: uint64 → ..., bool - A less than or equal to B => {0 or 1} ## >= -- Opcode: 0x0f +- Bytecode: 0x0f - Stack: ..., A: uint64, B: uint64 → ..., bool - A greater than or equal to B => {0 or 1} ## && -- Opcode: 0x10 +- Bytecode: 0x10 - Stack: ..., A: uint64, B: uint64 → ..., bool - A is not zero and B is not zero => {0 or 1} ## || -- Opcode: 0x11 +- Bytecode: 0x11 - Stack: ..., A: uint64, B: uint64 → ..., bool - A is not zero or B is not zero => {0 or 1} ## == -- Opcode: 0x12 +- Bytecode: 0x12 - Stack: ..., A, B → ..., bool - A is equal to B => {0 or 1} ## != -- Opcode: 0x13 +- Bytecode: 0x13 - Stack: ..., A, B → ..., bool - A is not equal to B => {0 or 1} ## ! -- Opcode: 0x14 +- Bytecode: 0x14 - Stack: ..., A: uint64 → ..., uint64 - A == 0 yields 1; else 0 ## len -- Opcode: 0x15 +- Bytecode: 0x15 - Stack: ..., A: []byte → ..., uint64 - yields length of byte value A ## itob -- Opcode: 0x16 +- Bytecode: 0x16 - Stack: ..., A: uint64 → ..., []byte - converts uint64 A to big-endian byte array, always of length 8 ## btoi -- Opcode: 0x17 +- Bytecode: 0x17 - Stack: ..., A: []byte → ..., uint64 - converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. @@ -189,50 +194,50 @@ Overflow is an error condition which halts execution and fails the transaction. ## % -- Opcode: 0x18 +- Bytecode: 0x18 - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A modulo B. Fail if B == 0. ## | -- Opcode: 0x19 +- Bytecode: 0x19 - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A bitwise-or B ## & -- Opcode: 0x1a +- Bytecode: 0x1a - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A bitwise-and B ## ^ -- Opcode: 0x1b +- Bytecode: 0x1b - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A bitwise-xor B ## ~ -- Opcode: 0x1c +- Bytecode: 0x1c - Stack: ..., A: uint64 → ..., uint64 - bitwise invert value A ## mulw -- Opcode: 0x1d +- Bytecode: 0x1d - Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 - A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low ## addw -- Opcode: 0x1e +- Bytecode: 0x1e - Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 - A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits. - Availability: v2 ## divmodw -- Opcode: 0x1f +- Bytecode: 0x1f - Stack: ..., A: uint64, B: uint64, C: uint64, D: uint64 → ..., W: uint64, X: uint64, Y: uint64, Z: uint64 - W,X = (A,B / C,D); Y,Z = (A,B modulo C,D) - **Cost**: 20 @@ -240,124 +245,132 @@ Overflow is an error condition which halts execution and fails the transaction. The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low. -## intcblock uint ... +## intcblock -- Opcode: 0x20 {varuint count} [{varuint value}, ...] +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} - Stack: ... → ... - prepare block of uint64 constants for use by intc `intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. -## intc i +## intc -- Opcode: 0x21 {uint8 int constant index} +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} - Stack: ... → ..., uint64 - Ith constant from intcblock ## intc_0 -- Opcode: 0x22 +- Bytecode: 0x22 - Stack: ... → ..., uint64 - constant 0 from intcblock ## intc_1 -- Opcode: 0x23 +- Bytecode: 0x23 - Stack: ... → ..., uint64 - constant 1 from intcblock ## intc_2 -- Opcode: 0x24 +- Bytecode: 0x24 - Stack: ... → ..., uint64 - constant 2 from intcblock ## intc_3 -- Opcode: 0x25 +- Bytecode: 0x25 - Stack: ... → ..., uint64 - constant 3 from intcblock -## bytecblock bytes ... +## bytecblock -- Opcode: 0x26 {varuint count} [({varuint length} bytes), ...] +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte const values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} - Stack: ... → ... - prepare block of byte-array constants for use by bytec `bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. -## bytec i +## bytec -- Opcode: 0x27 {uint8 byte constant index} +- Syntax: `bytec I` ∋ I: an index in the bytec block +- Bytecode: 0x27 {uint8} - Stack: ... → ..., []byte - Ith constant from bytecblock ## bytec_0 -- Opcode: 0x28 +- Bytecode: 0x28 - Stack: ... → ..., []byte - constant 0 from bytecblock ## bytec_1 -- Opcode: 0x29 +- Bytecode: 0x29 - Stack: ... → ..., []byte - constant 1 from bytecblock ## bytec_2 -- Opcode: 0x2a +- Bytecode: 0x2a - Stack: ... → ..., []byte - constant 2 from bytecblock ## bytec_3 -- Opcode: 0x2b +- Bytecode: 0x2b - Stack: ... → ..., []byte - constant 3 from bytecblock -## arg n +## arg -- Opcode: 0x2c {uint8 arg index} +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} - Stack: ... → ..., []byte - Nth LogicSig argument - Mode: Signature ## arg_0 -- Opcode: 0x2d +- Bytecode: 0x2d - Stack: ... → ..., []byte - LogicSig argument 0 - Mode: Signature ## arg_1 -- Opcode: 0x2e +- Bytecode: 0x2e - Stack: ... → ..., []byte - LogicSig argument 1 - Mode: Signature ## arg_2 -- Opcode: 0x2f +- Bytecode: 0x2f - Stack: ... → ..., []byte - LogicSig argument 2 - Mode: Signature ## arg_3 -- Opcode: 0x30 +- Bytecode: 0x30 - Stack: ... → ..., []byte - LogicSig argument 3 - Mode: Signature -## txn f +## txn -- Opcode: 0x31 {uint8 transaction field index} +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} - Stack: ... → ..., any - field F of current transaction -`txn` Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)): +### Field Group txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | @@ -424,13 +437,16 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 67 | NumClearStateProgramPages | uint64 | v7 | Number of ClearState Program pages | -## global f +## global -- Opcode: 0x32 {uint8 global field index} +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} - Stack: ... → ..., any - global field F -`global` Fields: +### Field Group global + +Fields | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | @@ -451,34 +467,40 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 14 | CallerApplicationAddress | addr | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | -## gtxn t f +## gtxn -- Opcode: 0x33 {uint8 transaction group index} {uint8 transaction field index} +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} - Stack: ... → ..., any - field F of the Tth transaction in the current group for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. -## load i +## load -- Opcode: 0x34 {uint8 position in scratch space to load from} +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} - Stack: ... → ..., any - Ith scratch space value. All scratch spaces are 0 at program start. -## store i +## store -- Opcode: 0x35 {uint8 position in scratch space to store to} +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} - Stack: ..., A → ... - store A to the Ith scratch space -## txna f i +## txna -- Opcode: 0x36 {uint8 transaction field index} {uint8 transaction field array index} +- Syntax: `txna F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x36 {uint8}, {uint8} - Stack: ... → ..., any - Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. - Availability: v2 -`txna` Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)): +### Field Group txna + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | @@ -491,32 +513,36 @@ for notes on transaction fields available, see `txn`. If this transaction is _i_ | 66 | ClearStateProgramPages | []byte | v7 | ClearState Program as an array of pages | -## gtxna t f i +## gtxna -- Opcode: 0x37 {uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index} +- Syntax: `gtxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x37 {uint8}, {uint8}, {uint8} - Stack: ... → ..., any - Ith value of the array field F from the Tth transaction in the current group
`gtxna` can be called using `gtxn` with 3 immediates. - Availability: v2 -## gtxns f +## gtxns -- Opcode: 0x38 {uint8 transaction field index} +- Syntax: `gtxns F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x38 {uint8} - Stack: ..., A: uint64 → ..., any - field F of the Ath transaction in the current group - Availability: v3 for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction. -## gtxnsa f i +## gtxnsa -- Opcode: 0x39 {uint8 transaction field index} {uint8 transaction field array index} +- Syntax: `gtxnsa F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x39 {uint8}, {uint8} - Stack: ..., A: uint64 → ..., any - Ith value of the array field F from the Ath transaction in the current group
`gtxnsa` can be called using `gtxns` with 2 immediates. - Availability: v3 -## gload t i +## gload -- Opcode: 0x3a {uint8 transaction group index} {uint8 position in scratch space to load from} +- Syntax: `gload T I` ∋ T: transaction group index, I: position in scratch space to load from +- Bytecode: 0x3a {uint8}, {uint8} - Stack: ... → ..., any - Ith scratch space value of the Tth transaction in the current group - Availability: v4 @@ -524,9 +550,10 @@ for notes on transaction fields available, see `txn`. If top of stack is _i_, `g `gload` fails unless the requested transaction is an ApplicationCall and T < GroupIndex. -## gloads i +## gloads -- Opcode: 0x3b {uint8 position in scratch space to load from} +- Syntax: `gloads I` ∋ I: position in scratch space to load from +- Bytecode: 0x3b {uint8} - Stack: ..., A: uint64 → ..., any - Ith scratch space value of the Ath transaction in the current group - Availability: v4 @@ -534,9 +561,10 @@ for notes on transaction fields available, see `txn`. If top of stack is _i_, `g `gloads` fails unless the requested transaction is an ApplicationCall and A < GroupIndex. -## gaid t +## gaid -- Opcode: 0x3c {uint8 transaction group index} +- Syntax: `gaid T` ∋ T: transaction group index +- Bytecode: 0x3c {uint8} - Stack: ... → ..., uint64 - ID of the asset or application created in the Tth transaction of the current group - Availability: v4 @@ -546,7 +574,7 @@ for notes on transaction fields available, see `txn`. If top of stack is _i_, `g ## gaids -- Opcode: 0x3d +- Bytecode: 0x3d - Stack: ..., A: uint64 → ..., uint64 - ID of the asset or application created in the Ath transaction of the current group - Availability: v4 @@ -556,21 +584,22 @@ for notes on transaction fields available, see `txn`. If top of stack is _i_, `g ## loads -- Opcode: 0x3e +- Bytecode: 0x3e - Stack: ..., A: uint64 → ..., any - Ath scratch space value. All scratch spaces are 0 at program start. - Availability: v5 ## stores -- Opcode: 0x3f +- Bytecode: 0x3f - Stack: ..., A: uint64, B → ... - store B to the Ath scratch space - Availability: v5 -## bnz target +## bnz -- Opcode: 0x40 {int16 branch offset, big-endian} +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} - Stack: ..., A: uint64 → ... - branch to TARGET if value A is not zero @@ -578,18 +607,20 @@ The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) -## bz target +## bz -- Opcode: 0x41 {int16 branch offset, big-endian} +- Syntax: `bz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x41 {int16 (big-endian)} - Stack: ..., A: uint64 → ... - branch to TARGET if value A is zero - Availability: v2 See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`. -## b target +## b -- Opcode: 0x42 {int16 branch offset, big-endian} +- Syntax: `b TARGET` ∋ TARGET: branch offset +- Bytecode: 0x42 {int16 (big-endian)} - Stack: ... → ... - branch unconditionally to TARGET - Availability: v2 @@ -598,119 +629,126 @@ See `bnz` for details on how branches work. `b` always jumps to the offset. ## return -- Opcode: 0x43 +- Bytecode: 0x43 - Stack: ..., A: uint64 → _exits_ - use A as success value; end - Availability: v2 ## assert -- Opcode: 0x44 +- Bytecode: 0x44 - Stack: ..., A: uint64 → ... - immediately fail unless A is a non-zero number - Availability: v3 -## bury n +## bury -- Opcode: 0x45 {uint8 depth} +- Syntax: `bury N` ∋ N: depth +- Bytecode: 0x45 {uint8} - Stack: ..., A → ... - replace the Nth value from the top of the stack with A. bury 0 fails. - Availability: v8 -## popn n +## popn -- Opcode: 0x46 {uint8 stack depth} +- Syntax: `popn N` ∋ N: stack depth +- Bytecode: 0x46 {uint8} - Stack: ..., [N items] → ... - remove N values from the top of the stack - Availability: v8 -## dupn n +## dupn -- Opcode: 0x47 {uint8 copy count} +- Syntax: `dupn N` ∋ N: copy count +- Bytecode: 0x47 {uint8} - Stack: ..., A → ..., A, [N copies of A] - duplicate A, N times - Availability: v8 ## pop -- Opcode: 0x48 +- Bytecode: 0x48 - Stack: ..., A → ... - discard A ## dup -- Opcode: 0x49 +- Bytecode: 0x49 - Stack: ..., A → ..., A, A - duplicate A ## dup2 -- Opcode: 0x4a +- Bytecode: 0x4a - Stack: ..., A, B → ..., A, B, A, B - duplicate A and B - Availability: v2 -## dig n +## dig -- Opcode: 0x4b {uint8 depth} +- Syntax: `dig N` ∋ N: depth +- Bytecode: 0x4b {uint8} - Stack: ..., A, [N items] → ..., A, [N items], A - Nth value from the top of the stack. dig 0 is equivalent to dup - Availability: v3 ## swap -- Opcode: 0x4c +- Bytecode: 0x4c - Stack: ..., A, B → ..., B, A - swaps A and B on stack - Availability: v3 ## select -- Opcode: 0x4d +- Bytecode: 0x4d - Stack: ..., A, B, C: uint64 → ..., A or B - selects one of two values based on top-of-stack: B if C != 0, else A - Availability: v3 -## cover n +## cover -- Opcode: 0x4e {uint8 depth} +- Syntax: `cover N` ∋ N: depth +- Bytecode: 0x4e {uint8} - Stack: ..., [N items], A → ..., A, [N items] - remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth <= N. - Availability: v5 -## uncover n +## uncover -- Opcode: 0x4f {uint8 depth} +- Syntax: `uncover N` ∋ N: depth +- Bytecode: 0x4f {uint8} - Stack: ..., A, [N items] → ..., [N items], A - remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth <= N. - Availability: v5 ## concat -- Opcode: 0x50 +- Bytecode: 0x50 - Stack: ..., A: []byte, B: []byte → ..., []byte - join A and B - Availability: v2 `concat` fails if the result would be greater than 4096 bytes. -## substring s e +## substring -- Opcode: 0x51 {uint8 start position} {uint8 end position} +- Syntax: `substring S E` ∋ S: start position, E: end position +- Bytecode: 0x51 {uint8}, {uint8} - Stack: ..., A: []byte → ..., []byte - A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails - Availability: v2 ## substring3 -- Opcode: 0x52 +- Bytecode: 0x52 - Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte - A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails - Availability: v2 ## getbit -- Opcode: 0x53 +- Bytecode: 0x53 - Stack: ..., A, B: uint64 → ..., uint64 - Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails - Availability: v3 @@ -719,7 +757,7 @@ see explanation of bit ordering in setbit ## setbit -- Opcode: 0x54 +- Bytecode: 0x54 - Stack: ..., A, B: uint64, C: uint64 → ..., any - Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails - Availability: v3 @@ -728,76 +766,81 @@ When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on ## getbyte -- Opcode: 0x55 +- Bytecode: 0x55 - Stack: ..., A: []byte, B: uint64 → ..., uint64 - Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails - Availability: v3 ## setbyte -- Opcode: 0x56 +- Bytecode: 0x56 - Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte - Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails - Availability: v3 -## extract s l +## extract -- Opcode: 0x57 {uint8 start position} {uint8 length} +- Syntax: `extract S L` ∋ S: start position, L: length +- Bytecode: 0x57 {uint8}, {uint8} - Stack: ..., A: []byte → ..., []byte - A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails - Availability: v5 ## extract3 -- Opcode: 0x58 +- Bytecode: 0x58 - Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte - A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails
`extract3` can be called using `extract` with no immediates. - Availability: v5 ## extract_uint16 -- Opcode: 0x59 +- Bytecode: 0x59 - Stack: ..., A: []byte, B: uint64 → ..., uint64 - A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails - Availability: v5 ## extract_uint32 -- Opcode: 0x5a +- Bytecode: 0x5a - Stack: ..., A: []byte, B: uint64 → ..., uint64 - A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails - Availability: v5 ## extract_uint64 -- Opcode: 0x5b +- Bytecode: 0x5b - Stack: ..., A: []byte, B: uint64 → ..., uint64 - A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails - Availability: v5 -## replace2 s +## replace2 -- Opcode: 0x5c {uint8 start position} +- Syntax: `replace2 S` ∋ S: start position +- Bytecode: 0x5c {uint8} - Stack: ..., A: []byte, B: []byte → ..., []byte - Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)
`replace2` can be called using `replace` with 1 immediate. - Availability: v7 ## replace3 -- Opcode: 0x5d +- Bytecode: 0x5d - Stack: ..., A: []byte, B: uint64, C: []byte → ..., []byte - Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)
`replace3` can be called using `replace` with no immediates. - Availability: v7 -## base64_decode e +## base64_decode -- Opcode: 0x5e {uint8 encoding index} +- Syntax: `base64_decode E` ∋ E: [base64](#field-group-base64) +- Bytecode: 0x5e {uint8} - Stack: ..., A: []byte → ..., []byte - decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E - **Cost**: 1 + 1 per 16 bytes of A - Availability: v7 -`base64` Encodings: +### Field Group base64 + +Encodings | Index | Name | Notes | | - | ------ | --------- | @@ -809,15 +852,18 @@ When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\n` and `\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\r`, or `\n`. -## json_ref r +## json_ref -- Opcode: 0x5f {uint8 return type index} +- Syntax: `json_ref R` ∋ R: [json_ref](#field-group-json_ref) +- Bytecode: 0x5f {uint8} - Stack: ..., A: []byte, B: []byte → ..., any - key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A - **Cost**: 25 + 2 per 7 bytes of A - Availability: v7 -`json_ref` Types: +### Field Group json_ref + +Types | Index | Name | Type | Notes | | - | ------ | -- | --------- | @@ -832,7 +878,7 @@ Almost all smart contracts should use simpler and smaller methods (such as the [ ## balance -- Opcode: 0x60 +- Bytecode: 0x60 - Stack: ..., A → ..., uint64 - balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit` - Availability: v2 @@ -842,7 +888,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), _ava ## app_opted_in -- Opcode: 0x61 +- Bytecode: 0x61 - Stack: ..., A, B: uint64 → ..., bool - 1 if account A is opted in to application B, else 0 - Availability: v2 @@ -852,7 +898,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), _ava ## app_local_get -- Opcode: 0x62 +- Bytecode: 0x62 - Stack: ..., A, B: key → ..., any - local state of the key B in the current application in account A - Availability: v2 @@ -862,7 +908,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_local_get_ex -- Opcode: 0x63 +- Bytecode: 0x63 - Stack: ..., A, B: uint64, C: key → ..., X: any, Y: bool - X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 - Availability: v2 @@ -872,7 +918,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), _ava ## app_global_get -- Opcode: 0x64 +- Bytecode: 0x64 - Stack: ..., A: key → ..., any - global state of the key A in the current application - Availability: v2 @@ -882,7 +928,7 @@ params: state key. Return: value. The value is zero (of type uint64) if the key ## app_global_get_ex -- Opcode: 0x65 +- Bytecode: 0x65 - Stack: ..., A: uint64, B: key → ..., X: any, Y: bool - X is the global state of application A, key B. Y is 1 if key existed, else 0 - Availability: v2 @@ -892,7 +938,7 @@ params: Txn.ForeignApps offset (or, since v4, an _available_ application id), st ## app_local_put -- Opcode: 0x66 +- Bytecode: 0x66 - Stack: ..., A, B: key, C → ... - write C to key B in account A's local state of the current application - Availability: v2 @@ -902,7 +948,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_global_put -- Opcode: 0x67 +- Bytecode: 0x67 - Stack: ..., A: key, B → ... - write B to key A in the global state of the current application - Availability: v2 @@ -910,7 +956,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_local_del -- Opcode: 0x68 +- Bytecode: 0x68 - Stack: ..., A, B: key → ... - delete key B from account A's local state of the current application - Availability: v2 @@ -922,7 +968,7 @@ Deleting a key which is already absent has no effect on the application local st ## app_global_del -- Opcode: 0x69 +- Bytecode: 0x69 - Stack: ..., A: key → ... - delete key A from the global state of the current application - Availability: v2 @@ -932,15 +978,18 @@ params: state key. Deleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.) -## asset_holding_get f +## asset_holding_get -- Opcode: 0x70 {uint8 asset holding field index} +- Syntax: `asset_holding_get F` ∋ F: [asset_holding](#field-group-asset_holding) +- Bytecode: 0x70 {uint8} - Stack: ..., A, B: uint64 → ..., X: any, Y: bool - X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 - Availability: v2 - Mode: Application -`asset_holding` Fields: +### Field Group asset_holding + +Fields | Index | Name | Type | Notes | | - | ------ | -- | --------- | @@ -950,15 +999,18 @@ Deleting a key which is already absent has no effect on the application global s params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. -## asset_params_get f +## asset_params_get -- Opcode: 0x71 {uint8 asset params field index} +- Syntax: `asset_params_get F` ∋ F: [asset_params](#field-group-asset_params) +- Bytecode: 0x71 {uint8} - Stack: ..., A: uint64 → ..., X: any, Y: bool - X is field F from asset A. Y is 1 if A exists, else 0 - Availability: v2 - Mode: Application -`asset_params` Fields: +### Field Group asset_params + +Fields | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | @@ -978,15 +1030,18 @@ params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. -## app_params_get f +## app_params_get -- Opcode: 0x72 {uint8 app params field index} +- Syntax: `app_params_get F` ∋ F: [app_params](#field-group-app_params) +- Bytecode: 0x72 {uint8} - Stack: ..., A: uint64 → ..., X: any, Y: bool - X is field F from app A. Y is 1 if A exists, else 0 - Availability: v5 - Mode: Application -`app_params` Fields: +### Field Group app_params + +Fields | Index | Name | Type | Notes | | - | ------ | -- | --------- | @@ -1003,15 +1058,18 @@ params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value. -## acct_params_get f +## acct_params_get -- Opcode: 0x73 {uint8 account params field index} +- Syntax: `acct_params_get F` ∋ F: [acct_params](#field-group-acct_params) +- Bytecode: 0x73 {uint8} - Stack: ..., A → ..., X: any, Y: bool - X is field F from account A. Y is 1 if A owns positive algos, else 0 - Availability: v6 - Mode: Application -`acct_params` Fields: +### Field Group acct_params + +Fields | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | @@ -1031,7 +1089,7 @@ params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag ## min_balance -- Opcode: 0x78 +- Bytecode: 0x78 - Stack: ..., A → ..., uint64 - minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change. - Availability: v3 @@ -1039,36 +1097,40 @@ params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. -## pushbytes bytes +## pushbytes -- Opcode: 0x80 {varuint length} {bytes} +- Syntax: `pushbytes BYTES` ∋ BYTES: a byte constant +- Bytecode: 0x80 {varuint length, bytes} - Stack: ... → ..., []byte - immediate BYTES - Availability: v3 pushbytes args are not added to the bytecblock during assembly processes -## pushint uint +## pushint -- Opcode: 0x81 {varuint int} +- Syntax: `pushint UINT` ∋ UINT: an int constant +- Bytecode: 0x81 {varuint} - Stack: ... → ..., uint64 - immediate UINT - Availability: v3 pushint args are not added to the intcblock during assembly processes -## pushbytess bytes ... +## pushbytess -- Opcode: 0x82 {varuint count} [({varuint length} bytes), ...] +- Syntax: `pushbytess BYTES ...` ∋ BYTES ...: a list of byte constants +- Bytecode: 0x82 {varuint count, [varuint length, bytes ...]} - Stack: ... → ..., [N items] - push sequences of immediate byte arrays to stack (first byte array being deepest) - Availability: v8 pushbytess args are not added to the bytecblock during assembly processes -## pushints uint ... +## pushints -- Opcode: 0x83 {varuint count} [{varuint value}, ...] +- Syntax: `pushints UINT ...` ∋ UINT ...: a list of int constants +- Bytecode: 0x83 {varuint count, [varuint ...]} - Stack: ... → ..., [N items] - push sequence of immediate uints to stack in the order they appear (first uint being deepest) - Availability: v8 @@ -1077,15 +1139,16 @@ pushints args are not added to the intcblock during assembly processes ## ed25519verify_bare -- Opcode: 0x84 +- Bytecode: 0x84 - Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool - for (data A, signature B, pubkey C) verify the signature of the data against the pubkey => {0 or 1} - **Cost**: 1900 - Availability: v7 -## callsub target +## callsub -- Opcode: 0x88 {int16 branch offset, big-endian} +- Syntax: `callsub TARGET` ∋ TARGET: branch offset +- Bytecode: 0x88 {int16 (big-endian)} - Stack: ... → ... - branch unconditionally to TARGET, saving the next instruction on the call stack - Availability: v4 @@ -1094,46 +1157,51 @@ The call stack is separate from the data stack. Only `callsub`, `retsub`, and `p ## retsub -- Opcode: 0x89 +- Bytecode: 0x89 - Stack: ... → ... - pop the top instruction from the call stack and branch to it - Availability: v4 If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values. -## proto a r +## proto -- Opcode: 0x8a {uint8 arguments} {uint8 return values} +- Syntax: `proto A R` ∋ A: number of arguments, R: number of return values +- Bytecode: 0x8a {uint8}, {uint8} - Stack: ... → ... - Prepare top call frame for a retsub that will assume A args and R return values. - Availability: v8 Fails unless the last instruction executed was a `callsub`. -## frame_dig i +## frame_dig -- Opcode: 0x8b {int8 frame slot} +- Syntax: `frame_dig I` ∋ I: frame slot +- Bytecode: 0x8b {int8} - Stack: ... → ..., any - Nth (signed) value from the frame pointer. - Availability: v8 -## frame_bury i +## frame_bury -- Opcode: 0x8c {int8 frame slot} +- Syntax: `frame_bury I` ∋ I: frame slot +- Bytecode: 0x8c {int8} - Stack: ..., A → ... - replace the Nth (signed) value from the frame pointer in the stack with A - Availability: v8 -## switch target ... +## switch -- Opcode: 0x8d {uint8 branch count} [{int16 branch offset, big-endian}, ...] +- Syntax: `switch TARGET ...` ∋ TARGET ...: list of labels +- Bytecode: 0x8d {varuint count, [int16 (big-endian) ...]} - Stack: ..., A: uint64 → ... - branch to the Ath label. Continue at following instruction if index A exceeds the number of labels. - Availability: v8 -## match target ... +## match -- Opcode: 0x8e {uint8 branch count} [{int16 branch offset, big-endian}, ...] +- Syntax: `match TARGET ...` ∋ TARGET ...: list of labels +- Bytecode: 0x8e {varuint count, [int16 (big-endian) ...]} - Stack: ..., [A1, A2, ..., AN], B → ... - given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found. - Availability: v8 @@ -1142,21 +1210,21 @@ Fails unless the last instruction executed was a `callsub`. ## shl -- Opcode: 0x90 +- Bytecode: 0x90 - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A times 2^B, modulo 2^64 - Availability: v4 ## shr -- Opcode: 0x91 +- Bytecode: 0x91 - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A divided by 2^B - Availability: v4 ## sqrt -- Opcode: 0x92 +- Bytecode: 0x92 - Stack: ..., A: uint64 → ..., uint64 - The largest integer I such that I^2 <= A - **Cost**: 4 @@ -1164,7 +1232,7 @@ Fails unless the last instruction executed was a `callsub`. ## bitlen -- Opcode: 0x93 +- Bytecode: 0x93 - Stack: ..., A → ..., uint64 - The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4 - Availability: v4 @@ -1173,14 +1241,14 @@ bitlen interprets arrays as big-endian integers, unlike setbit/getbit ## exp -- Opcode: 0x94 +- Bytecode: 0x94 - Stack: ..., A: uint64, B: uint64 → ..., uint64 - A raised to the Bth power. Fail if A == B == 0 and on overflow - Availability: v4 ## expw -- Opcode: 0x95 +- Bytecode: 0x95 - Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 - A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1 - **Cost**: 10 @@ -1188,7 +1256,7 @@ bitlen interprets arrays as big-endian integers, unlike setbit/getbit ## bsqrt -- Opcode: 0x96 +- Bytecode: 0x96 - Stack: ..., A: []byte → ..., []byte - The largest integer I such that I^2 <= A. A and I are interpreted as big-endian unsigned integers - **Cost**: 40 @@ -1196,7 +1264,7 @@ bitlen interprets arrays as big-endian integers, unlike setbit/getbit ## divw -- Opcode: 0x97 +- Bytecode: 0x97 - Stack: ..., A: uint64, B: uint64, C: uint64 → ..., uint64 - A,B / C. Fail if C == 0 or if result overflows. - Availability: v6 @@ -1205,7 +1273,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## sha3_256 -- Opcode: 0x98 +- Bytecode: 0x98 - Stack: ..., A: []byte → ..., []byte - SHA3_256 hash of value A, yields [32]byte - **Cost**: 130 @@ -1213,7 +1281,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b+ -- Opcode: 0xa0 +- Bytecode: 0xa0 - Stack: ..., A: bigint, B: bigint → ..., bigint - A plus B. A and B are interpreted as big-endian unsigned integers - **Cost**: 10 @@ -1221,7 +1289,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b- -- Opcode: 0xa1 +- Bytecode: 0xa1 - Stack: ..., A: bigint, B: bigint → ..., bigint - A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow. - **Cost**: 10 @@ -1229,7 +1297,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b/ -- Opcode: 0xa2 +- Bytecode: 0xa2 - Stack: ..., A: bigint, B: bigint → ..., bigint - A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero. - **Cost**: 20 @@ -1237,7 +1305,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b* -- Opcode: 0xa3 +- Bytecode: 0xa3 - Stack: ..., A: bigint, B: bigint → ..., bigint - A times B. A and B are interpreted as big-endian unsigned integers. - **Cost**: 20 @@ -1245,49 +1313,49 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b< -- Opcode: 0xa4 +- Bytecode: 0xa4 - Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b> -- Opcode: 0xa5 +- Bytecode: 0xa5 - Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b<= -- Opcode: 0xa6 +- Bytecode: 0xa6 - Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b>= -- Opcode: 0xa7 +- Bytecode: 0xa7 - Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b== -- Opcode: 0xa8 +- Bytecode: 0xa8 - Stack: ..., A: bigint, B: bigint → ..., bool - 1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b!= -- Opcode: 0xa9 +- Bytecode: 0xa9 - Stack: ..., A: bigint, B: bigint → ..., bool - 0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers - Availability: v4 ## b% -- Opcode: 0xaa +- Bytecode: 0xaa - Stack: ..., A: []byte, B: []byte → ..., []byte - A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero. - **Cost**: 20 @@ -1295,7 +1363,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b| -- Opcode: 0xab +- Bytecode: 0xab - Stack: ..., A: []byte, B: []byte → ..., []byte - A bitwise-or B. A and B are zero-left extended to the greater of their lengths - **Cost**: 6 @@ -1303,7 +1371,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b& -- Opcode: 0xac +- Bytecode: 0xac - Stack: ..., A: []byte, B: []byte → ..., []byte - A bitwise-and B. A and B are zero-left extended to the greater of their lengths - **Cost**: 6 @@ -1311,7 +1379,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b^ -- Opcode: 0xad +- Bytecode: 0xad - Stack: ..., A: []byte, B: []byte → ..., []byte - A bitwise-xor B. A and B are zero-left extended to the greater of their lengths - **Cost**: 6 @@ -1319,7 +1387,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b~ -- Opcode: 0xae +- Bytecode: 0xae - Stack: ..., A: []byte → ..., []byte - A with all bits inverted - **Cost**: 4 @@ -1327,14 +1395,14 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## bzero -- Opcode: 0xaf +- Bytecode: 0xaf - Stack: ..., A: uint64 → ..., []byte - zero filled byte-array of length A - Availability: v4 ## log -- Opcode: 0xb0 +- Bytecode: 0xb0 - Stack: ..., A: []byte → ... - write A to log state of the current application - Availability: v5 @@ -1344,7 +1412,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## itxn_begin -- Opcode: 0xb1 +- Bytecode: 0xb1 - Stack: ... → ... - begin preparation of a new inner transaction in a new transaction group - Availability: v5 @@ -1352,9 +1420,10 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with `itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values. -## itxn_field f +## itxn_field -- Opcode: 0xb2 {uint8 transaction field index} +- Syntax: `itxn_field F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb2 {uint8} - Stack: ..., A → ... - set field F of the current inner transaction to A - Availability: v5 @@ -1364,7 +1433,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## itxn_submit -- Opcode: 0xb3 +- Bytecode: 0xb3 - Stack: ... → ... - execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails. - Availability: v5 @@ -1372,17 +1441,19 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with `itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction. -## itxn f +## itxn -- Opcode: 0xb4 {uint8 transaction field index} +- Syntax: `itxn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb4 {uint8} - Stack: ... → ..., any - field F of the last inner transaction - Availability: v5 - Mode: Application -## itxna f i +## itxna -- Opcode: 0xb5 {uint8 transaction field index} {uint8 transaction field array index} +- Syntax: `itxna F I` ∋ F: [txna](#field-group-txna), I: a transaction field array index +- Bytecode: 0xb5 {uint8}, {uint8} - Stack: ... → ..., any - Ith value of the array field F of the last inner transaction - Availability: v5 @@ -1390,7 +1461,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## itxn_next -- Opcode: 0xb6 +- Bytecode: 0xb6 - Stack: ... → ... - begin preparation of a new inner transaction in the same transaction group - Availability: v6 @@ -1398,17 +1469,19 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with `itxn_next` initializes the transaction exactly as `itxn_begin` does -## gitxn t f +## gitxn -- Opcode: 0xb7 {uint8 transaction group index} {uint8 transaction field index} +- Syntax: `gitxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0xb7 {uint8}, {uint8} - Stack: ... → ..., any - field F of the Tth transaction in the last inner group submitted - Availability: v6 - Mode: Application -## gitxna t f i +## gitxna -- Opcode: 0xb8 {uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index} +- Syntax: `gitxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0xb8 {uint8}, {uint8}, {uint8} - Stack: ... → ..., any - Ith value of the array field F from the Tth transaction in the last inner group submitted - Availability: v6 @@ -1416,7 +1489,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## box_create -- Opcode: 0xb9 +- Bytecode: 0xb9 - Stack: ..., A: key, B: uint64 → ..., bool - create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1 - Availability: v8 @@ -1426,7 +1499,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_extract -- Opcode: 0xba +- Bytecode: 0xba - Stack: ..., A: key, B: uint64, C: uint64 → ..., []byte - read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. - Availability: v8 @@ -1434,7 +1507,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_replace -- Opcode: 0xbb +- Bytecode: 0xbb - Stack: ..., A: key, B: uint64, C: []byte → ... - write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. - Availability: v8 @@ -1442,7 +1515,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_del -- Opcode: 0xbc +- Bytecode: 0xbc - Stack: ..., A: key → ..., bool - delete box named A if it exists. Return 1 if A existed, 0 otherwise - Availability: v8 @@ -1450,7 +1523,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_len -- Opcode: 0xbd +- Bytecode: 0xbd - Stack: ..., A: key → ..., X: uint64, Y: bool - X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0. - Availability: v8 @@ -1458,7 +1531,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_get -- Opcode: 0xbe +- Bytecode: 0xbe - Stack: ..., A: key → ..., X: []byte, Y: bool - X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0. - Availability: v8 @@ -1468,7 +1541,7 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo ## box_put -- Opcode: 0xbf +- Bytecode: 0xbf - Stack: ..., A: key, B: []byte → ... - replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist - Availability: v8 @@ -1476,30 +1549,33 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace` -## txnas f +## txnas -- Opcode: 0xc0 {uint8 transaction field index} +- Syntax: `txnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc0 {uint8} - Stack: ..., A: uint64 → ..., any - Ath value of the array field F of the current transaction - Availability: v5 -## gtxnas t f +## gtxnas -- Opcode: 0xc1 {uint8 transaction group index} {uint8 transaction field index} +- Syntax: `gtxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc1 {uint8}, {uint8} - Stack: ..., A: uint64 → ..., any - Ath value of the array field F from the Tth transaction in the current group - Availability: v5 -## gtxnsas f +## gtxnsas -- Opcode: 0xc2 {uint8 transaction field index} +- Syntax: `gtxnsas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc2 {uint8} - Stack: ..., A: uint64, B: uint64 → ..., any - Bth value of the array field F from the Ath transaction in the current group - Availability: v5 ## args -- Opcode: 0xc3 +- Bytecode: 0xc3 - Stack: ..., A: uint64 → ..., []byte - Ath LogicSig argument - Availability: v5 @@ -1507,37 +1583,42 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo ## gloadss -- Opcode: 0xc4 +- Bytecode: 0xc4 - Stack: ..., A: uint64, B: uint64 → ..., any - Bth scratch space value of the Ath transaction in the current group - Availability: v6 - Mode: Application -## itxnas f +## itxnas -- Opcode: 0xc5 {uint8 transaction field index} +- Syntax: `itxnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc5 {uint8} - Stack: ..., A: uint64 → ..., any - Ath value of the array field F of the last inner transaction - Availability: v6 - Mode: Application -## gitxnas t f +## gitxnas -- Opcode: 0xc6 {uint8 transaction group index} {uint8 transaction field index} +- Syntax: `gitxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc6 {uint8}, {uint8} - Stack: ..., A: uint64 → ..., any - Ath value of the array field F from the Tth transaction in the last inner group submitted - Availability: v6 - Mode: Application -## vrf_verify s +## vrf_verify -- Opcode: 0xd0 {uint8 parameters index} +- Syntax: `vrf_verify S` ∋ S: [vrf_verify](#field-group-vrf_verify) +- Bytecode: 0xd0 {uint8} - Stack: ..., A: []byte, B: []byte, C: []byte → ..., X: []byte, Y: bool - Verify the proof B of message A against pubkey C. Returns vrf output and verification flag. - **Cost**: 5700 - Availability: v7 -`vrf_verify` Standards: +### Field Group vrf_verify + +Standards | Index | Name | Notes | | - | ------ | --------- | @@ -1546,14 +1627,17 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo `VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/). -## block f +## block -- Opcode: 0xd1 {uint8 block field index} +- Syntax: `block F` ∋ F: [block](#field-group-block) +- Bytecode: 0xd1 {uint8} - Stack: ..., A: uint64 → ..., any - field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive) - Availability: v7 -`block` Fields: +### Field Group block + +Fields | Index | Name | Type | Notes | | - | ------ | -- | --------- | diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index 31a0412fa3..a0e6513c6e 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -17,6 +17,8 @@ package logic import ( + "strings" + "github.com/algorand/go-algorand/protocol" ) @@ -219,84 +221,113 @@ func OpDoc(opName string) string { return opDocByName[opName] } -var opcodeImmediateNotes = map[string]string{ - "intcblock": "{varuint count} [{varuint value}, ...]", - "intc": "{uint8 int constant index}", - "pushint": "{varuint int}", - "pushints": "{varuint count} [{varuint value}, ...]", - "bytecblock": "{varuint count} [({varuint length} bytes), ...]", - "bytec": "{uint8 byte constant index}", - "pushbytes": "{varuint length} {bytes}", - "pushbytess": "{varuint count} [({varuint length} bytes), ...]", - - "arg": "{uint8 arg index}", - "global": "{uint8 global field index}", - - "txn": "{uint8 transaction field index}", - "gtxn": "{uint8 transaction group index} {uint8 transaction field index}", - "gtxns": "{uint8 transaction field index}", - "txna": "{uint8 transaction field index} {uint8 transaction field array index}", - "gtxna": "{uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index}", - "gtxnsa": "{uint8 transaction field index} {uint8 transaction field array index}", - "txnas": "{uint8 transaction field index}", - "gtxnas": "{uint8 transaction group index} {uint8 transaction field index}", - "gtxnsas": "{uint8 transaction field index}", - - "bnz": "{int16 branch offset, big-endian}", - "bz": "{int16 branch offset, big-endian}", - "b": "{int16 branch offset, big-endian}", - "callsub": "{int16 branch offset, big-endian}", - - "load": "{uint8 position in scratch space to load from}", - "store": "{uint8 position in scratch space to store to}", - "gload": "{uint8 transaction group index} {uint8 position in scratch space to load from}", - "gloads": "{uint8 position in scratch space to load from}", - "gaid": "{uint8 transaction group index}", - - "substring": "{uint8 start position} {uint8 end position}", - "extract": "{uint8 start position} {uint8 length}", - "replace2": "{uint8 start position}", - "dig": "{uint8 depth}", - "bury": "{uint8 depth}", - "cover": "{uint8 depth}", - "uncover": "{uint8 depth}", - - "asset_holding_get": "{uint8 asset holding field index}", - "asset_params_get": "{uint8 asset params field index}", - "app_params_get": "{uint8 app params field index}", - "acct_params_get": "{uint8 account params field index}", - - "itxn_field": "{uint8 transaction field index}", - "itxn": "{uint8 transaction field index}", - "itxna": "{uint8 transaction field index} {uint8 transaction field array index}", - "itxnas": "{uint8 transaction field index}", - "gitxn": "{uint8 transaction group index} {uint8 transaction field index}", - "gitxna": "{uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index}", - "gitxnas": "{uint8 transaction group index} {uint8 transaction field index}", - - "ecdsa_verify": "{uint8 curve index}", - "ecdsa_pk_decompress": "{uint8 curve index}", - "ecdsa_pk_recover": "{uint8 curve index}", - - "base64_decode": "{uint8 encoding index}", - "json_ref": "{uint8 return type index}", - - "vrf_verify": "{uint8 parameters index}", - "block": "{uint8 block field index}", - - "switch": "{uint8 branch count} [{int16 branch offset, big-endian}, ...]", - "match": "{uint8 branch count} [{int16 branch offset, big-endian}, ...]", - - "proto": "{uint8 arguments} {uint8 return values}", - "frame_dig": "{int8 frame slot}", - "frame_bury": "{int8 frame slot}", - "popn": "{uint8 stack depth}", - "dupn": "{uint8 copy count}", +var opcodeImmediateNotes = map[string][]string{ + "intcblock": {"a block of int constant values"}, + "intc": {"an index in the intcblock"}, + "pushint": {"an int constant"}, + "pushints": {"a list of int constants"}, + "bytecblock": {"a block of byte const values"}, + "bytec": {"an index in the bytec block"}, + "pushbytes": {"a byte constant"}, + "pushbytess": {"a list of byte constants"}, + + "arg": {"an arg index"}, + "global": {"a global field index"}, + + "txn": {"transaction field index"}, + "gtxn": {"transaction group index", "transaction field index"}, + "gtxns": {"transaction field index"}, + "txna": {"transaction field index", "transaction field array index"}, + "gtxna": {"transaction group index", "transaction field index", "transaction field array index"}, + "gtxnsa": {"transaction field index", "transaction field array index"}, + "txnas": {"transaction field index"}, + "gtxnas": {"transaction group index", "transaction field index"}, + "gtxnsas": {"transaction field index"}, + + "bnz": {"branch offset"}, + "bz": {"branch offset"}, + "b": {"branch offset"}, + "callsub": {"branch offset"}, + + "load": {"position in scratch space to load from"}, + "store": {"position in scratch space to store to"}, + "gload": {"transaction group index", "position in scratch space to load from"}, + "gloads": {"position in scratch space to load from"}, + "gaid": {"transaction group index"}, + + "substring": {"start position", "end position"}, + "extract": {"start position", "length"}, + "replace2": {"start position"}, + "dig": {"depth"}, + "bury": {"depth"}, + "cover": {"depth"}, + "uncover": {"depth"}, + + "asset_holding_get": {"asset holding field index"}, + "asset_params_get": {"asset params field index"}, + "app_params_get": {"app params field index"}, + "acct_params_get": {"account params field index"}, + + "itxn_field": {"transaction field index"}, + "itxn": {"transaction field index"}, + "itxna": {"transaction field index", "a transaction field array index"}, + "itxnas": {"transaction field index"}, + "gitxn": {"transaction group index", "transaction field index"}, + "gitxna": {"transaction group index", "transaction field index", "transaction field array index"}, + "gitxnas": {"transaction group index", "transaction field index"}, + + "ecdsa_verify": {"curve index"}, + "ecdsa_pk_decompress": {"curve index"}, + "ecdsa_pk_recover": {"curve index"}, + + "base64_decode": {"encoding index"}, + "json_ref": {"return type index"}, + + "vrf_verify": {" parameters index"}, + "block": {" block field index"}, + + "switch": {"list of labels"}, + "match": {"list of labels"}, + + "proto": {"number of arguments", "number of return values"}, + "frame_dig": {"frame slot"}, + "frame_bury": {"frame slot"}, + "popn": {"stack depth"}, + "dupn": {"copy count"}, +} + +// OpImmediateDetails contains information about the an immediate argument for +// a given opcode, combining OpSpec details with the extra note in +// the opcodeImmediateNotes map +type OpImmediateDetails struct { + Comment string `json:",omitempty"` + Encoding string `json:",omitempty"` + Name string `json:",omitempty"` + Reference string `json:",omitempty"` } -// OpImmediateNote returns a short string about immediate data which follows the op byte -func OpImmediateNote(opName string) string { - return opcodeImmediateNotes[opName] +// OpImmediateDetailsFromSpec provides a slice of OpImmediateDetails +// for a given OpSpec +func OpImmediateDetailsFromSpec(spec OpSpec) []OpImmediateDetails { + argNotes := opcodeImmediateNotes[spec.Name] + if len(argNotes) == 0 { + return nil + } + + details := make([]OpImmediateDetails, len(spec.Immediates)) + for idx, imm := range spec.Immediates { + details[idx] = OpImmediateDetails{ + Name: strings.ToTitle(imm.Name), + Comment: argNotes[idx], + Encoding: imm.kind.String(), + } + + if imm.Group != nil { + details[idx].Reference = imm.Group.Name + } + } + + return details } // further documentation on the function of the opcode diff --git a/data/transactions/logic/doc_test.go b/data/transactions/logic/doc_test.go index 8afca4520f..d1006f2ef7 100644 --- a/data/transactions/logic/doc_test.go +++ b/data/transactions/logic/doc_test.go @@ -98,43 +98,23 @@ func TestOpDoc(t *testing.T) { require.Empty(t, xd) } -func TestOpImmediateNote(t *testing.T) { +func TestOpImmediateDetails(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - xd := OpImmediateNote("txn") - require.NotEmpty(t, xd) - xd = OpImmediateNote("+") - require.Empty(t, xd) -} + for _, os := range OpSpecs { + deets := OpImmediateDetailsFromSpec(os) + require.Equal(t, len(os.Immediates), len(deets)) -func TestAllImmediatesDocumented(t *testing.T) { - partitiontest.PartitionTest(t) - t.Parallel() + for idx, d := range deets { + imm := os.Immediates[idx] + require.NotEmpty(t, d.Comment) + require.Equal(t, strings.ToLower(d.Name), imm.Name) + require.Equal(t, d.Encoding, imm.kind.String()) - for _, op := range OpSpecs { - count := len(op.Immediates) - note := OpImmediateNote(op.Name) - if count == 1 && op.Immediates[0].kind >= immBytes { - // More elaborate than can be checked by easy count. - assert.NotEmpty(t, note) - continue - } - assert.Equal(t, count, strings.Count(note, "{"), "opcodeImmediateNotes for %s is wrong", op.Name) - assert.Equal(t, count, strings.Count(note, "}"), "opcodeImmediateNotes for %s is wrong", op.Name) - for _, imm := range op.Immediates { - switch imm.kind { - case immByte: - require.True(t, strings.HasPrefix(note, "{uint8 "), "%v %v", op.Name, note) - case immInt8: - require.True(t, strings.HasPrefix(note, "{int8 "), "%v %v", op.Name, note) - case immLabel: - require.True(t, strings.HasPrefix(note, "{int16 "), "%v %v", op.Name, note) - case immInt: - require.True(t, strings.HasPrefix(note, "{varuint "), "%v %v", op.Name, note) + if imm.Group != nil { + require.Equal(t, d.Reference, imm.Group.Name) } - close := strings.Index(note, "}") - note = strings.TrimPrefix(note[close+1:], " ") } } } diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index 7d20a868ef..80b46fcec0 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -106,7 +106,14 @@ ], "Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}", "DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", - "ImmediateNote": "{uint8 curve index}", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], "IntroducedVersion": 5, "Groups": [ "Arithmetic" @@ -133,7 +140,14 @@ ], "Doc": "decompress pubkey A into components X, Y", "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", - "ImmediateNote": "{uint8 curve index}", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], "IntroducedVersion": 5, "Groups": [ "Arithmetic" @@ -163,7 +177,14 @@ ], "Doc": "for (data A, recovery id B, signature C, D) recover a public key", "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", - "ImmediateNote": "{uint8 curve index}", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], "IntroducedVersion": 5, "Groups": [ "Arithmetic" @@ -590,7 +611,13 @@ "Size": 0, "Doc": "prepare block of uint64 constants for use by intc", "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", - "ImmediateNote": "{varuint count} [{varuint value}, ...]", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -604,7 +631,13 @@ ], "Size": 2, "Doc": "Ith constant from intcblock", - "ImmediateNote": "{uint8 int constant index}", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -668,7 +701,13 @@ "Size": 0, "Doc": "prepare block of byte-array constants for use by bytec", "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", - "ImmediateNote": "{varuint count} [({varuint length} bytes), ...]", + "ImmediateNote": [ + { + "Comment": "a block of byte const values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -682,7 +721,13 @@ ], "Size": 2, "Doc": "Ith constant from bytecblock", - "ImmediateNote": "{uint8 byte constant index}", + "ImmediateNote": [ + { + "Comment": "an index in the bytec block", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -748,7 +793,13 @@ ], "Size": 2, "Doc": "Nth LogicSig argument", - "ImmediateNote": "{uint8 arg index}", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -954,7 +1005,14 @@ "uint64" ], "Doc": "field F of current transaction", - "ImmediateNote": "{uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -1002,7 +1060,14 @@ "addr" ], "Doc": "global field F", - "ImmediateNote": "{uint8 global field index}", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -1157,7 +1222,19 @@ ], "Doc": "field F of the Tth transaction in the current group", "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", - "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -1171,7 +1248,13 @@ ], "Size": 2, "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", - "ImmediateNote": "{uint8 position in scratch space to load from}", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -1185,7 +1268,13 @@ ], "Size": 2, "Doc": "store A to the Ith scratch space", - "ImmediateNote": "{uint8 position in scratch space to store to}", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 1, "Groups": [ "Loading Values" @@ -1217,7 +1306,19 @@ "[]byte" ], "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", - "ImmediateNote": "{uint8 transaction field index} {uint8 transaction field array index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 2, "Groups": [ "Loading Values" @@ -1249,7 +1350,24 @@ "[]byte" ], "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", - "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index}", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 2, "Groups": [ "Loading Values" @@ -1407,7 +1525,14 @@ ], "Doc": "field F of the Ath transaction in the current group", "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", - "ImmediateNote": "{uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], "IntroducedVersion": 3, "Groups": [ "Loading Values" @@ -1442,7 +1567,19 @@ "[]byte" ], "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", - "ImmediateNote": "{uint8 transaction field index} {uint8 transaction field array index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 3, "Groups": [ "Loading Values" @@ -1457,7 +1594,18 @@ "Size": 3, "Doc": "Ith scratch space value of the Tth transaction in the current group", "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.", - "ImmediateNote": "{uint8 transaction group index} {uint8 position in scratch space to load from}", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 4, "Groups": [ "Loading Values" @@ -1475,7 +1623,13 @@ "Size": 2, "Doc": "Ith scratch space value of the Ath transaction in the current group", "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.", - "ImmediateNote": "{uint8 position in scratch space to load from}", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 4, "Groups": [ "Loading Values" @@ -1490,7 +1644,13 @@ "Size": 2, "Doc": "ID of the asset or application created in the Tth transaction of the current group", "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.", - "ImmediateNote": "{uint8 transaction group index}", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + } + ], "IntroducedVersion": 4, "Groups": [ "Loading Values" @@ -1552,7 +1712,13 @@ "Size": 3, "Doc": "branch to TARGET if value A is not zero", "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", - "ImmediateNote": "{int16 branch offset, big-endian}", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], "IntroducedVersion": 1, "Groups": [ "Flow Control" @@ -1567,7 +1733,13 @@ "Size": 3, "Doc": "branch to TARGET if value A is zero", "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", - "ImmediateNote": "{int16 branch offset, big-endian}", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], "IntroducedVersion": 2, "Groups": [ "Flow Control" @@ -1579,7 +1751,13 @@ "Size": 3, "Doc": "branch unconditionally to TARGET", "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", - "ImmediateNote": "{int16 branch offset, big-endian}", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], "IntroducedVersion": 2, "Groups": [ "Flow Control" @@ -1622,7 +1800,13 @@ ], "Size": 2, "Doc": "replace the Nth value from the top of the stack with A. bury 0 fails.", - "ImmediateNote": "{uint8 depth}", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], "IntroducedVersion": 8, "Groups": [ "Flow Control" @@ -1633,7 +1817,13 @@ "Name": "popn", "Size": 2, "Doc": "remove N values from the top of the stack", - "ImmediateNote": "{uint8 stack depth}", + "ImmediateNote": [ + { + "Comment": "stack depth", + "Encoding": "uint8", + "Name": "N" + } + ], "IntroducedVersion": 8, "Groups": [ "Flow Control" @@ -1647,7 +1837,13 @@ ], "Size": 2, "Doc": "duplicate A, N times", - "ImmediateNote": "{uint8 copy count}", + "ImmediateNote": [ + { + "Comment": "copy count", + "Encoding": "uint8", + "Name": "N" + } + ], "IntroducedVersion": 8, "Groups": [ "Flow Control" @@ -1715,7 +1911,13 @@ ], "Size": 2, "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", - "ImmediateNote": "{uint8 depth}", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], "IntroducedVersion": 3, "Groups": [ "Flow Control" @@ -1768,7 +1970,13 @@ ], "Size": 2, "Doc": "remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth \u003c= N.", - "ImmediateNote": "{uint8 depth}", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], "IntroducedVersion": 5, "Groups": [ "Flow Control" @@ -1785,7 +1993,13 @@ ], "Size": 2, "Doc": "remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth \u003c= N.", - "ImmediateNote": "{uint8 depth}", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], "IntroducedVersion": 5, "Groups": [ "Flow Control" @@ -1820,7 +2034,18 @@ ], "Size": 3, "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", - "ImmediateNote": "{uint8 start position} {uint8 end position}", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "end position", + "Encoding": "uint8", + "Name": "E" + } + ], "IntroducedVersion": 2, "Groups": [ "Byte Array Manipulation" @@ -1927,7 +2152,18 @@ ], "Size": 3, "Doc": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", - "ImmediateNote": "{uint8 start position} {uint8 length}", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "length", + "Encoding": "uint8", + "Name": "L" + } + ], "IntroducedVersion": 5, "Groups": [ "Byte Array Manipulation" @@ -2014,7 +2250,13 @@ ], "Size": 2, "Doc": "Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)\n`replace2` can be called using `replace` with 1 immediate.", - "ImmediateNote": "{uint8 start position}", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + } + ], "IntroducedVersion": 7, "Groups": [ "Byte Array Manipulation" @@ -2058,7 +2300,14 @@ ], "Doc": "decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E", "DocExtra": "*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings.\tThis opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings.\n\n Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.", - "ImmediateNote": "{uint8 encoding index}", + "ImmediateNote": [ + { + "Comment": "encoding index", + "Encoding": "uint8", + "Name": "E", + "Reference": "base64" + } + ], "IntroducedVersion": 7, "Groups": [ "Byte Array Manipulation" @@ -2087,7 +2336,14 @@ ], "Doc": "key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A", "DocExtra": "*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size.\n\nAlmost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON.", - "ImmediateNote": "{uint8 return type index}", + "ImmediateNote": [ + { + "Comment": "return type index", + "Encoding": "uint8", + "Name": "R", + "Reference": "json_ref" + } + ], "IntroducedVersion": 7, "Groups": [ "Byte Array Manipulation" @@ -2283,7 +2539,14 @@ ], "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", - "ImmediateNote": "{uint8 asset holding field index}", + "ImmediateNote": [ + { + "Comment": "asset holding field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_holding" + } + ], "IntroducedVersion": 2, "Groups": [ "State Access" @@ -2330,7 +2593,14 @@ ], "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", - "ImmediateNote": "{uint8 asset params field index}", + "ImmediateNote": [ + { + "Comment": "asset params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_params" + } + ], "IntroducedVersion": 2, "Groups": [ "State Access" @@ -2371,7 +2641,14 @@ ], "Doc": "X is field F from app A. Y is 1 if A exists, else 0", "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", - "ImmediateNote": "{uint8 app params field index}", + "ImmediateNote": [ + { + "Comment": "app params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "app_params" + } + ], "IntroducedVersion": 5, "Groups": [ "State Access" @@ -2417,7 +2694,14 @@ "uint64" ], "Doc": "X is field F from account A. Y is 1 if A owns positive algos, else 0", - "ImmediateNote": "{uint8 account params field index}", + "ImmediateNote": [ + { + "Comment": "account params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "acct_params" + } + ], "IntroducedVersion": 6, "Groups": [ "State Access" @@ -2449,7 +2733,13 @@ "Size": 0, "Doc": "immediate BYTES", "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", - "ImmediateNote": "{varuint length} {bytes}", + "ImmediateNote": [ + { + "Comment": "a byte constant", + "Encoding": "varuint length, bytes", + "Name": "BYTES" + } + ], "IntroducedVersion": 3, "Groups": [ "Loading Values" @@ -2464,7 +2754,13 @@ "Size": 0, "Doc": "immediate UINT", "DocExtra": "pushint args are not added to the intcblock during assembly processes", - "ImmediateNote": "{varuint int}", + "ImmediateNote": [ + { + "Comment": "an int constant", + "Encoding": "varuint", + "Name": "UINT" + } + ], "IntroducedVersion": 3, "Groups": [ "Loading Values" @@ -2476,7 +2772,13 @@ "Size": 0, "Doc": "push sequences of immediate byte arrays to stack (first byte array being deepest)", "DocExtra": "pushbytess args are not added to the bytecblock during assembly processes", - "ImmediateNote": "{varuint count} [({varuint length} bytes), ...]", + "ImmediateNote": [ + { + "Comment": "a list of byte constants", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], "IntroducedVersion": 8, "Groups": [ "Loading Values" @@ -2488,7 +2790,13 @@ "Size": 0, "Doc": "push sequence of immediate uints to stack in the order they appear (first uint being deepest)", "DocExtra": "pushints args are not added to the intcblock during assembly processes", - "ImmediateNote": "{varuint count} [{varuint value}, ...]", + "ImmediateNote": [ + { + "Comment": "a list of int constants", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], "IntroducedVersion": 8, "Groups": [ "Loading Values" @@ -2518,7 +2826,13 @@ "Size": 3, "Doc": "branch unconditionally to TARGET, saving the next instruction on the call stack", "DocExtra": "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", - "ImmediateNote": "{int16 branch offset, big-endian}", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], "IntroducedVersion": 4, "Groups": [ "Flow Control" @@ -2541,7 +2855,18 @@ "Size": 3, "Doc": "Prepare top call frame for a retsub that will assume A args and R return values.", "DocExtra": "Fails unless the last instruction executed was a `callsub`.", - "ImmediateNote": "{uint8 arguments} {uint8 return values}", + "ImmediateNote": [ + { + "Comment": "number of arguments", + "Encoding": "uint8", + "Name": "A" + }, + { + "Comment": "number of return values", + "Encoding": "uint8", + "Name": "R" + } + ], "IntroducedVersion": 8, "Groups": [ "Flow Control" @@ -2555,7 +2880,13 @@ ], "Size": 2, "Doc": "Nth (signed) value from the frame pointer.", - "ImmediateNote": "{int8 frame slot}", + "ImmediateNote": [ + { + "Comment": "frame slot", + "Encoding": "int8", + "Name": "I" + } + ], "IntroducedVersion": 8, "Groups": [ "Flow Control" @@ -2569,7 +2900,13 @@ ], "Size": 2, "Doc": "replace the Nth (signed) value from the frame pointer in the stack with A", - "ImmediateNote": "{int8 frame slot}", + "ImmediateNote": [ + { + "Comment": "frame slot", + "Encoding": "int8", + "Name": "I" + } + ], "IntroducedVersion": 8, "Groups": [ "Flow Control" @@ -2583,7 +2920,13 @@ ], "Size": 0, "Doc": "branch to the Ath label. Continue at following instruction if index A exceeds the number of labels.", - "ImmediateNote": "{uint8 branch count} [{int16 branch offset, big-endian}, ...]", + "ImmediateNote": [ + { + "Comment": "list of labels", + "Encoding": "varuint count, [int16 (big-endian) ...]", + "Name": "TARGET ..." + } + ], "IntroducedVersion": 8, "Groups": [ "Flow Control" @@ -2595,7 +2938,13 @@ "Size": 0, "Doc": "given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found.", "DocExtra": "`match` consumes N+1 values from the stack. Let the top stack value be B. The following N values represent an ordered list of match cases/constants (A), where the first value (A[0]) is the deepest in the stack. The immediate arguments are an ordered list of N labels (T). `match` will branch to target T[I], where A[I] = B. If there are no matches then execution continues on to the next instruction.", - "ImmediateNote": "{uint8 branch count} [{int16 branch offset, big-endian}, ...]", + "ImmediateNote": [ + { + "Comment": "list of labels", + "Encoding": "varuint count, [int16 (big-endian) ...]", + "Name": "TARGET ..." + } + ], "IntroducedVersion": 8, "Groups": [ "Flow Control" @@ -3164,7 +3513,14 @@ ], "Doc": "set field F of the current inner transaction to A", "DocExtra": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", - "ImmediateNote": "{uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], "IntroducedVersion": 5, "Groups": [ "Inner Transactions" @@ -3329,7 +3685,14 @@ "uint64" ], "Doc": "field F of the last inner transaction", - "ImmediateNote": "{uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], "IntroducedVersion": 5, "Groups": [ "Inner Transactions" @@ -3361,7 +3724,19 @@ "[]byte" ], "Doc": "Ith value of the array field F of the last inner transaction", - "ImmediateNote": "{uint8 transaction field index} {uint8 transaction field array index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "a transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 5, "Groups": [ "Inner Transactions" @@ -3526,7 +3901,19 @@ "uint64" ], "Doc": "field F of the Tth transaction in the last inner group submitted", - "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], "IntroducedVersion": 6, "Groups": [ "Inner Transactions" @@ -3558,7 +3945,24 @@ "[]byte" ], "Doc": "Ith value of the array field F from the Tth transaction in the last inner group submitted", - "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index} {uint8 transaction field array index}", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], "IntroducedVersion": 6, "Groups": [ "Inner Transactions" @@ -3710,7 +4114,14 @@ "[]byte" ], "Doc": "Ath value of the array field F of the current transaction", - "ImmediateNote": "{uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], "IntroducedVersion": 5, "Groups": [ "Loading Values" @@ -3745,7 +4156,19 @@ "[]byte" ], "Doc": "Ath value of the array field F from the Tth transaction in the current group", - "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], "IntroducedVersion": 5, "Groups": [ "Loading Values" @@ -3781,7 +4204,14 @@ "[]byte" ], "Doc": "Bth value of the array field F from the Ath transaction in the current group", - "ImmediateNote": "{uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], "IntroducedVersion": 5, "Groups": [ "Loading Values" @@ -3831,7 +4261,14 @@ ], "Size": 2, "Doc": "Ath value of the array field F of the last inner transaction", - "ImmediateNote": "{uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], "IntroducedVersion": 6, "Groups": [ "Inner Transactions" @@ -3848,7 +4285,19 @@ ], "Size": 3, "Doc": "Ath value of the array field F from the Tth transaction in the last inner group submitted", - "ImmediateNote": "{uint8 transaction group index} {uint8 transaction field index}", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], "IntroducedVersion": 6, "Groups": [ "Inner Transactions" @@ -3875,7 +4324,14 @@ ], "Doc": "Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.", "DocExtra": "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).", - "ImmediateNote": "{uint8 parameters index}", + "ImmediateNote": [ + { + "Comment": " parameters index", + "Encoding": "uint8", + "Name": "S", + "Reference": "vrf_verify" + } + ], "IntroducedVersion": 7, "Groups": [ "Arithmetic" @@ -3900,7 +4356,14 @@ "uint64" ], "Doc": "field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)", - "ImmediateNote": "{uint8 block field index}", + "ImmediateNote": [ + { + "Comment": " block field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "block" + } + ], "IntroducedVersion": 7, "Groups": [ "State Access" diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index ff0f9a2dd6..d784aeff52 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -317,6 +317,28 @@ const ( immLabels ) +func (ik immKind) String() string { + switch ik { + case immByte: + return "uint8" + case immInt8: + return "int8" + case immLabel: + return "int16 (big-endian)" + case immInt: + return "varuint" + case immBytes: + return "varuint length, bytes" + case immInts: + return fmt.Sprintf("varuint count, [%s ...]", immInt.String()) + case immBytess: // "ss" not a typo. Multiple "bytes" + return fmt.Sprintf("varuint count, [%s ...]", immBytes.String()) + case immLabels: + return fmt.Sprintf("varuint count, [%s ...]", immLabel.String()) + } + return "unknown" +} + type immediate struct { Name string kind immKind From 2aa92cb4f85e4625a2c58f52b551245092acd3b9 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 10 Feb 2023 14:48:57 -0500 Subject: [PATCH 06/41] use StackType to check assignability --- data/transactions/logic/assembler.go | 14 +++----------- data/transactions/logic/eval.go | 17 +++++++++++++++++ data/transactions/logic/evalStateful_test.go | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 8bf1529d01..6b7be18367 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1693,16 +1693,8 @@ func (le lineError) Unwrap() error { return le.Err } -func typecheck(expected, got avmType) bool { - // Some ops push 'any' and we wait for run time to see what it is. - // Some of those 'any' are based on fields that we _could_ know now but haven't written a more detailed system of typecheck for (yet). - if expected == avmAny && got == avmNone { // Any is lenient, but stack can't be empty - return false - } - if (expected == avmAny) || (got == avmAny) { - return true - } - return expected == got +func typecheck(expected, got StackType) bool { + return got.AssignableTo(expected) } // newline not included since handled in scanner @@ -1827,7 +1819,7 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction } else { ops.trace(", %s", argType) } - if !typecheck(argType.AVMType, stype.AVMType) { + if !typecheck(argType, stype) { ops.typeErrorf("%s arg %d wanted type %s got %s", strings.Join(instruction, " "), i, argType, stype) } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 86263c3226..e2ea1bc35c 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -632,6 +632,23 @@ func (at avmType) String() string { return "internal error, unknown type" } +// stackType lifts the AVM type 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)) diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 638cd8e358..48a24f2795 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -2577,7 +2577,7 @@ func TestReturnTypes(t *testing.T) { stackType := cx.stack[i].argType() retType := spec.Return.Types[i] require.True( - t, typecheck(retType.AVMType, stackType), + t, typecheck(retType, stackType.stackType()), "%s expected to return %s but actual is %s", spec.Name, retType, stackType, ) } From b966f21f7a85781b2503d78658f60d95fe8fa978 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 10 Feb 2023 15:07:51 -0500 Subject: [PATCH 07/41] adding type refinement to bytes and bzero --- data/transactions/logic/assembler.go | 16 +++++++++++++++- data/transactions/logic/assembler_test.go | 15 ++++++++------- data/transactions/logic/eval.go | 2 +- data/transactions/logic/evalAppTxn_test.go | 12 ++++++------ data/transactions/logic/eval_test.go | 2 +- data/transactions/logic/opcodes.go | 2 +- 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 6b7be18367..90fbe18c2c 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1506,6 +1506,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] { @@ -1587,7 +1601,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 diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 64512be95e..0a599e4dda 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -772,7 +772,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 @@ -1853,7 +1853,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) @@ -1869,7 +1869,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) @@ -2656,10 +2656,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) } @@ -2671,14 +2672,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."}) } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index e2ea1bc35c..c63ca6d7a3 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -632,7 +632,7 @@ func (at avmType) String() string { return "internal error, unknown type" } -// stackType lifts the AVM type to a StackType +// 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 { diff --git a/data/transactions/logic/evalAppTxn_test.go b/data/transactions/logic/evalAppTxn_test.go index b33a97d742..64b0f9b3bc 100644 --- a/data/transactions/logic/evalAppTxn_test.go +++ b/data/transactions/logic/evalAppTxn_test.go @@ -104,15 +104,15 @@ 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;", + TestApp(t, NoTrack("itxn_begin; byte \"\"; itxn_field CloseRemainderTo;"), ep, "not an address") + TestApp(t, NoTrack("itxn_begin; byte \"\"; itxn_field AssetSender;"), ep, "not an address") + TestApp(t, NoTrack("itxn_begin; byte \"01234567890123456789012345678901\"; itxn_field AssetReceiver;"), 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") diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index b89a6cd0da..26520b6c11 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -4502,7 +4502,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) diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index ff0f9a2dd6..db4ec96dda 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -604,7 +604,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)}, From 79dcf29f80d6a8850bbff651a3ce9beed67e10d0 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 3 Apr 2023 11:48:37 -0400 Subject: [PATCH 08/41] partial cr --- cmd/opdoc/opdoc.go | 9 +- data/transactions/logic/TEAL_opcodes.md | 20 ++-- data/transactions/logic/assembler.go | 20 ++-- data/transactions/logic/eval.go | 111 ++++++++++++------- data/transactions/logic/evalStateful_test.go | 4 +- data/transactions/logic/eval_test.go | 8 +- data/transactions/logic/fields.go | 14 +-- data/transactions/logic/langspec.json | 87 ++++++--------- 8 files changed, 143 insertions(+), 130 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index 2fa9a7b2eb..d5ab8bd10f 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -260,11 +260,12 @@ type LanguageSpec struct { } func typeStrings(types []logic.StackType) []string { - out := make([]string, len(types)) - for i, t := range types { - out[i] = t.String() + out := []string{} + for _, t := range types { + if t.String() != "none" { + out = append(out, t.String()) + } } - return out } diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index f1124e4626..75487af257 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -12,7 +12,7 @@ Ops have a 'cost' of 1 unless otherwise specified. ## sha256 - Opcode: 0x01 -- Stack: ..., A: []byte → ..., hash +- Stack: ..., A: []byte → ..., [32]byte - SHA256 hash of value A, yields [32]byte - **Cost**: - 7 (v1) @@ -21,7 +21,7 @@ Ops have a 'cost' of 1 unless otherwise specified. ## keccak256 - Opcode: 0x02 -- Stack: ..., A: []byte → ..., hash +- Stack: ..., A: []byte → ..., [32]byte - Keccak256 hash of value A, yields [32]byte - **Cost**: - 26 (v1) @@ -30,7 +30,7 @@ Ops have a 'cost' of 1 unless otherwise specified. ## sha512_256 - Opcode: 0x03 -- Stack: ..., A: []byte → ..., hash +- Stack: ..., A: []byte → ..., [32]byte - SHA512_256 hash of value A, yields [32]byte - **Cost**: - 9 (v1) @@ -367,12 +367,12 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 3 | FirstValidTime | uint64 | v7 | UNIX timestamp of block before txn.FirstValid. Fails if negative | | 4 | LastValid | uint64 | | round number | | 5 | Note | []byte | | Any data up to 1024 bytes | -| 6 | Lease | hash | | 32 byte lease value | +| 6 | Lease | [32]byte | | 32 byte lease value | | 7 | Receiver | addr | | 32 byte address | | 8 | Amount | uint64 | | microalgos | | 9 | CloseRemainderTo | addr | | 32 byte address | -| 10 | VotePK | addr | | 32 byte address | -| 11 | SelectionPK | addr | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | | 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | | 13 | VoteLast | uint64 | | The last round that the participation key is valid. | | 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | @@ -384,7 +384,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 20 | AssetReceiver | addr | | 32 byte address | | 21 | AssetCloseTo | addr | | 32 byte address | | 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | -| 23 | TxID | hash | | The computed ID for this transaction. 32 bytes. | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | | 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | | 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | | 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | @@ -399,7 +399,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | | 38 | ConfigAssetName | []byte | v2 | The asset name | | 39 | ConfigAssetURL | []byte | v2 | URL | -| 40 | ConfigAssetMetadataHash | hash | v2 | 32 byte commitment to unspecified asset metadata | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | | 41 | ConfigAssetManager | addr | v2 | 32 byte address | | 42 | ConfigAssetReserve | addr | v2 | 32 byte address | | 43 | ConfigAssetFreeze | addr | v2 | 32 byte address | @@ -445,7 +445,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | | 9 | CreatorAddress | addr | v3 | Address of the creator of the current application. Application mode only. | | 10 | CurrentApplicationAddress | addr | v5 | Address that the current application controls. Application mode only. | -| 11 | GroupID | hash | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | +| 11 | GroupID | [32]byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | | 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | | 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | | 14 | CallerApplicationAddress | addr | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | @@ -968,7 +968,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or | 3 | AssetUnitName | []byte | | Asset unit name | | 4 | AssetName | []byte | | Asset name | | 5 | AssetURL | []byte | | URL with additional info about the asset | -| 6 | AssetMetadataHash | hash | | Arbitrary commitment | +| 6 | AssetMetadataHash | [32]byte | | Arbitrary commitment | | 7 | AssetManager | addr | | Manager address | | 8 | AssetReserve | addr | | Reserve address | | 9 | AssetFreeze | addr | | Freeze address | diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 8bf1529d01..edefea5729 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1336,7 +1336,11 @@ func typeSelect(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e top := len(pgm.stack) - 1 if top >= 2 { if pgm.stack[top-1].AVMType == pgm.stack[top-2].AVMType { - return nil, StackTypes{pgm.stack[top-1]}, nil + unioned, err := pgm.stack[top-1].union(pgm.stack[top-2]) + if err != nil { + return nil, nil, err + } + return nil, StackTypes{unioned}, nil } } return nil, nil, nil @@ -1693,16 +1697,8 @@ func (le lineError) Unwrap() error { return le.Err } -func typecheck(expected, got avmType) bool { - // Some ops push 'any' and we wait for run time to see what it is. - // Some of those 'any' are based on fields that we _could_ know now but haven't written a more detailed system of typecheck for (yet). - if expected == avmAny && got == avmNone { // Any is lenient, but stack can't be empty - return false - } - if (expected == avmAny) || (got == avmAny) { - return true - } - return expected == got +func typecheck(expected, got StackType) bool { + return got.AssignableTo(expected) } // newline not included since handled in scanner @@ -1827,7 +1823,7 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction } else { ops.trace(", %s", argType) } - if !typecheck(argType.AVMType, stype.AVMType) { + if !typecheck(argType, stype) { ops.typeErrorf("%s arg %d wanted type %s got %s", strings.Join(instruction, " "), i, argType, stype) } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index f197140e5e..5f5bf53e7b 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -82,13 +82,20 @@ type stackValue struct { Bytes []byte } -func (sv stackValue) argType() avmType { +func (sv stackValue) avmType() avmType { if sv.Bytes != nil { return avmBytes } return avmUint64 } +func (sv stackValue) stackType() StackType { + if sv.Bytes != nil { + return NewStackType(sv.avmType(), static(uint64(len(sv.Bytes)))) + } + return NewStackType(sv.avmType(), static(sv.Uint)) +} + func (sv stackValue) typeName() string { if sv.Bytes != nil { return "[]byte" @@ -154,7 +161,7 @@ func (sv stackValue) string(limit int) (string, error) { } func (sv stackValue) toTealValue() (tv basics.TealValue) { - if sv.argType() == avmBytes { + if sv.avmType() == avmBytes { return basics.TealValue{Type: basics.TealBytesType, Bytes: string(sv.Bytes)} } return basics.TealValue{Type: basics.TealUintType, Uint: sv.Uint} @@ -664,16 +671,18 @@ var ( // StackBoolean constrains the int to 1 or 0, representing True or False StackBoolean = NewStackType(avmUint64, bound(0, 1), "bool") - // StackHash represents output from a hash function or a field that returns a hash - StackHash = NewStackType(avmBytes, static(32), "hash") - // StackAddress represents a public key or address for an account + // StackAddress represents an address StackAddress = NewStackType(avmBytes, static(32), "addr") + // StackBytes32 represents a bytestring that should have exactly 32 bytes + StackBytes32 = NewStackType(avmBytes, static(32), "[32]byte") // StackBigInt represents a bytestring that should be treated like an int StackBigInt = NewStackType(avmBytes, bound(0, maxByteMathSize), "bigint") // StackMethodSelector represents a bytestring that should be treated like a method selector StackMethodSelector = NewStackType(avmBytes, static(4), "method") // StackStorageKey represents a bytestring that can be used as a key to some storage (global/local/box) StackStorageKey = NewStackType(avmBytes, bound(0, 64), "key") + // StackBoxKey represents a bytestring that can be used as a key to a box + StackBoxKey = NewStackType(avmBytes, bound(1, 64), "bkey") // AllStackTypes is a list of all the stack types we recognize // so that we can iterate over them in doc prep @@ -683,11 +692,11 @@ var ( StackAny, StackNone, StackBoolean, - StackHash, - StackAddress, + StackBytes32, StackBigInt, StackMethodSelector, StackStorageKey, + StackBoxKey, } ) @@ -726,6 +735,37 @@ func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { return st } +func (st StackType) union(other ...StackType) (StackType, error) { + if len(other) == 0 { + return st, nil + } + + for _, o := range other { + if !st.AssignableTo(o) { + return StackType{}, fmt.Errorf("cannot union %s and %s", st.Name, o.Name) + } + + switch st.AVMType { + case avmUint64: + if o.ValueBound[0] < st.ValueBound[0] { + st.ValueBound[0] = o.ValueBound[0] + } + if o.ValueBound[1] > st.ValueBound[1] { + st.ValueBound[1] = o.ValueBound[1] + } + case avmBytes: + if o.LengthBound[0] < st.LengthBound[0] { + st.LengthBound[0] = o.LengthBound[0] + } + if o.LengthBound[1] > st.LengthBound[1] { + st.LengthBound[1] = o.LengthBound[1] + } + } + } + + return st, nil +} + func (st StackType) narrowed(bounds [2]uint64) StackType { // It's static, set the name to show // the static value @@ -784,10 +824,9 @@ func (st StackType) AssignableTo(other StackType) bool { return !(smin == smax && omin == omax && smin != omin) case avmUint64: - // No static values at compile - // time so hard to do any typechecks for assembler, - // dont use this for avm runtime - return true + smin, smax := st.ValueBound[0], st.ValueBound[1] + omin, omax := other.ValueBound[0], other.ValueBound[1] + return !(smin == smax && omin == omax && smin != omin) default: panic("no stack type match in AssignableTo check") } @@ -847,14 +886,12 @@ func parseStackTypes(spec string) StackTypes { types[i] = StackUint64 case 'x': types[i] = StackNone - case 'A': - types[i] = StackAddress case 'N': types[i] = StackBigInt case 'T': types[i] = StackBoolean case 'H': - types[i] = StackHash + types[i] = StackBytes32 case 'M': types[i] = StackMethodSelector case 'K': @@ -1280,7 +1317,7 @@ func (cx *EvalContext) step() error { } first := len(cx.stack) - len(spec.Arg.Types) for i, argType := range spec.Arg.Types { - if !opCompat(argType.AVMType, cx.stack[first+i].argType()) { + if !opCompat(argType.AVMType, cx.stack[first+i].avmType()) { return fmt.Errorf("%s arg %d wanted %s but got %s", spec.Name, i, argType, cx.stack[first+i].typeName()) } } @@ -1328,7 +1365,7 @@ func (cx *EvalContext) step() error { } first = postheight - len(spec.Return.Types) for i, argType := range spec.Return.Types { - stackType := cx.stack[first+i].argType() + stackType := cx.stack[first+i].avmType() if !opCompat(argType.AVMType, stackType) { if spec.AlwaysExits() { // We test in the loop because it's the uncommon case. break @@ -1725,8 +1762,8 @@ func opOr(cx *EvalContext) error { func opEq(cx *EvalContext) error { last := len(cx.stack) - 1 prev := last - 1 - ta := cx.stack[prev].argType() - tb := cx.stack[last].argType() + ta := cx.stack[prev].avmType() + tb := cx.stack[last].avmType() if ta != tb { return fmt.Errorf("cannot compare (%s to %s)", cx.stack[prev].typeName(), cx.stack[last].typeName()) } @@ -1767,7 +1804,7 @@ func opItob(cx *EvalContext) error { ibytes := make([]byte, 8) binary.BigEndian.PutUint64(ibytes, cx.stack[last].Uint) // cx.stack[last].Uint is not cleared out as optimization - // stackValue.argType() checks Bytes field first + // stackValue.avmType() checks Bytes field first cx.stack[last].Bytes = ibytes return nil } @@ -1870,7 +1907,7 @@ func opSqrt(cx *EvalContext) error { func opBitLen(cx *EvalContext) error { last := len(cx.stack) - 1 - if cx.stack[last].argType() == avmUint64 { + if cx.stack[last].avmType() == avmUint64 { cx.stack[last].Uint = uint64(bits.Len64(cx.stack[last].Uint)) return nil } @@ -2524,14 +2561,14 @@ func opMatch(cx *EvalContext) error { matchedIdx := n for i, stackArg := range matchList { - if stackArg.argType() != matchVal.argType() { + if stackArg.avmType() != matchVal.avmType() { continue } - if matchVal.argType() == avmBytes && bytes.Equal(matchVal.Bytes, stackArg.Bytes) { + if matchVal.avmType() == avmBytes && bytes.Equal(matchVal.Bytes, stackArg.Bytes) { matchedIdx = i break - } else if matchVal.argType() == avmUint64 && matchVal.Uint == stackArg.Uint { + } else if matchVal.avmType() == avmUint64 && matchVal.Uint == stackArg.Uint { matchedIdx = i break } @@ -2669,8 +2706,8 @@ func (cx *EvalContext) assetHoldingToValue(holding *basics.AssetHolding, fs asse return sv, fmt.Errorf("invalid asset_holding_get field %d", fs.field) } - if fs.ftype.AVMType != sv.argType() { - return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) + if fs.ftype.AVMType != sv.avmType() { + return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.avmType()) } return sv, nil } @@ -2705,8 +2742,8 @@ func (cx *EvalContext) assetParamsToValue(params *basics.AssetParams, creator ba return sv, fmt.Errorf("invalid asset_params_get field %d", fs.field) } - if fs.ftype.AVMType != sv.argType() { - return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) + if fs.ftype.AVMType != sv.avmType() { + return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.avmType()) } return sv, nil } @@ -2732,8 +2769,8 @@ func (cx *EvalContext) appParamsToValue(params *basics.AppParams, fs appParamsFi return sv, fmt.Errorf("invalid app_params_get field %d", fs.field) } - if fs.ftype.AVMType != sv.argType() { - return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) + if fs.ftype.AVMType != sv.avmType() { + return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.avmType()) } return sv, nil } @@ -3066,8 +3103,8 @@ func (cx *EvalContext) txnFieldToStack(stxn *transactions.SignedTxnWithAD, fs *t return sv, fmt.Errorf("invalid txn field %s", fs.field) } - if fs.ftype.AVMType != sv.argType() { - return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) + if fs.ftype.AVMType != sv.avmType() { + return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.avmType()) } return sv, nil } @@ -3514,8 +3551,8 @@ func (cx *EvalContext) globalFieldToValue(fs globalFieldSpec) (sv stackValue, er err = fmt.Errorf("invalid global field %d", fs.field) } - if fs.ftype.AVMType != sv.argType() { - return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.argType()) + if fs.ftype.AVMType != sv.avmType() { + return sv, fmt.Errorf("%s expected field type is %s but got %s", fs.field, fs.ftype, sv.avmType()) } return sv, err @@ -3943,7 +3980,7 @@ func opGetBit(cx *EvalContext) error { target := cx.stack[prev] var bit uint64 - if target.argType() == avmUint64 { + if target.avmType() == avmUint64 { if idx > 63 { return errors.New("getbit index > 63 with with Uint") } @@ -3985,7 +4022,7 @@ func opSetBit(cx *EvalContext) error { return errors.New("setbit value > 1") } - if target.argType() == avmUint64 { + if target.avmType() == avmUint64 { if idx > 63 { return errors.New("setbit index > 63 with Uint") } @@ -4206,7 +4243,7 @@ func opExtract64Bits(cx *EvalContext) error { // than by index into txn.Accounts. func (cx *EvalContext) accountReference(account stackValue) (basics.Address, uint64, error) { - if account.argType() == avmUint64 { + if account.avmType() == avmUint64 { addr, err := cx.txn.Txn.AddressByIndex(account.Uint, cx.txn.Txn.Sender) return addr, account.Uint, err } @@ -4919,7 +4956,7 @@ func opItxnNext(cx *EvalContext) error { // that don't need (or want!) to allow low numbers to represent the account at // that index in Accounts array. func (cx *EvalContext) availableAccount(sv stackValue) (basics.Address, error) { - if sv.argType() != avmBytes || len(sv.Bytes) != crypto.DigestSize { + if sv.avmType() != avmBytes || len(sv.Bytes) != crypto.DigestSize { return basics.Address{}, fmt.Errorf("not an address") } diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 57f7f931dc..40823f6c6f 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -2580,10 +2580,10 @@ func TestReturnTypes(t *testing.T) { } require.Len(t, cx.stack, len(spec.Return.Types), "%s", ep.Trace) for i := 0; i < len(spec.Return.Types); i++ { - stackType := cx.stack[i].argType() + stackType := cx.stack[i].stackType() retType := spec.Return.Types[i] require.True( - t, typecheck(retType.AVMType, stackType), + t, typecheck(retType, stackType), "%s expected to return %s but actual is %s", spec.Name, retType, stackType, ) } diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 8ec4160527..ad07be5ae4 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -4174,13 +4174,13 @@ func TestArgType(t *testing.T) { t.Parallel() var sv stackValue - require.Equal(t, avmUint64, sv.argType()) + require.Equal(t, avmUint64, sv.avmType()) sv.Bytes = []byte("") - require.Equal(t, avmBytes, sv.argType()) + require.Equal(t, avmBytes, sv.avmType()) sv.Uint = 1 - require.Equal(t, avmBytes, sv.argType()) + require.Equal(t, avmBytes, sv.avmType()) sv.Bytes = nil - require.Equal(t, avmUint64, sv.argType()) + require.Equal(t, avmUint64, sv.avmType()) } func TestApplicationsDisallowOldTeal(t *testing.T) { diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index bbe3b4149d..40660f1f40 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -273,12 +273,12 @@ var txnFieldSpecs = [...]txnFieldSpec{ {FirstValidTime, StackUint64, false, randomnessVersion, 0, false, "UNIX timestamp of block before txn.FirstValid. Fails if negative"}, {LastValid, StackUint64, false, 0, 0, false, "round number"}, {Note, StackBytes, false, 0, 6, false, "Any data up to 1024 bytes"}, - {Lease, StackHash, false, 0, 0, false, "32 byte lease value"}, + {Lease, StackBytes32, false, 0, 0, false, "32 byte lease value"}, {Receiver, StackAddress, false, 0, 5, false, "32 byte address"}, {Amount, StackUint64, false, 0, 5, false, "microalgos"}, {CloseRemainderTo, StackAddress, false, 0, 5, false, "32 byte address"}, - {VotePK, StackAddress, false, 0, 6, false, "32 byte address"}, - {SelectionPK, StackAddress, false, 0, 6, false, "32 byte address"}, + {VotePK, StackBytes32, false, 0, 6, false, "32 byte address"}, + {SelectionPK, StackBytes32, false, 0, 6, false, "32 byte address"}, {VoteFirst, StackUint64, false, 0, 6, false, "The first round that the participation key is valid."}, {VoteLast, StackUint64, false, 0, 6, false, "The last round that the participation key is valid."}, {VoteKeyDilution, StackUint64, false, 0, 6, false, "Dilution for the 2-level participation key"}, @@ -292,7 +292,7 @@ var txnFieldSpecs = [...]txnFieldSpec{ {AssetCloseTo, StackAddress, false, 0, 5, false, "32 byte address"}, {GroupIndex, StackUint64, false, 0, 0, false, "Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1"}, - {TxID, StackHash, false, 0, 0, false, "The computed ID for this transaction. 32 bytes."}, + {TxID, StackBytes32, false, 0, 0, false, "The computed ID for this transaction. 32 bytes."}, {ApplicationID, StackUint64, false, 2, 6, false, "ApplicationID from ApplicationCall transaction"}, {OnCompletion, StackUint64, false, 2, 6, false, "ApplicationCall transaction on completion action"}, {ApplicationArgs, StackBytes, true, 2, 6, false, @@ -312,7 +312,7 @@ var txnFieldSpecs = [...]txnFieldSpec{ {ConfigAssetUnitName, StackBytes, false, 2, 5, false, "Unit name of the asset"}, {ConfigAssetName, StackBytes, false, 2, 5, false, "The asset name"}, {ConfigAssetURL, StackBytes, false, 2, 5, false, "URL"}, - {ConfigAssetMetadataHash, StackHash, false, 2, 5, false, + {ConfigAssetMetadataHash, StackBytes32, false, 2, 5, false, "32 byte commitment to unspecified asset metadata"}, {ConfigAssetManager, StackAddress, false, 2, 5, false, "32 byte address"}, {ConfigAssetReserve, StackAddress, false, 2, 5, false, "32 byte address"}, @@ -580,7 +580,7 @@ var globalFieldSpecs = [...]globalFieldSpec{ "Address of the creator of the current application"}, {CurrentApplicationAddress, StackAddress, ModeApp, 5, "Address that the current application controls"}, - {GroupID, StackHash, modeAny, 5, + {GroupID, StackBytes32, modeAny, 5, "ID of the transaction group. 32 zero bytes if the transaction is not part of a group."}, {OpcodeBudget, StackUint64, modeAny, 6, "The remaining cost that can be spent by opcodes in this program."}, @@ -1074,7 +1074,7 @@ var assetParamsFieldSpecs = [...]assetParamsFieldSpec{ {AssetUnitName, StackBytes, 2, "Asset unit name"}, {AssetName, StackBytes, 2, "Asset name"}, {AssetURL, StackBytes, 2, "URL with additional info about the asset"}, - {AssetMetadataHash, StackHash, 2, "Arbitrary commitment"}, + {AssetMetadataHash, StackBytes32, 2, "Arbitrary commitment"}, {AssetManager, StackAddress, 2, "Manager address"}, {AssetReserve, StackAddress, 2, "Reserve address"}, {AssetFreeze, StackAddress, 2, "Freeze address"}, diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index 7d20a868ef..d7b0f1f6d1 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -5,9 +5,6 @@ { "Opcode": 0, "Name": "err", - "Returns": [ - "none" - ], "Size": 1, "Doc": "Fail immediately.", "IntroducedVersion": 1, @@ -22,7 +19,7 @@ "[]byte" ], "Returns": [ - "hash" + "[32]byte" ], "Size": 1, "Doc": "SHA256 hash of value A, yields [32]byte", @@ -38,7 +35,7 @@ "[]byte" ], "Returns": [ - "hash" + "[32]byte" ], "Size": 1, "Doc": "Keccak256 hash of value A, yields [32]byte", @@ -54,7 +51,7 @@ "[]byte" ], "Returns": [ - "hash" + "[32]byte" ], "Size": 1, "Doc": "SHA512_256 hash of value A, yields [32]byte", @@ -100,10 +97,6 @@ "Secp256k1", "Secp256r1" ], - "ArgEnumTypes": [ - "none", - "none" - ], "Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}", "DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", "ImmediateNote": "{uint8 curve index}", @@ -127,10 +120,6 @@ "Secp256k1", "Secp256r1" ], - "ArgEnumTypes": [ - "none", - "none" - ], "Doc": "decompress pubkey A into components X, Y", "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", "ImmediateNote": "{uint8 curve index}", @@ -157,10 +146,6 @@ "Secp256k1", "Secp256r1" ], - "ArgEnumTypes": [ - "none", - "none" - ], "Doc": "for (data A, recovery id B, signature C, D) recover a public key", "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", "ImmediateNote": "{uint8 curve index}", @@ -890,12 +875,12 @@ "uint64", "uint64", "[]byte", - "hash", + "[32]byte", "addr", "uint64", "addr", - "addr", - "addr", + "[32]byte", + "[32]byte", "uint64", "uint64", "uint64", @@ -907,7 +892,7 @@ "addr", "addr", "uint64", - "hash", + "[32]byte", "uint64", "uint64", "[]byte", @@ -924,7 +909,7 @@ "[]byte", "[]byte", "[]byte", - "hash", + "[32]byte", "addr", "addr", "addr", @@ -996,7 +981,7 @@ "uint64", "addr", "addr", - "hash", + "[32]byte", "uint64", "uint64", "addr" @@ -1092,12 +1077,12 @@ "uint64", "uint64", "[]byte", - "hash", + "[32]byte", "addr", "uint64", "addr", - "addr", - "addr", + "[32]byte", + "[32]byte", "uint64", "uint64", "uint64", @@ -1109,7 +1094,7 @@ "addr", "addr", "uint64", - "hash", + "[32]byte", "uint64", "uint64", "[]byte", @@ -1126,7 +1111,7 @@ "[]byte", "[]byte", "[]byte", - "hash", + "[32]byte", "addr", "addr", "addr", @@ -1342,12 +1327,12 @@ "uint64", "uint64", "[]byte", - "hash", + "[32]byte", "addr", "uint64", "addr", - "addr", - "addr", + "[32]byte", + "[32]byte", "uint64", "uint64", "uint64", @@ -1359,7 +1344,7 @@ "addr", "addr", "uint64", - "hash", + "[32]byte", "uint64", "uint64", "[]byte", @@ -1376,7 +1361,7 @@ "[]byte", "[]byte", "[]byte", - "hash", + "[32]byte", "addr", "addr", "addr", @@ -1591,9 +1576,6 @@ "Args": [ "uint64" ], - "Returns": [ - "none" - ], "Size": 1, "Doc": "use A as success value; end", "IntroducedVersion": 2, @@ -2321,7 +2303,7 @@ "[]byte", "[]byte", "[]byte", - "hash", + "[32]byte", "addr", "addr", "addr", @@ -3116,8 +3098,8 @@ "addr", "uint64", "addr", - "addr", - "addr", + "[32]byte", + "[32]byte", "uint64", "uint64", "uint64", @@ -3142,7 +3124,7 @@ "[]byte", "[]byte", "[]byte", - "hash", + "[32]byte", "addr", "addr", "addr", @@ -3265,12 +3247,12 @@ "uint64", "uint64", "[]byte", - "hash", + "[32]byte", "addr", "uint64", "addr", - "addr", - "addr", + "[32]byte", + "[32]byte", "uint64", "uint64", "uint64", @@ -3282,7 +3264,7 @@ "addr", "addr", "uint64", - "hash", + "[32]byte", "uint64", "uint64", "[]byte", @@ -3299,7 +3281,7 @@ "[]byte", "[]byte", "[]byte", - "hash", + "[32]byte", "addr", "addr", "addr", @@ -3462,12 +3444,12 @@ "uint64", "uint64", "[]byte", - "hash", + "[32]byte", "addr", "uint64", "addr", - "addr", - "addr", + "[32]byte", + "[32]byte", "uint64", "uint64", "uint64", @@ -3479,7 +3461,7 @@ "addr", "addr", "uint64", - "hash", + "[32]byte", "uint64", "uint64", "[]byte", @@ -3496,7 +3478,7 @@ "[]byte", "[]byte", "[]byte", - "hash", + "[32]byte", "addr", "addr", "addr", @@ -3870,9 +3852,6 @@ "ArgEnum": [ "VrfAlgorand" ], - "ArgEnumTypes": [ - "none" - ], "Doc": "Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.", "DocExtra": "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).", "ImmediateNote": "{uint8 parameters index}", From 5e665af8563c2149b675e4f1ccc72d1c28cf4b0f Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 3 Apr 2023 12:23:46 -0400 Subject: [PATCH 09/41] fix assignable overlap check --- data/transactions/logic/README.md | 14 ++++----- data/transactions/logic/assembler.go | 2 +- data/transactions/logic/assembler_test.go | 1 + data/transactions/logic/eval.go | 36 +++++++---------------- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index 03a1fddc18..beb6747e72 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -450,12 +450,12 @@ Some of these have immediate data in the byte or bytes after the opcode. | 3 | FirstValidTime | uint64 | v7 | UNIX timestamp of block before txn.FirstValid. Fails if negative | | 4 | LastValid | uint64 | | round number | | 5 | Note | []byte | | Any data up to 1024 bytes | -| 6 | Lease | hash | | 32 byte lease value | +| 6 | Lease | [32]byte | | 32 byte lease value | | 7 | Receiver | addr | | 32 byte address | | 8 | Amount | uint64 | | microalgos | | 9 | CloseRemainderTo | addr | | 32 byte address | -| 10 | VotePK | addr | | 32 byte address | -| 11 | SelectionPK | addr | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | | 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | | 13 | VoteLast | uint64 | | The last round that the participation key is valid. | | 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | @@ -467,7 +467,7 @@ Some of these have immediate data in the byte or bytes after the opcode. | 20 | AssetReceiver | addr | | 32 byte address | | 21 | AssetCloseTo | addr | | 32 byte address | | 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | -| 23 | TxID | hash | | The computed ID for this transaction. 32 bytes. | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | | 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | | 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | | 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | @@ -482,7 +482,7 @@ Some of these have immediate data in the byte or bytes after the opcode. | 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | | 38 | ConfigAssetName | []byte | v2 | The asset name | | 39 | ConfigAssetURL | []byte | v2 | URL | -| 40 | ConfigAssetMetadataHash | hash | v2 | 32 byte commitment to unspecified asset metadata | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | | 41 | ConfigAssetManager | addr | v2 | 32 byte address | | 42 | ConfigAssetReserve | addr | v2 | 32 byte address | | 43 | ConfigAssetFreeze | addr | v2 | 32 byte address | @@ -537,7 +537,7 @@ Global fields are fields that are common to all the transactions in the group. I | 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | | 9 | CreatorAddress | addr | v3 | Address of the creator of the current application. Application mode only. | | 10 | CurrentApplicationAddress | addr | v5 | Address that the current application controls. Application mode only. | -| 11 | GroupID | hash | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | +| 11 | GroupID | [32]byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | | 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | | 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | | 14 | CallerApplicationAddress | addr | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | @@ -561,7 +561,7 @@ Asset fields include `AssetHolding` and `AssetParam` fields that are used in the | 3 | AssetUnitName | []byte | | Asset unit name | | 4 | AssetName | []byte | | Asset name | | 5 | AssetURL | []byte | | URL with additional info about the asset | -| 6 | AssetMetadataHash | hash | | Arbitrary commitment | +| 6 | AssetMetadataHash | [32]byte | | Arbitrary commitment | | 7 | AssetManager | addr | | Manager address | | 8 | AssetReserve | addr | | Reserve address | | 9 | AssetFreeze | addr | | Freeze address | diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index edefea5729..56482ed7f4 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1982,7 +1982,7 @@ func (ops *OpStream) assemble(text string) error { if ops.Errors != nil { l := len(ops.Errors) if l == 1 { - return errors.New("1 error") + return fmt.Errorf("1 error: %s", ops.Errors[0].Error()) } return fmt.Errorf("%d errors", l) } diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 64512be95e..4af5bfd71c 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2471,6 +2471,7 @@ func TestBranchAssemblyTypeCheck(t *testing.T) { ops := newOpStream(AssemblerMaxVersion) err := ops.assemble(text) + t.Logf("%+v", err) require.NoError(t, err) require.Empty(t, ops.Warnings) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 5f5bf53e7b..bfe4e13c8a 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -798,38 +798,24 @@ func (st StackType) AssignableTo(other StackType) bool { } // Same type now + // Check if our constraints will satisfy the other type + var ( + smin, smax uint64 + omin, omax uint64 + ) - // Check if our constraints will be satisfied by - // the other type switch st.AVMType { case avmBytes: - smin, smax := st.LengthBound[0], st.LengthBound[1] - omin, omax := other.LengthBound[0], other.LengthBound[1] - - // yes definitely - // [32,32] => [0..4k] - // [32,32] => [32,32] - - // yes, maybe determined at runtime - // [0..4k] => [32,32] - - // no, cant fit - // [64,64] => [32,32] - // no, makes no sense - // [32,32] => [64,64] - - // we only have 0-N and [N,N] (static) and only - // those that are both not static and have different lengths - // can be assigned - return !(smin == smax && omin == omax && smin != omin) - + smin, smax = st.LengthBound[0], st.LengthBound[1] + omin, omax = other.LengthBound[0], other.LengthBound[1] case avmUint64: - smin, smax := st.ValueBound[0], st.ValueBound[1] - omin, omax := other.ValueBound[0], other.ValueBound[1] - return !(smin == smax && omin == omax && smin != omin) + smin, smax = st.ValueBound[0], st.ValueBound[1] + omin, omax = other.ValueBound[0], other.ValueBound[1] default: panic("no stack type match in AssignableTo check") } + + return smin <= omax && smax >= omin } func (st StackType) String() string { From 0b2e713cc5bc8b614c4180a32494256d37efdc5b Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 3 Apr 2023 13:50:04 -0400 Subject: [PATCH 10/41] modify union fn to skip assignable check and demote type to Any where avmType doesnt match --- data/transactions/logic/assembler.go | 9 ++---- data/transactions/logic/eval.go | 47 ++++++++++++++-------------- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 56482ed7f4..6a2aacf666 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1335,13 +1335,8 @@ func typeDupTwo(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e func typeSelect(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { top := len(pgm.stack) - 1 if top >= 2 { - if pgm.stack[top-1].AVMType == pgm.stack[top-2].AVMType { - unioned, err := pgm.stack[top-1].union(pgm.stack[top-2]) - if err != nil { - return nil, nil, err - } - return nil, StackTypes{unioned}, nil - } + unioned, err := unionStackTypes(pgm.stack[top-1], pgm.stack[top-2]) + return nil, StackTypes{unioned}, err } return nil, nil, nil } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index bfe4e13c8a..113f14dcc3 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -735,35 +735,34 @@ func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { return st } -func (st StackType) union(other ...StackType) (StackType, error) { - if len(other) == 0 { - return st, nil +func unionStackTypes(a, b StackType) (StackType, error) { + if a.AVMType != b.AVMType { + return StackAny, nil } - for _, o := range other { - if !st.AssignableTo(o) { - return StackType{}, fmt.Errorf("cannot union %s and %s", st.Name, o.Name) - } + // Same type now, so we can just take the union of the bounds + var bounds [2]uint64 - switch st.AVMType { - case avmUint64: - if o.ValueBound[0] < st.ValueBound[0] { - st.ValueBound[0] = o.ValueBound[0] - } - if o.ValueBound[1] > st.ValueBound[1] { - st.ValueBound[1] = o.ValueBound[1] - } - case avmBytes: - if o.LengthBound[0] < st.LengthBound[0] { - st.LengthBound[0] = o.LengthBound[0] - } - if o.LengthBound[1] > st.LengthBound[1] { - st.LengthBound[1] = o.LengthBound[1] - } - } + switch a.AVMType { + case avmUint64: + bounds = unionBounds(a.ValueBound, b.ValueBound) + case avmBytes: + bounds = unionBounds(a.LengthBound, b.LengthBound) + } + + return NewStackType(a.AVMType, bounds), nil +} + +func unionBounds(a, b [2]uint64) [2]uint64 { + u := [2]uint64{a[0], a[1]} + if b[0] < u[0] { + u[0] = b[0] } - return st, nil + if b[1] > u[1] { + u[1] = b[1] + } + return u } func (st StackType) narrowed(bounds [2]uint64) StackType { From b1dc4d81fa9527b38be4ecc6e83fbf0a10580957 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 10 Apr 2023 06:10:46 -0400 Subject: [PATCH 11/41] Make union a method on StackType receiver, fix bounds on bytemath results --- data/transactions/logic/assembler.go | 14 +++++++++++--- data/transactions/logic/assembler_test.go | 1 - data/transactions/logic/eval.go | 6 +++--- data/transactions/logic/fields_test.go | 8 ++++---- data/transactions/logic/opcodes.go | 4 ++-- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 72de405267..9488d4fd14 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1335,7 +1335,7 @@ func typeDupTwo(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e func typeSelect(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { top := len(pgm.stack) - 1 if top >= 2 { - unioned, err := unionStackTypes(pgm.stack[top-1], pgm.stack[top-2]) + unioned, err := pgm.stack[top-1].union(pgm.stack[top-2]) return nil, StackTypes{unioned}, err } return nil, nil, nil @@ -1389,6 +1389,12 @@ func typeUncover(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, return anyTypes(depth), returns, nil } +func typeByteMath(resultSize uint64) refineFunc { + return func(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { + return nil, StackTypes{NewStackType(avmBytes, bound(0, resultSize))}, nil + } +} + func typeTxField(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { if len(args) != 1 { return nil, nil, nil @@ -1418,7 +1424,9 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e return nil, 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 + // 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].AVMType = avmAny } @@ -1977,7 +1985,7 @@ func (ops *OpStream) assemble(text string) error { if ops.Errors != nil { l := len(ops.Errors) if l == 1 { - return fmt.Errorf("1 error: %s", ops.Errors[0].Error()) + return fmt.Errorf("1 error: %w", ops.Errors[0]) } return fmt.Errorf("%d errors", l) } diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 4512ae1b24..656f725873 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2488,7 +2488,6 @@ func TestBranchAssemblyTypeCheck(t *testing.T) { ops := newOpStream(AssemblerMaxVersion) err := ops.assemble(text) - t.Logf("%+v", err) require.NoError(t, err) require.Empty(t, ops.Warnings) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 1e33a702a3..994bf768d2 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -715,7 +715,7 @@ func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { return st } -func unionStackTypes(a, b StackType) (StackType, error) { +func (a StackType) union(b StackType) (StackType, error) { if a.AVMType != b.AVMType { return StackAny, nil } @@ -759,8 +759,8 @@ func (st StackType) narrowed(bounds [2]uint64) StackType { return NewStackType(st.AVMType, bounds) } -// AssignableTo returns a bool indicating whether the receiver can be -// assigned to some other type that is expected by the next operation +// AssignableTo indicates whether a value typed st might be +// usable as other. func (st StackType) AssignableTo(other StackType) bool { if st.AVMType == avmNone || other.AVMType == avmNone { return false diff --git a/data/transactions/logic/fields_test.go b/data/transactions/logic/fields_test.go index 2d58854d5e..1f9a9c4233 100644 --- a/data/transactions/logic/fields_test.go +++ b/data/transactions/logic/fields_test.go @@ -220,10 +220,10 @@ func TestAssetParamsFieldsVersions(t *testing.T) { // Need to use intc so we can "backversion" the // program and not have it fail because of pushint. text := fmt.Sprintf("intcblock 55 1; intc_0; asset_params_get %s; bnz ok; err; ok: ", field.field.String()) - switch field.ftype { - case StackUint64: // ensure the return type is uint64 by adding + switch field.ftype.AVMType { + case avmUint64: // ensure the return type is uint64 by adding text += " intc_1; +" - case StackBytes: // ensure the return type is bytes by using len + case avmBytes: // ensure the return type is bytes by using len text += " len" // also happens to ensure that we get non empty - the params fields are fixed width } // check assembler fails if version before introduction @@ -271,7 +271,7 @@ func TestAcctParamsFieldsVersions(t *testing.T) { for _, field := range acctParamsFieldSpecs { text := fmt.Sprintf("txn Sender; acct_params_get %s; assert;", field.field.String()) - if field.ftype == StackBytes { + if field.ftype.AVMType == avmBytes { text += "global ZeroAddress; concat; len" // use concat to prove we have bytes } else { text += "global ZeroAddress; len; +" // use + to prove we have an int diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index 961673cf87..960f44dfa5 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -600,10 +600,10 @@ var OpSpecs = []OpSpec{ {0x9b, "bn256_pairing", opBn256Pairing, proto("bb:i"), pairingVersion, costly(8700)}, // Byteslice math. - {0xa0, "b+", opBytesPlus, proto("NN:N"), 4, costly(10)}, + {0xa0, "b+", opBytesPlus, proto("NN:b"), 4, costly(10).typed(typeByteMath(maxByteMathSize + 1))}, {0xa1, "b-", opBytesMinus, proto("NN:N"), 4, costly(10)}, {0xa2, "b/", opBytesDiv, proto("NN:N"), 4, costly(20)}, - {0xa3, "b*", opBytesMul, proto("NN:N"), 4, costly(20)}, + {0xa3, "b*", opBytesMul, proto("NN:b"), 4, costly(20).typed(typeByteMath(maxByteMathSize * 2))}, {0xa4, "b<", opBytesLt, proto("NN:T"), 4, detDefault()}, {0xa5, "b>", opBytesGt, proto("NN:T"), 4, detDefault()}, {0xa6, "b<=", opBytesLe, proto("NN:T"), 4, detDefault()}, From a85c88bd90936479344a1598f8b5c189ee2a77e3 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 10 Apr 2023 06:20:03 -0400 Subject: [PATCH 12/41] single bounds array for StackType, regen docs --- data/transactions/logic/TEAL_opcodes.md | 4 +- data/transactions/logic/eval.go | 53 +++++------------------- data/transactions/logic/langspec.json | 4 +- data/transactions/logic/opcodes.go | 54 ++++++++++++------------- 4 files changed, 41 insertions(+), 74 deletions(-) diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 83663b305b..eab2f0bd51 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -1214,7 +1214,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b+ - Opcode: 0xa0 -- Stack: ..., A: bigint, B: bigint → ..., bigint +- Stack: ..., A: bigint, B: bigint → ..., []byte - A plus B. A and B are interpreted as big-endian unsigned integers - **Cost**: 10 - Availability: v4 @@ -1238,7 +1238,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## b* - Opcode: 0xa3 -- Stack: ..., A: bigint, B: bigint → ..., bigint +- Stack: ..., A: bigint, B: bigint → ..., []byte - A times B. A and B are interpreted as big-endian unsigned integers. - **Cost**: 20 - Availability: v4 diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 994bf768d2..91ea9e9d5c 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -637,10 +637,9 @@ var ( StackBytes = NewStackType(avmBytes, bound(0, maxStringSize)) // StackAny could be Bytes or Uint64 StackAny = StackType{ - Name: avmAny.String(), - AVMType: avmAny, - ValueBound: StackUint64.ValueBound, - LengthBound: StackBytes.LengthBound, + Name: avmAny.String(), + AVMType: avmAny, + Bound: [2]uint64{0, 0}, } // StackNone is used when there is no input or output to // an opcode @@ -690,10 +689,9 @@ func static(size uint64) [2]uint64 { // StackType describes the type of a value on the operand stack type StackType struct { - Name string - AVMType avmType - LengthBound [2]uint64 - ValueBound [2]uint64 + Name string + AVMType avmType + Bound [2]uint64 } // NewStackType Initializes a new StackType with fields passed @@ -703,16 +701,7 @@ func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { name = stname[0] } - st := StackType{Name: name, AVMType: at} - - switch at { - case avmBytes: - st.LengthBound = bounds - case avmUint64: - st.ValueBound = bounds - } - - return st + return StackType{Name: name, AVMType: at, Bound: bounds} } func (a StackType) union(b StackType) (StackType, error) { @@ -721,16 +710,7 @@ func (a StackType) union(b StackType) (StackType, error) { } // Same type now, so we can just take the union of the bounds - var bounds [2]uint64 - - switch a.AVMType { - case avmUint64: - bounds = unionBounds(a.ValueBound, b.ValueBound) - case avmBytes: - bounds = unionBounds(a.LengthBound, b.LengthBound) - } - - return NewStackType(a.AVMType, bounds), nil + return NewStackType(a.AVMType, unionBounds(a.Bound, b.Bound)), nil } func unionBounds(a, b [2]uint64) [2]uint64 { @@ -778,21 +758,8 @@ func (st StackType) AssignableTo(other StackType) bool { // Same type now // Check if our constraints will satisfy the other type - var ( - smin, smax uint64 - omin, omax uint64 - ) - - switch st.AVMType { - case avmBytes: - smin, smax = st.LengthBound[0], st.LengthBound[1] - omin, omax = other.LengthBound[0], other.LengthBound[1] - case avmUint64: - smin, smax = st.ValueBound[0], st.ValueBound[1] - omin, omax = other.ValueBound[0], other.ValueBound[1] - default: - panic("no stack type match in AssignableTo check") - } + smin, smax := st.Bound[0], st.Bound[1] + omin, omax := other.Bound[0], other.Bound[1] return smin <= omax && smax >= omin } diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index a6936886a9..b727ad1b8e 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -2744,7 +2744,7 @@ "bigint" ], "Returns": [ - "bigint" + "[]byte" ], "Size": 1, "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers", @@ -2795,7 +2795,7 @@ "bigint" ], "Returns": [ - "bigint" + "[]byte" ], "Size": 1, "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.", diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index 960f44dfa5..cc14c47b28 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -531,33 +531,33 @@ var OpSpecs = []OpSpec{ {0x60, "balance", opBalance, proto("i:i"), 2, only(ModeApp)}, {0x60, "balance", opBalance, proto("a:i"), directRefEnabledVersion, only(ModeApp)}, {0x60, "balance", opBalance, proto("b:i"), sharedResourcesVersion, only(ModeApp)}, - {0x61, "app_opted_in", opAppOptedIn, proto("ii:i"), 2, only(ModeApp)}, - {0x61, "app_opted_in", opAppOptedIn, proto("ai:i"), directRefEnabledVersion, only(ModeApp)}, - {0x61, "app_opted_in", opAppOptedIn, proto("bi:i"), sharedResourcesVersion, only(ModeApp)}, - {0x62, "app_local_get", opAppLocalGet, proto("ib:a"), 2, only(ModeApp)}, - {0x62, "app_local_get", opAppLocalGet, proto("ab:a"), directRefEnabledVersion, only(ModeApp)}, - {0x62, "app_local_get", opAppLocalGet, proto("bb:a"), sharedResourcesVersion, only(ModeApp)}, - {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iib:ai"), 2, only(ModeApp)}, - {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aib:ai"), directRefEnabledVersion, only(ModeApp)}, - {0x63, "app_local_get_ex", opAppLocalGetEx, proto("bib:ai"), sharedResourcesVersion, only(ModeApp)}, - {0x64, "app_global_get", opAppGlobalGet, proto("b:a"), 2, only(ModeApp)}, - {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("ib:ai"), 2, only(ModeApp)}, - {0x66, "app_local_put", opAppLocalPut, proto("iba:"), 2, only(ModeApp)}, - {0x66, "app_local_put", opAppLocalPut, proto("aba:"), directRefEnabledVersion, only(ModeApp)}, - {0x66, "app_local_put", opAppLocalPut, proto("bba:"), sharedResourcesVersion, only(ModeApp)}, - {0x67, "app_global_put", opAppGlobalPut, proto("ba:"), 2, only(ModeApp)}, - {0x68, "app_local_del", opAppLocalDel, proto("ib:"), 2, only(ModeApp)}, - {0x68, "app_local_del", opAppLocalDel, proto("ab:"), directRefEnabledVersion, only(ModeApp)}, - {0x68, "app_local_del", opAppLocalDel, proto("bb:"), sharedResourcesVersion, only(ModeApp)}, - {0x69, "app_global_del", opAppGlobalDel, proto("b:"), 2, only(ModeApp)}, - - {0x70, "asset_holding_get", opAssetHoldingGet, proto("ii:ai"), 2, field("f", &AssetHoldingFields).only(ModeApp)}, - {0x70, "asset_holding_get", opAssetHoldingGet, proto("ai:ai"), directRefEnabledVersion, field("f", &AssetHoldingFields).only(ModeApp)}, - {0x70, "asset_holding_get", opAssetHoldingGet, proto("bi:ai"), sharedResourcesVersion, field("f", &AssetHoldingFields).only(ModeApp)}, - {0x71, "asset_params_get", opAssetParamsGet, proto("i:ai"), 2, field("f", &AssetParamsFields).only(ModeApp)}, - {0x72, "app_params_get", opAppParamsGet, proto("i:ai"), 5, field("f", &AppParamsFields).only(ModeApp)}, - {0x73, "acct_params_get", opAcctParamsGet, proto("a:ai"), 6, field("f", &AcctParamsFields).only(ModeApp)}, - {0x73, "acct_params_get", opAcctParamsGet, proto("b:ai"), sharedResourcesVersion, field("f", &AcctParamsFields).only(ModeApp)}, + {0x61, "app_opted_in", opAppOptedIn, proto("ii:T"), 2, only(ModeApp)}, + {0x61, "app_opted_in", opAppOptedIn, proto("ai:T"), directRefEnabledVersion, only(ModeApp)}, + {0x61, "app_opted_in", opAppOptedIn, proto("bi:T"), sharedResourcesVersion, only(ModeApp)}, + {0x62, "app_local_get", opAppLocalGet, proto("iK:a"), 2, only(ModeApp)}, + {0x62, "app_local_get", opAppLocalGet, proto("aK:a"), directRefEnabledVersion, only(ModeApp)}, + {0x62, "app_local_get", opAppLocalGet, proto("bK:a"), sharedResourcesVersion, only(ModeApp)}, + {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iiK:aT"), 2, only(ModeApp)}, + {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aiK:aT"), directRefEnabledVersion, only(ModeApp)}, + {0x63, "app_local_get_ex", opAppLocalGetEx, proto("biK:aT"), sharedResourcesVersion, only(ModeApp)}, + {0x64, "app_global_get", opAppGlobalGet, proto("K:a"), 2, only(ModeApp)}, + {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("iK:aT"), 2, only(ModeApp)}, + {0x66, "app_local_put", opAppLocalPut, proto("iKa:"), 2, only(ModeApp)}, + {0x66, "app_local_put", opAppLocalPut, proto("aKa:"), directRefEnabledVersion, only(ModeApp)}, + {0x66, "app_local_put", opAppLocalPut, proto("bKa:"), sharedResourcesVersion, only(ModeApp)}, + {0x67, "app_global_put", opAppGlobalPut, proto("Ka:"), 2, only(ModeApp)}, + {0x68, "app_local_del", opAppLocalDel, proto("iK:"), 2, only(ModeApp)}, + {0x68, "app_local_del", opAppLocalDel, proto("aK:"), directRefEnabledVersion, only(ModeApp)}, + {0x68, "app_local_del", opAppLocalDel, proto("bK:"), sharedResourcesVersion, only(ModeApp)}, + {0x69, "app_global_del", opAppGlobalDel, proto("K:"), 2, only(ModeApp)}, + + {0x70, "asset_holding_get", opAssetHoldingGet, proto("ii:aT"), 2, field("f", &AssetHoldingFields).only(ModeApp)}, + {0x70, "asset_holding_get", opAssetHoldingGet, proto("ai:aT"), directRefEnabledVersion, field("f", &AssetHoldingFields).only(ModeApp)}, + {0x70, "asset_holding_get", opAssetHoldingGet, proto("bi:aT"), sharedResourcesVersion, field("f", &AssetHoldingFields).only(ModeApp)}, + {0x71, "asset_params_get", opAssetParamsGet, proto("i:aT"), 2, field("f", &AssetParamsFields).only(ModeApp)}, + {0x72, "app_params_get", opAppParamsGet, proto("i:aT"), 5, field("f", &AppParamsFields).only(ModeApp)}, + {0x73, "acct_params_get", opAcctParamsGet, proto("a:aT"), 6, field("f", &AcctParamsFields).only(ModeApp)}, + {0x73, "acct_params_get", opAcctParamsGet, proto("b:aT"), sharedResourcesVersion, field("f", &AcctParamsFields).only(ModeApp)}, {0x78, "min_balance", opMinBalance, proto("i:i"), 3, only(ModeApp)}, {0x78, "min_balance", opMinBalance, proto("a:i"), directRefEnabledVersion, only(ModeApp)}, From b49fb93b82d29732f6ca832db201be00536b70db Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 10 Apr 2023 06:32:24 -0400 Subject: [PATCH 13/41] union stack type into all scratch slots --- data/transactions/logic/assembler.go | 11 +++++----- data/transactions/logic/eval.go | 30 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 9488d4fd14..cd7b314682 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1423,12 +1423,13 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e if top < 0 { return nil, nil, nil } + var err error 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].AVMType = avmAny + // We can't know what slot stacktop is being stored in + // so we union it into all scratch slots + pgm.scratchSpace[i], err = pgm.scratchSpace[i].union(pgm.stack[top]) + if err != nil { + return nil, nil, err } } return nil, nil, nil diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 91ea9e9d5c..8a73ea8ac1 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -687,6 +687,18 @@ func static(size uint64) [2]uint64 { return bound(size, size) } +func union(a, b [2]uint64) [2]uint64 { + u := [2]uint64{a[0], a[1]} + if b[0] < u[0] { + u[0] = b[0] + } + + if b[1] > u[1] { + u[1] = b[1] + } + return u +} + // StackType describes the type of a value on the operand stack type StackType struct { Name string @@ -704,25 +716,13 @@ func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { return StackType{Name: name, AVMType: at, Bound: bounds} } -func (a StackType) union(b StackType) (StackType, error) { - if a.AVMType != b.AVMType { +func (st StackType) union(b StackType) (StackType, error) { + if st.AVMType != b.AVMType { return StackAny, nil } // Same type now, so we can just take the union of the bounds - return NewStackType(a.AVMType, unionBounds(a.Bound, b.Bound)), nil -} - -func unionBounds(a, b [2]uint64) [2]uint64 { - u := [2]uint64{a[0], a[1]} - if b[0] < u[0] { - u[0] = b[0] - } - - if b[1] > u[1] { - u[1] = b[1] - } - return u + return NewStackType(st.AVMType, union(st.Bound, b.Bound)), nil } func (st StackType) narrowed(bounds [2]uint64) StackType { From 26779b242102952105f7b18d0e3f92666ca05fee Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Thu, 13 Apr 2023 14:14:35 -0400 Subject: [PATCH 14/41] dont modify the scratch space until we're sure there is no error --- data/transactions/logic/assembler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 8acedd9e2e..5f0bef2ed7 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1433,14 +1433,14 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e if top < 0 { return nil, nil, nil } - var err error for i := range pgm.scratchSpace { // We can't know what slot stacktop is being stored in // so we union it into all scratch slots - pgm.scratchSpace[i], err = pgm.scratchSpace[i].union(pgm.stack[top]) + unioned, err := pgm.scratchSpace[i].union(pgm.stack[top]) if err != nil { return nil, nil, err } + pgm.scratchSpace[i] = unioned } return nil, nil, nil } From b2170c0f64aacaf7745500f4ac41b87119e3dbe5 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Thu, 13 Apr 2023 14:38:21 -0400 Subject: [PATCH 15/41] add fn to refine int pseudo op --- data/transactions/logic/assembler.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 5f0bef2ed7..10c0d5274a 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1524,6 +1524,15 @@ func typePushInts(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, return nil, types, nil } +func typePushInt(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { + types := make(StackTypes, len(args)) + for i := range types { + val, _ := strconv.ParseUint(args[i], 10, 64) + types[i] = NewStackType(avmUint64, bound(val, val)) + } + return nil, types, nil +} + func joinIntsOnOr(singularTerminator string, list ...int) string { if len(list) == 1 { switch list[0] { @@ -1604,7 +1613,7 @@ func getSpec(ops *OpStream, name string, args []string) (OpSpec, string, bool) { const anyImmediates = -1 var pseudoOps = map[string]map[int]OpSpec{ - "int": {anyImmediates: OpSpec{Name: "int", Proto: proto(":i"), OpDetails: assembler(asmInt)}}, + "int": {anyImmediates: OpSpec{Name: "int", Proto: proto(":i"), OpDetails: assembler(asmInt).typed(typePushInt)}}, "byte": {anyImmediates: OpSpec{Name: "byte", Proto: proto(":b"), OpDetails: assembler(asmByte)}}, // parse basics.Address, actually just another []byte constant "addr": {anyImmediates: OpSpec{Name: "addr", Proto: proto(":b"), OpDetails: assembler(asmAddr)}}, From f9b38aa1c9b251850d1d4a7727cc677d415fd356 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Thu, 13 Apr 2023 14:42:19 -0400 Subject: [PATCH 16/41] remove err from union method sig, cleanup --- data/transactions/logic/assembler.go | 9 ++------- data/transactions/logic/eval.go | 6 +++--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 5f0bef2ed7..da50461b79 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1345,8 +1345,7 @@ func typeDupTwo(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e func typeSelect(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { top := len(pgm.stack) - 1 if top >= 2 { - unioned, err := pgm.stack[top-1].union(pgm.stack[top-2]) - return nil, StackTypes{unioned}, err + return nil, StackTypes{pgm.stack[top-1].union(pgm.stack[top-2])}, nil } return nil, nil, nil } @@ -1436,11 +1435,7 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e for i := range pgm.scratchSpace { // We can't know what slot stacktop is being stored in // so we union it into all scratch slots - unioned, err := pgm.scratchSpace[i].union(pgm.stack[top]) - if err != nil { - return nil, nil, err - } - pgm.scratchSpace[i] = unioned + pgm.scratchSpace[i] = pgm.scratchSpace[i].union(pgm.stack[top]) } return nil, nil, nil } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 8a73ea8ac1..d23bc3e354 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -716,13 +716,13 @@ func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { return StackType{Name: name, AVMType: at, Bound: bounds} } -func (st StackType) union(b StackType) (StackType, error) { +func (st StackType) union(b StackType) StackType { if st.AVMType != b.AVMType { - return StackAny, nil + return StackAny } // Same type now, so we can just take the union of the bounds - return NewStackType(st.AVMType, union(st.Bound, b.Bound)), nil + return NewStackType(st.AVMType, union(st.Bound, b.Bound)) } func (st StackType) narrowed(bounds [2]uint64) StackType { From 51a12137cb122b41fcccc1cb0949aeb528428081 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Thu, 13 Apr 2023 16:05:54 -0400 Subject: [PATCH 17/41] amend union to allow either to be None and return the other --- data/transactions/logic/eval.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index d23bc3e354..0b07b94bf3 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -717,6 +717,14 @@ func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { } func (st StackType) union(b StackType) StackType { + if st.AVMType == avmNone { + return b + } + + if b.AVMType == avmNone { + return st + } + if st.AVMType != b.AVMType { return StackAny } From 8117273ebdfcc0b01c4547aa86e0ec30ddd485a8 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Thu, 13 Apr 2023 16:43:04 -0400 Subject: [PATCH 18/41] initialize scratch to zeros --- data/transactions/logic/assembler.go | 2 +- data/transactions/logic/eval.go | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index da50461b79..103d01e9de 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -274,7 +274,7 @@ func newOpStream(version uint64) OpStream { } for i := range o.known.scratchSpace { - o.known.scratchSpace[i] = StackUint64 + o.known.scratchSpace[i] = StackZeroUint64 } return o diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 0b07b94bf3..dc2275eec5 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -663,6 +663,11 @@ var ( // StackBoxKey represents a bytestring that can be used as a key to a box StackBoxKey = NewStackType(avmBytes, bound(1, 64), "bkey") + // StackZeroUint64 is a StackUint64 with a minimum value of 0 and a maximum value of 0 + StackZeroUint64 = NewStackType(avmUint64, bound(0, 0), "0") + // StackZeroBytes is a StackBytes with a minimum length of 0 and a maximum length of 0 + StackZeroBytes = NewStackType(avmUint64, bound(0, 0), "''") + // AllStackTypes is a list of all the stack types we recognize // so that we can iterate over them in doc prep AllStackTypes = []StackType{ @@ -676,6 +681,8 @@ var ( StackMethodSelector, StackStorageKey, StackBoxKey, + StackZeroUint64, + StackZeroBytes, } ) @@ -717,13 +724,8 @@ func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { } func (st StackType) union(b StackType) StackType { - if st.AVMType == avmNone { - return b - } - - if b.AVMType == avmNone { - return st - } + // TODO: Can we ever receive one or the other + // as None? should that be a panic? if st.AVMType != b.AVMType { return StackAny From 5f9b73a1af8eee45a1faa0e0ff225e76b4b5ee3f Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Thu, 13 Apr 2023 17:16:04 -0400 Subject: [PATCH 19/41] adding a test to check that scratch slot bounds get set appropriately --- data/transactions/logic/assembler_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index cec3eb39df..c7d6c76b53 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2621,6 +2621,29 @@ func TestScratchTypeCheck(t *testing.T) { 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{1, "btoi arg 0..."}) + +} + +func TestScratchBounds(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + os := testProg(t, "int 5; store 1; load 1; return;", AssemblerMaxVersion) + sv := os.known.scratchSpace[1] + require.Equal(t, sv.AVMType, avmUint64) + require.ElementsMatch(t, sv.Bound, static(5)) + + os = testProg(t, "int 5; store 1; load 1; int 1; int 1; stores; return;", AssemblerMaxVersion) + sv = os.known.scratchSpace[1] + require.Equal(t, sv.AVMType, avmUint64) + require.ElementsMatch(t, sv.Bound, bound(1, 5)) + + os = testProg(t, "int 5; store 1; load 1; int 1; byte 0xff; stores; return;", AssemblerMaxVersion) + sv = os.known.scratchSpace[1] + require.Equal(t, sv.AVMType, avmAny) + require.ElementsMatch(t, os.known.scratchSpace[1].Bound, static(0)) + + testProg(t, "byte 0xff; store 1; load 1; return", AssemblerMaxVersion, Expect{1, "return arg 0 wanted type uint64 ..."}) } // TestProtoAsm confirms that the assembler will yell at you if you are From f4b2775f5528dc8cec285f4790fb77cbb5317759 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Thu, 13 Apr 2023 17:25:29 -0400 Subject: [PATCH 20/41] specify BoxKey for box ops --- data/transactions/logic/eval.go | 2 ++ data/transactions/logic/opcodes.go | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index dc2275eec5..ba1cc35578 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -838,6 +838,8 @@ func parseStackTypes(spec string) StackTypes { types[i] = StackMethodSelector case 'K': types[i] = StackStorageKey + case 'B': + types[i] = StackBoxKey default: panic(spec) } diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index cc14c47b28..5e8adc0294 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -629,13 +629,13 @@ var OpSpecs = []OpSpec{ {0xb8, "gitxna", opGitxna, proto(":a"), 6, immediates("t", "f", "i").field("f", &TxnArrayFields).only(ModeApp)}, // Unlimited Global Storage - Boxes - {0xb9, "box_create", opBoxCreate, proto("Ki:T"), boxVersion, only(ModeApp)}, - {0xba, "box_extract", opBoxExtract, proto("Kii:b"), boxVersion, only(ModeApp)}, - {0xbb, "box_replace", opBoxReplace, proto("Kib:"), boxVersion, only(ModeApp)}, - {0xbc, "box_del", opBoxDel, proto("K:T"), boxVersion, only(ModeApp)}, - {0xbd, "box_len", opBoxLen, proto("K:iT"), boxVersion, only(ModeApp)}, - {0xbe, "box_get", opBoxGet, proto("K:bT"), boxVersion, only(ModeApp)}, - {0xbf, "box_put", opBoxPut, proto("Kb:"), boxVersion, only(ModeApp)}, + {0xb9, "box_create", opBoxCreate, proto("Bi:T"), boxVersion, only(ModeApp)}, + {0xba, "box_extract", opBoxExtract, proto("Bii:b"), boxVersion, only(ModeApp)}, + {0xbb, "box_replace", opBoxReplace, proto("Bib:"), boxVersion, only(ModeApp)}, + {0xbc, "box_del", opBoxDel, proto("B:T"), boxVersion, only(ModeApp)}, + {0xbd, "box_len", opBoxLen, proto("B:iT"), boxVersion, only(ModeApp)}, + {0xbe, "box_get", opBoxGet, proto("B:bT"), boxVersion, only(ModeApp)}, + {0xbf, "box_put", opBoxPut, proto("Bb:"), boxVersion, only(ModeApp)}, // Dynamic indexing {0xc0, "txnas", opTxnas, proto("i:a"), 5, field("f", &TxnArrayFields)}, From 1e938b2422d0b28e9cf65bce81bfbf0fc90afe49 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Thu, 13 Apr 2023 17:57:52 -0400 Subject: [PATCH 21/41] fix tests --- data/transactions/logic/assembler.go | 6 ++++-- data/transactions/logic/eval.go | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index cca42591aa..5147df9dec 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1316,8 +1316,10 @@ func typeFrameBury(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes func typeEquals(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { top := len(pgm.stack) - 1 if top >= 0 { - //Require arg0 and arg1 to have same type - return StackTypes{pgm.stack[top], pgm.stack[top]}, nil, nil + // Require arg0 and arg1 to have same avm type + // but the bounds shouldn't matter + widened := pgm.stack[top].widened() + return StackTypes{widened, widened}, nil, nil } return nil, nil, nil } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index ba1cc35578..f90638e890 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -749,6 +749,20 @@ func (st StackType) narrowed(bounds [2]uint64) StackType { return NewStackType(st.AVMType, bounds) } +func (st StackType) widened() StackType { + // Take only the avm type + switch st.AVMType { + case avmBytes: + return StackBytes + case avmUint64: + return StackUint64 + case avmAny: + return StackAny + default: + panic(fmt.Sprintf("What are you tyring to widen?: %+v", st)) + } +} + // AssignableTo indicates whether a value typed st might be // usable as other. func (st StackType) AssignableTo(other StackType) bool { From acb52fd89c712c537f708f13ad61bbfc5a798d28 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 14 Apr 2023 08:14:39 -0400 Subject: [PATCH 22/41] regen langspec --- data/transactions/logic/TEAL_opcodes.md | 14 +++++++------- data/transactions/logic/evalAppTxn_test.go | 2 +- data/transactions/logic/langspec.json | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index eab2f0bd51..e4d9fee7cc 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -1417,7 +1417,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## box_create - Opcode: 0xb9 -- Stack: ..., A: key, B: uint64 → ..., bool +- Stack: ..., A: bkey, B: uint64 → ..., bool - create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1 - Availability: v8 - Mode: Application @@ -1427,7 +1427,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_extract - Opcode: 0xba -- Stack: ..., A: key, B: uint64, C: uint64 → ..., []byte +- Stack: ..., A: bkey, B: uint64, C: uint64 → ..., []byte - read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. - Availability: v8 - Mode: Application @@ -1435,7 +1435,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_replace - Opcode: 0xbb -- Stack: ..., A: key, B: uint64, C: []byte → ... +- Stack: ..., A: bkey, B: uint64, C: []byte → ... - write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. - Availability: v8 - Mode: Application @@ -1443,7 +1443,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_del - Opcode: 0xbc -- Stack: ..., A: key → ..., bool +- Stack: ..., A: bkey → ..., bool - delete box named A if it exists. Return 1 if A existed, 0 otherwise - Availability: v8 - Mode: Application @@ -1451,7 +1451,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_len - Opcode: 0xbd -- Stack: ..., A: key → ..., X: uint64, Y: bool +- Stack: ..., A: bkey → ..., X: uint64, Y: bool - X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0. - Availability: v8 - Mode: Application @@ -1459,7 +1459,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_get - Opcode: 0xbe -- Stack: ..., A: key → ..., X: []byte, Y: bool +- Stack: ..., A: bkey → ..., X: []byte, Y: bool - X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0. - Availability: v8 - Mode: Application @@ -1469,7 +1469,7 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo ## box_put - Opcode: 0xbf -- Stack: ..., A: key, B: []byte → ... +- Stack: ..., A: bkey, B: []byte → ... - replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist - Availability: v8 - Mode: Application diff --git a/data/transactions/logic/evalAppTxn_test.go b/data/transactions/logic/evalAppTxn_test.go index 84040dddf1..763d00381f 100644 --- a/data/transactions/logic/evalAppTxn_test.go +++ b/data/transactions/logic/evalAppTxn_test.go @@ -837,7 +837,7 @@ func TestFieldSetting(t *testing.T) { TestApp(t, "itxn_begin; int 0; itxn_field Nonparticipation; int 1", ep) TestApp(t, "itxn_begin; int 1; itxn_field Nonparticipation; int 1", ep) - TestApp(t, "itxn_begin; int 2; itxn_field Nonparticipation; int 1", ep, + TestApp(t, NoTrack("itxn_begin; int 2; itxn_field Nonparticipation; int 1"), ep, "boolean is neither 1 nor 0") TestApp(t, "itxn_begin; int 32; bzero; itxn_field RekeyTo; int 1", ep) diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index b727ad1b8e..1f825561dd 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -3550,7 +3550,7 @@ "Opcode": 185, "Name": "box_create", "Args": [ - "key", + "bkey", "uint64" ], "Returns": [ @@ -3568,7 +3568,7 @@ "Opcode": 186, "Name": "box_extract", "Args": [ - "key", + "bkey", "uint64", "uint64" ], @@ -3586,7 +3586,7 @@ "Opcode": 187, "Name": "box_replace", "Args": [ - "key", + "bkey", "uint64", "[]byte" ], @@ -3601,7 +3601,7 @@ "Opcode": 188, "Name": "box_del", "Args": [ - "key" + "bkey" ], "Returns": [ "bool" @@ -3617,7 +3617,7 @@ "Opcode": 189, "Name": "box_len", "Args": [ - "key" + "bkey" ], "Returns": [ "uint64", @@ -3634,7 +3634,7 @@ "Opcode": 190, "Name": "box_get", "Args": [ - "key" + "bkey" ], "Returns": [ "[]byte", @@ -3652,7 +3652,7 @@ "Opcode": 191, "Name": "box_put", "Args": [ - "key", + "bkey", "[]byte" ], "Size": 1, From ec25ba5a4f2804222233b21b78e034c57943bbb2 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 14 Apr 2023 11:51:26 -0400 Subject: [PATCH 23/41] tmp --- data/transactions/logic/assembler.go | 13 ++++++++++++- data/transactions/logic/assembler_test.go | 15 ++++++++++----- data/transactions/logic/eval.go | 11 +++++++++-- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 5147df9dec..8bd6ba90d1 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1434,6 +1434,17 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e if top < 0 { return nil, nil, nil } + + // If the index of the scratch slot is a const + // we can modify only that scratch slots type + if top >= 1 { + idx, isConst := pgm.stack[top-1].constant() + if isConst { + pgm.scratchSpace[idx] = pgm.stack[top] + return nil, nil, nil + } + } + for i := range pgm.scratchSpace { // We can't know what slot stacktop is being stored in // so we union it into all scratch slots @@ -1718,7 +1729,7 @@ func (le lineError) Unwrap() error { } func typecheck(expected, got StackType) bool { - return got.AssignableTo(expected) + return got.assignableTo(expected) } // newline not included since handled in scanner diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index c7d6c76b53..7130a5dc97 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2636,12 +2636,17 @@ func TestScratchBounds(t *testing.T) { os = testProg(t, "int 5; store 1; load 1; int 1; int 1; stores; return;", AssemblerMaxVersion) sv = os.known.scratchSpace[1] require.Equal(t, sv.AVMType, avmUint64) - require.ElementsMatch(t, sv.Bound, bound(1, 5)) + require.ElementsMatch(t, sv.Bound, bound(1, 1)) - os = testProg(t, "int 5; store 1; load 1; int 1; byte 0xff; stores; return;", AssemblerMaxVersion) - sv = os.known.scratchSpace[1] - require.Equal(t, sv.AVMType, avmAny) - require.ElementsMatch(t, os.known.scratchSpace[1].Bound, static(0)) + // Since stores may not know at assembly time + //os = testProg(t, "int 5; store 1; load 1; int 1; byte 0xff; stores; return;", AssemblerMaxVersion) + //sv = os.known.scratchSpace[1] + //require.Equal(t, sv.AVMType, avmBytes) + //require.ElementsMatch(t, sv.Bound, static(0)) + + //osv := os.known.scratchSpace[0] + //require.Equal(t, osv.AVMType, avmAny) + //require.ElementsMatch(t, osv.Bound, static(0)) testProg(t, "byte 0xff; store 1; load 1; return", AssemblerMaxVersion, Expect{1, "return arg 0 wanted type uint64 ..."}) } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index f90638e890..1b6e4c50bc 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -763,9 +763,16 @@ func (st StackType) widened() StackType { } } -// AssignableTo indicates whether a value typed st might be +func (st StackType) constant() (uint64, bool) { + if st.Bound[0] == st.Bound[1] { + return st.Bound[0], true + } + return 0, false +} + +// assignableTo indicates whether a value typed st might be // usable as other. -func (st StackType) AssignableTo(other StackType) bool { +func (st StackType) assignableTo(other StackType) bool { if st.AVMType == avmNone || other.AVMType == avmNone { return false } From 1f225d57cb035400842844ed350d54cd2a43fd84 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 14 Apr 2023 12:04:56 -0400 Subject: [PATCH 24/41] rename to assignable to overlaps --- data/transactions/logic/assembler.go | 6 +----- data/transactions/logic/assembler_test.go | 12 ++++++++++++ data/transactions/logic/eval.go | 19 ++++++++++--------- data/transactions/logic/evalStateful_test.go | 2 +- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 9bd0fb68d4..f92d426b51 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1742,10 +1742,6 @@ func (le lineError) Unwrap() error { return le.Err } -func typecheck(expected, got StackType) bool { - return got.assignableTo(expected) -} - // newline not included since handled in scanner var tokenSeparators = [256]bool{'\t': true, ' ': true, ';': true} @@ -1868,7 +1864,7 @@ func (ops *OpStream) trackStack(args StackTypes, returns StackTypes, instruction } else { ops.trace(", %s", argType) } - if !typecheck(argType, stype) { + if !stype.overlaps(argType) { ops.typeErrorf("%s arg %d wanted type %s got %s", strings.Join(instruction, " "), i, argType, stype) } diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 69517e608d..a79805ddbd 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2638,6 +2638,8 @@ func TestScratchBounds(t *testing.T) { require.Equal(t, sv.AVMType, avmUint64) require.ElementsMatch(t, sv.Bound, bound(1, 1)) + // If the stack type for the slot index is a const, known at assembly time + // we can be sure of the slot we need to update os = testProg(t, "int 5; store 1; load 1; int 1; byte 0xff; stores; return;", AssemblerMaxVersion) sv = os.known.scratchSpace[1] require.Equal(t, sv.AVMType, avmBytes) @@ -2647,6 +2649,16 @@ func TestScratchBounds(t *testing.T) { require.Equal(t, osv.AVMType, avmUint64) require.ElementsMatch(t, osv.Bound, static(0)) + // Otherwise, we just union all stack types with the incoming type + os = testProg(t, "int 5; store 1; load 1; byte 0xaa; btoi; byte 0xff; stores; return;", AssemblerMaxVersion) + sv = os.known.scratchSpace[1] + require.Equal(t, sv.AVMType, avmAny) + require.ElementsMatch(t, sv.Bound, static(0)) + + osv = os.known.scratchSpace[0] + require.Equal(t, osv.AVMType, avmAny) + require.ElementsMatch(t, osv.Bound, static(0)) + testProg(t, "byte 0xff; store 1; load 1; return", AssemblerMaxVersion, Expect{1, "return arg 0 wanted type uint64 ..."}) } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 2a9204953f..4b96d729a8 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -787,29 +787,30 @@ func (st StackType) constant() (uint64, bool) { return 0, false } -// assignableTo indicates whether a value typed st might be -// usable as other. -func (st StackType) assignableTo(other StackType) bool { - if st.AVMType == avmNone || other.AVMType == avmNone { +// overlaps checks if there is enough overlap +// between the given types that the receiver can +// possible fit in the expected type +func (st StackType) overlaps(expected StackType) bool { + if st.AVMType == avmNone || expected.AVMType == avmNone { return false } - if st.AVMType == avmAny || other.AVMType == avmAny { + if st.AVMType == avmAny || expected.AVMType == avmAny { return true } // By now, both are either uint or bytes // and must match - if st.AVMType != other.AVMType { + if st.AVMType != expected.AVMType { return false } // Same type now // Check if our constraints will satisfy the other type smin, smax := st.Bound[0], st.Bound[1] - omin, omax := other.Bound[0], other.Bound[1] + emin, emax := expected.Bound[0], expected.Bound[1] - return smin <= omax && smax >= omin + return smin <= emax && smax >= emin } func (st StackType) String() string { @@ -830,7 +831,7 @@ type StackTypes []StackType // Reverse returns the StackTypes in reverse order // useful for displaying the stack as an op sees it -func (st StackTypes) Reverse() StackTypes { +func (st StackTypes) Reversed() StackTypes { nst := make(StackTypes, len(st)) for idx := 0; idx < len(st); idx++ { nst[idx] = st[len(st)-1-idx] diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index a710022c9b..0f912d5a76 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -2799,7 +2799,7 @@ func TestReturnTypes(t *testing.T) { stackType := cx.stack[i].stackType() retType := spec.Return.Types[i] require.True( - t, typecheck(retType, stackType), + t, stackType.overlaps(retType), "%s expected to return %s but actual is %s", spec.Name, retType, stackType, ) } From 4ec1e6cff56ee3c8738e3f14449d4ea2a060625a Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 14 Apr 2023 12:13:42 -0400 Subject: [PATCH 25/41] notrack on more tests that fail at assembly time --- data/transactions/logic/evalAppTxn_test.go | 4 ++-- data/transactions/logic/evalStateful_test.go | 4 ++-- data/transactions/logic/eval_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/transactions/logic/evalAppTxn_test.go b/data/transactions/logic/evalAppTxn_test.go index bc97a2e9bb..026194f295 100644 --- a/data/transactions/logic/evalAppTxn_test.go +++ b/data/transactions/logic/evalAppTxn_test.go @@ -108,8 +108,8 @@ func TestFieldTypes(t *testing.T) { // 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") + TestApp(t, NoTrack("itxn_begin; byte \"\"; itxn_field CloseRemainderTo;"), ep, "not an address") + TestApp(t, NoTrack("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") diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 0f912d5a76..5b7d92ea9d 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -1743,12 +1743,12 @@ func TestAppLocalGlobalErrorCases(t *testing.T) { ep, tx, ledger := makeSampleEnv() ledger.NewApp(tx.Sender, 888, basics.AppParams{}) - testApp(t, fmt.Sprintf(`byte "%v"; int 1; app_global_put; int 1`, strings.Repeat("v", ep.Proto.MaxAppKeyLen+1)), ep, "key too long") + testApp(t, NoTrack(fmt.Sprintf(`byte "%v"; int 1; app_global_put; int 1`, strings.Repeat("v", ep.Proto.MaxAppKeyLen+1))), ep, "key too long") testApp(t, fmt.Sprintf(`byte "%v"; int 1; app_global_put; int 1`, strings.Repeat("v", ep.Proto.MaxAppKeyLen)), ep) ledger.NewLocals(tx.Sender, 888) - testApp(t, fmt.Sprintf(`txn Sender; byte "%v"; int 1; app_local_put; int 1`, strings.Repeat("v", ep.Proto.MaxAppKeyLen+1)), ep, "key too long") + testApp(t, NoTrack(fmt.Sprintf(`txn Sender; byte "%v"; int 1; app_local_put; int 1`, strings.Repeat("v", ep.Proto.MaxAppKeyLen+1))), ep, "key too long") testApp(t, fmt.Sprintf(`txn Sender; byte "%v"; int 1; app_local_put; int 1`, strings.Repeat("v", ep.Proto.MaxAppKeyLen)), ep) diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 1fb0ad79cd..edfefc18a4 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -4918,7 +4918,7 @@ func TestBytesMath(t *testing.T) { // 64 byte long inputs are accepted, even if they produce longer outputs testAccepts(t, fmt.Sprintf("byte 0x%s; byte 0x10; b+; len; int 65; ==", effs), 4) // 65 byte inputs are not ok. - testPanics(t, fmt.Sprintf("byte 0x%s00; byte 0x10; b-; len; int 65; ==", effs), 4) + testPanics(t, NoTrack(fmt.Sprintf("byte 0x%s00; byte 0x10; b-; len; int 65; ==", effs)), 4) testAccepts(t, `byte 0x01; byte 0x01; b-; byte ""; ==`, 4) testAccepts(t, "byte 0x0200; byte 0x01; b-; byte 0x01FF; ==", 4) @@ -5022,7 +5022,7 @@ func TestBytesBits(t *testing.T) { testAccepts(t, "int 33; bzero; byte 0x000000000000000000000000000000000000000000000000000000000000000000; ==", 4) testAccepts(t, "int 4096; bzero; len; int 4096; ==", 4) - testPanics(t, "int 4097; bzero; len; int 4097; ==", 4) + testPanics(t, NoTrack("int 4097; bzero; len; int 4097; =="), 4) } func TestBytesConversions(t *testing.T) { From a1b05ad04979365810b405e66b6a371cad83b0a4 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 14 Apr 2023 12:20:43 -0400 Subject: [PATCH 26/41] adding more refinement to loads if we know its a const --- data/transactions/logic/assembler.go | 10 ++++++++++ data/transactions/logic/assembler_test.go | 6 ++++-- data/transactions/logic/box_test.go | 8 ++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index f92d426b51..50be9253f7 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1477,6 +1477,16 @@ func typeProto(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, er } func typeLoads(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { + + top := len(pgm.stack) - 1 + if top < 0 { + return nil, nil, nil + } + + if val, isConst := pgm.stack[top].constant(); isConst { + return nil, StackTypes{pgm.scratchSpace[val]}, nil + } + 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 diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index a79805ddbd..4fb7655783 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2609,8 +2609,10 @@ func TestScratchTypeCheck(t *testing.T) { testProg(t, "byte 0x01; store 0; load 0; int 1; +", AssemblerMaxVersion, Expect{1, "+ 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{1, "btoi arg 0..."}) - // Loads doesn't know the type when slot types vary - testProg(t, "byte 0x01; store 0; int 1; loads; btoi", AssemblerMaxVersion) + // Loads only knows the type when slot is a const + testProg(t, "byte 0x01; store 0; int 1; loads; btoi", AssemblerMaxVersion, Expect{1, "btoi arg 0..."}) + // Loads doesnt know the type if its the result of some other expression where we lose information + testProg(t, "byte 0x01; store 0; load 0; btoi; 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{1, "+ arg 0..."}) // ScratchSpace should reset after hitting label in deadcode diff --git a/data/transactions/logic/box_test.go b/data/transactions/logic/box_test.go index f73cdf52a9..fa139ce8dd 100644 --- a/data/transactions/logic/box_test.go +++ b/data/transactions/logic/box_test.go @@ -90,10 +90,10 @@ func TestBoxNewBad(t *testing.T) { long := strings.Repeat("x", 65) txn.Boxes = []transactions.BoxRef{{Name: []byte(long)}} - logic.TestApp(t, fmt.Sprintf(`byte "%s"; int 1000; box_create`, long), ep, "name too long") + logic.TestApp(t, logic.NoTrack(fmt.Sprintf(`byte "%s"; int 1000; box_create`, long)), ep, "name too long") txn.Boxes = []transactions.BoxRef{{Name: []byte("")}} // irrelevant, zero check comes first anyway - logic.TestApp(t, `byte ""; int 1000; box_create`, ep, "zero length") + logic.TestApp(t, logic.NoTrack(`byte ""; int 1000; box_create`), ep, "zero length") } func TestBoxReadWrite(t *testing.T) { @@ -531,7 +531,7 @@ func TestEarlyPanics(t *testing.T) { t.Parallel() ep, _, l := logic.MakeSampleEnv() l.NewApp(basics.Address{}, 888, basics.AppParams{}) - logic.TestApp(t, fmt.Sprintf(program, ""), ep, "zero length") + logic.TestApp(t, logic.NoTrack(fmt.Sprintf(program, "")), ep, "zero length") }) } @@ -542,7 +542,7 @@ func TestEarlyPanics(t *testing.T) { t.Parallel() ep, _, l := logic.MakeSampleEnv() l.NewApp(basics.Address{}, 888, basics.AppParams{}) - logic.TestApp(t, fmt.Sprintf(program, big), ep, "name too long") + logic.TestApp(t, logic.NoTrack(fmt.Sprintf(program, big)), ep, "name too long") }) } From 12d7ade40292cb894edf3e71106522c2b5f43a8e Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 14 Apr 2023 12:42:59 -0400 Subject: [PATCH 27/41] improve naming, fix more tests --- data/transactions/logic/assembler.go | 8 ++++++-- data/transactions/logic/assembler_test.go | 4 ++-- data/transactions/logic/eval.go | 25 ++++++++++++----------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 50be9253f7..1c49f7e449 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1545,8 +1545,12 @@ func typePushInts(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, func typePushInt(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, error) { types := make(StackTypes, len(args)) for i := range types { - val, _ := strconv.ParseUint(args[i], 10, 64) - types[i] = NewStackType(avmUint64, bound(val, val)) + val, err := strconv.ParseUint(args[i], 10, 64) + if err != nil { + types[i] = StackUint64 + } else { + types[i] = NewStackType(avmUint64, bound(val, val)) + } } return nil, types, nil } diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 4fb7655783..d21cebc6b7 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2713,7 +2713,7 @@ func TestTxTypes(t *testing.T) { partitiontest.PartitionTest(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; int 1; itxn_field Sender", 5, Expect{1, "...wanted type addr got 1"}) 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) @@ -2891,7 +2891,7 @@ func TestReplacePseudo(t *testing.T) { testProg(t, "byte 0x0000; byte 0x1234; replace 0", v) testProg(t, "byte 0x0000; int 0; byte 0x1234; replace", v) testProg(t, "byte 0x0000; byte 0x1234; replace", v, Expect{1, "replace without immediates expects 3 stack arguments but stack height is 2"}) - testProg(t, "byte 0x0000; int 0; byte 0x1234; replace 0", v, Expect{1, "replace 0 arg 0 wanted type []byte got uint64"}) + testProg(t, "byte 0x0000; int 0; byte 0x1234; replace 0", v, Expect{1, "replace 0 arg 0 wanted type []byte got 0"}) } } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 4b96d729a8..2a437d5f13 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -733,6 +733,18 @@ type StackType struct { // NewStackType Initializes a new StackType with fields passed func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { name := at.String() + + // It's static, set the name to show + // the static value + if bounds[0] == bounds[1] { + switch at { + case avmBytes: + name = fmt.Sprintf("[%d]byte", bounds[0]) + case avmUint64: + name = fmt.Sprintf("%d", bounds[0]) + } + } + if len(stname) > 0 { name = stname[0] } @@ -743,7 +755,6 @@ func NewStackType(at avmType, bounds [2]uint64, stname ...string) StackType { func (st StackType) union(b StackType) StackType { // TODO: Can we ever receive one or the other // as None? should that be a panic? - if st.AVMType != b.AVMType { return StackAny } @@ -753,16 +764,6 @@ func (st StackType) union(b StackType) StackType { } func (st StackType) narrowed(bounds [2]uint64) StackType { - // It's static, set the name to show - // the static value - if bounds[0] == bounds[1] { - switch st.AVMType { - case avmBytes: - return NewStackType(st.AVMType, bounds, fmt.Sprintf("[%d]byte", bounds[0])) - case avmUint64: - return NewStackType(st.AVMType, bounds, fmt.Sprintf("%d", bounds[0])) - } - } return NewStackType(st.AVMType, bounds) } @@ -829,7 +830,7 @@ func (st StackType) Typed() bool { // StackTypes is an alias for a list of StackType with syntactic sugar type StackTypes []StackType -// Reverse returns the StackTypes in reverse order +// Reversed returns the StackTypes in reverse order // useful for displaying the stack as an op sees it func (st StackTypes) Reversed() StackTypes { nst := make(StackTypes, len(st)) From b1bab65cced3d4d81a498208905e66dbfa18e0eb Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 14 Apr 2023 13:12:41 -0400 Subject: [PATCH 28/41] add check for nones in docs --- cmd/opdoc/opdoc.go | 23 +++++++++++--- data/transactions/logic/eval.go | 56 +++++++++++---------------------- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index d5ab8bd10f..869f7a0ff0 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -259,13 +259,26 @@ type LanguageSpec struct { Ops []OpRecord } -func typeStrings(types []logic.StackType) []string { - out := []string{} - for _, t := range types { - if t.String() != "none" { - out = append(out, t.String()) +func typeStrings(types logic.StackTypes) []string { + var ( + out = make([]string, len(types)) + allNones = true + ) + for idx, t := range types { + out[idx] = t.String() + if out[idx] != "none" { + allNones = false } } + + // If all the types are none, we just return + // an empty array, otherwise leave the nones + // in so we don't break the indices by omitting + // a valid none in a fields array + if allNones { + return []string{} + } + return out } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 2a437d5f13..1f895c0d67 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -677,29 +677,28 @@ var ( StackMethodSelector = NewStackType(avmBytes, static(4), "method") // StackStorageKey represents a bytestring that can be used as a key to some storage (global/local/box) StackStorageKey = NewStackType(avmBytes, bound(0, 64), "key") - // StackBoxKey represents a bytestring that can be used as a key to a box - StackBoxKey = NewStackType(avmBytes, bound(1, 64), "name") + // StackBoxName represents a bytestring that can be used as a key to a box + StackBoxName = NewStackType(avmBytes, bound(1, 64), "name") // StackZeroUint64 is a StackUint64 with a minimum value of 0 and a maximum value of 0 StackZeroUint64 = NewStackType(avmUint64, bound(0, 0), "0") // StackZeroBytes is a StackBytes with a minimum length of 0 and a maximum length of 0 StackZeroBytes = NewStackType(avmUint64, bound(0, 0), "''") - // AllStackTypes is a list of all the stack types we recognize + // AllStackTypes is a map of all the stack types we recognize // so that we can iterate over them in doc prep - AllStackTypes = []StackType{ - StackUint64, - StackBytes, - StackAny, - StackNone, - StackBoolean, - StackBytes32, - StackBigInt, - StackMethodSelector, - StackStorageKey, - StackBoxKey, - StackZeroUint64, - StackZeroBytes, + // and use them for opcode proto shorthand + AllStackTypes = map[rune]StackType{ + 'a': StackAny, + 'b': StackBytes, + 'i': StackUint64, + 'x': StackNone, + 'I': StackBigInt, + 'T': StackBoolean, + 'H': StackBytes32, + 'M': StackMethodSelector, + 'K': StackStorageKey, + 'N': StackBoxName, } ) @@ -859,30 +858,11 @@ func parseStackTypes(spec string) StackTypes { } types := make(StackTypes, len(spec)) for i, letter := range spec { - switch letter { - case 'a': - types[i] = StackAny - case 'b': - types[i] = StackBytes - case 'i': - types[i] = StackUint64 - case 'x': - types[i] = StackNone - case 'I': - types[i] = StackBigInt - case 'T': - types[i] = StackBoolean - case 'H': - types[i] = StackBytes32 - case 'M': - types[i] = StackMethodSelector - case 'K': - types[i] = StackStorageKey - case 'N': - types[i] = StackBoxKey - default: + st, ok := AllStackTypes[letter] + if !ok { panic(spec) } + types[i] = st } return types } From 734d9f441bdf74b69621fd0cbf3f97cc1dab3994 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 14 Apr 2023 14:49:06 -0400 Subject: [PATCH 29/41] adding named types to readme --- cmd/opdoc/opdoc.go | 41 +++++++++++ data/transactions/logic/README.md | 25 +++++++ data/transactions/logic/README_in.md | 11 +++ data/transactions/logic/eval.go | 1 + data/transactions/logic/langspec.json | 101 ++++++++++++++++++++++++++ 5 files changed, 179 insertions(+) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index 869f7a0ff0..1b65c421b2 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "os" + "sort" "strings" "github.com/algorand/go-algorand/config" @@ -50,6 +51,22 @@ func markdownTableEscape(x string) string { return strings.ReplaceAll(x, "|", "\\|") } +func namedStackTypesMarkdown(out io.Writer) { + fmt.Fprintf(out, "## StackTypes\n\n") + fmt.Fprintf(out, "| Name | Bound | AVM Type |\n") + fmt.Fprintf(out, "| ---- | ---- | -------- |\n") + + // Create a slice so we can sort it later + rows := []string{} + for _, st := range logic.AllStackTypes { + bound := fmt.Sprintf("%d - %d", st.Bound[0], st.Bound[1]) + rows = append(rows, fmt.Sprintf("| %s | %s | %s |", st.String(), bound, st.AVMType.String())) + } + fmt.Fprint(out, strings.Join(sort.StringSlice(rows), "\n")) + + out.Write([]byte("\n")) +} + func integerConstantsTableMarkdown(out io.Writer) { fmt.Fprintf(out, "#### OnComplete\n\n") fmt.Fprintf(out, "%s\n\n", logic.OnCompletionPreamble) @@ -252,10 +269,18 @@ type OpRecord struct { Groups []string } +type namedType struct { + Name string + Abbreviation string + Bounds []uint64 + AVMType string +} + // LanguageSpec records the ops of the language at some version type LanguageSpec struct { EvalMaxVersion int LogicSigVersion uint64 + NamedTypes []namedType Ops []OpRecord } @@ -349,9 +374,21 @@ func buildLanguageSpec(opGroups map[string][]string) *LanguageSpec { records[i].Groups = opGroups[spec.Name] records[i].IntroducedVersion = spec.Version } + named := []namedType{} + for abbr, t := range logic.AllStackTypes { + named = append(named, namedType{ + Name: t.String(), + Bounds: []uint64{t.Bound[0], t.Bound[1]}, + Abbreviation: string(abbr), + AVMType: t.AVMType.String(), + }) + } + sort.Slice(named, func(i, j int) bool { return strings.Compare(named[i].Name, named[j].Name) > 0 }) + return &LanguageSpec{ EvalMaxVersion: docVersion, LogicSigVersion: config.Consensus[protocol.ConsensusCurrentVersion].LogicSigVersion, + NamedTypes: named, Ops: records, } } @@ -384,6 +421,10 @@ func main() { integerConstantsTableMarkdown(constants) constants.Close() + namedTypes := create("named_stack_types.md") + namedStackTypesMarkdown(namedTypes) + namedTypes.Close() + written := make(map[string]bool) opSpecs := logic.OpcodesByVersion(uint64(docVersion)) for _, spec := range opSpecs { diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index 16d139f917..f1d9bb2744 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -40,6 +40,31 @@ has fewer than two elements, the operation fails. Some operations, like `frame_dig` and `proto` could fail because of an attempt to access above the current stack. +## Stack Types + +While every element of the stack is restricted to the types `uint64` and `bytes`, +the values of these types may be known to be bounded. The more common bounded types are +named to provide more semantic information in the documentation. They're also used during +assembly time to do type checking and to provide more informative error messages. + + +## StackTypes + +| Name | Bound | AVM Type | +| ---- | ---- | -------- | +| any | 0 - 0 | any | +| []byte | 0 - 4096 | []byte | +| uint64 | 0 - 18446744073709551615 | uint64 | +| none | 0 - 0 | none | +| name | 1 - 64 | []byte | +| key | 0 - 64 | []byte | +| addr | 32 - 32 | []byte | +| bigint | 0 - 64 | []byte | +| bool | 0 - 1 | uint64 | +| [32]byte | 32 - 32 | []byte | +| method | 4 - 4 | []byte | + + ## Scratch Space In addition to the stack there are 256 positions of scratch diff --git a/data/transactions/logic/README_in.md b/data/transactions/logic/README_in.md index ff5b524c80..23e782d7b0 100644 --- a/data/transactions/logic/README_in.md +++ b/data/transactions/logic/README_in.md @@ -40,6 +40,17 @@ has fewer than two elements, the operation fails. Some operations, like `frame_dig` and `proto` could fail because of an attempt to access above the current stack. +## Stack Types + +While every element of the stack is restricted to the types `uint64` and `bytes`, +the values of these types may be known to be bounded. The more common bounded types are +named to provide more semantic information in the documentation. They're also used during +assembly time to do type checking and to provide more informative error messages. + + +@@ named_stack_types.md @@ + + ## Scratch Space In addition to the stack there are 256 positions of scratch diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 1f895c0d67..20dfff9eab 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -693,6 +693,7 @@ var ( 'b': StackBytes, 'i': StackUint64, 'x': StackNone, + 'A': StackAddress, 'I': StackBigInt, 'T': StackBoolean, 'H': StackBytes32, diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index c3171c6f8c..a28ab527c1 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -1,6 +1,107 @@ { "EvalMaxVersion": 8, "LogicSigVersion": 8, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bounds": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bounds": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "name", + "Abbreviation": "N", + "Bounds": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bounds": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "key", + "Abbreviation": "K", + "Bounds": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bounds": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bounds": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bounds": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "addr", + "Abbreviation": "A", + "Bounds": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bounds": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bounds": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], "Ops": [ { "Opcode": 0, From 3070d168d59d4217cb1c25a69f901dca16cefc9a Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Sat, 15 Apr 2023 07:59:14 -0400 Subject: [PATCH 30/41] maybe sorted --- cmd/opdoc/opdoc.go | 48 +++++++++++++-------------- data/transactions/logic/README.md | 15 +++++---- data/transactions/logic/langspec.json | 22 ++++++------ 3 files changed, 43 insertions(+), 42 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index 1b3c3493a5..3eb5e92fb2 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -51,19 +51,15 @@ func markdownTableEscape(x string) string { return strings.ReplaceAll(x, "|", "\\|") } -func namedStackTypesMarkdown(out io.Writer) { +func namedStackTypesMarkdown(out io.Writer, stackTypes []namedType) { fmt.Fprintf(out, "#### StackType Definitions\n\n") fmt.Fprintf(out, "| Name | Bound | AVM Type |\n") fmt.Fprintf(out, "| ---- | ---- | -------- |\n") - // Create a slice so we can sort it later - rows := []string{} - for _, st := range logic.AllStackTypes { + for _, st := range stackTypes { bound := fmt.Sprintf("%d - %d", st.Bound[0], st.Bound[1]) - rows = append(rows, fmt.Sprintf("| %s | %s | %s |", st.String(), bound, st.AVMType.String())) + fmt.Fprintf(out, "| %s | %s | %s |\n", st.Name, bound, st.AVMType) } - fmt.Fprint(out, strings.Join(sort.StringSlice(rows), "\n")) - out.Write([]byte("\n")) } @@ -272,7 +268,7 @@ type OpRecord struct { type namedType struct { Name string Abbreviation string - Bounds []uint64 + Bound []uint64 AVMType string } @@ -358,7 +354,7 @@ func argEnums(name string) ([]string, []string) { } } -func buildLanguageSpec(opGroups map[string][]string) *LanguageSpec { +func buildLanguageSpec(opGroups map[string][]string, namedTypes []namedType) *LanguageSpec { opSpecs := logic.OpcodesByVersion(uint64(docVersion)) records := make([]OpRecord, len(opSpecs)) for i, spec := range opSpecs { @@ -374,21 +370,11 @@ func buildLanguageSpec(opGroups map[string][]string) *LanguageSpec { records[i].Groups = opGroups[spec.Name] records[i].IntroducedVersion = spec.Version } - named := []namedType{} - for abbr, t := range logic.AllStackTypes { - named = append(named, namedType{ - Name: t.String(), - Bounds: []uint64{t.Bound[0], t.Bound[1]}, - Abbreviation: string(abbr), - AVMType: t.AVMType.String(), - }) - } - sort.Slice(named, func(i, j int) bool { return strings.Compare(named[i].Name, named[j].Name) > 0 }) return &LanguageSpec{ EvalMaxVersion: docVersion, LogicSigVersion: config.Consensus[protocol.ConsensusCurrentVersion].LogicSigVersion, - NamedTypes: named, + NamedTypes: namedTypes, Ops: records, } } @@ -421,9 +407,20 @@ func main() { integerConstantsTableMarkdown(constants) constants.Close() - namedTypes := create("named_stack_types.md") - namedStackTypesMarkdown(namedTypes) - namedTypes.Close() + named := []namedType{} + for abbr, t := range logic.AllStackTypes { + named = append(named, namedType{ + Name: t.String(), + Bound: []uint64{t.Bound[0], t.Bound[1]}, + Abbreviation: string(abbr), + AVMType: t.AVMType.String(), + }) + } + sort.Slice(named, func(i, j int) bool { return strings.Compare(named[i].Name, named[j].Name) > 0 }) + + namedStackTypes := create("named_stack_types.md") + namedStackTypesMarkdown(namedStackTypes, named) + namedStackTypes.Close() written := make(map[string]bool) opSpecs := logic.OpcodesByVersion(uint64(docVersion)) @@ -441,7 +438,10 @@ func main() { langspecjs := create("langspec.json") enc := json.NewEncoder(langspecjs) enc.SetIndent("", " ") - enc.Encode(buildLanguageSpec(opGroups)) + err := enc.Encode(buildLanguageSpec(opGroups, named)) + if err != nil { + panic(err.Error()) + } langspecjs.Close() tealtm := create("teal.tmLanguage.json") diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index 276f639d92..376c0390d4 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -52,17 +52,18 @@ assembly time to do type checking and to provide more informative error messages | Name | Bound | AVM Type | | ---- | ---- | -------- | -| []byte | 0 - 4096 | []byte | -| bigint | 0 - 64 | []byte | -| [32]byte | 32 - 32 | []byte | +| uint64 | 0 - 18446744073709551615 | uint64 | +| none | 0 - 0 | none | +| name | 1 - 64 | []byte | | method | 4 - 4 | []byte | | key | 0 - 64 | []byte | -| name | 1 - 64 | []byte | +| bool | 0 - 1 | uint64 | +| bigint | 0 - 64 | []byte | | any | 0 - 0 | any | -| uint64 | 0 - 18446744073709551615 | uint64 | -| none | 0 - 0 | none | | addr | 32 - 32 | []byte | -| bool | 0 - 1 | uint64 | +| []byte | 0 - 4096 | []byte | +| [32]byte | 32 - 32 | []byte | + ## Scratch Space diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index a28ab527c1..bd34ec5ed5 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -5,7 +5,7 @@ { "Name": "uint64", "Abbreviation": "i", - "Bounds": [ + "Bound": [ 0, 18446744073709551615 ], @@ -14,7 +14,7 @@ { "Name": "none", "Abbreviation": "x", - "Bounds": [ + "Bound": [ 0, 0 ], @@ -23,7 +23,7 @@ { "Name": "name", "Abbreviation": "N", - "Bounds": [ + "Bound": [ 1, 64 ], @@ -32,7 +32,7 @@ { "Name": "method", "Abbreviation": "M", - "Bounds": [ + "Bound": [ 4, 4 ], @@ -41,7 +41,7 @@ { "Name": "key", "Abbreviation": "K", - "Bounds": [ + "Bound": [ 0, 64 ], @@ -50,7 +50,7 @@ { "Name": "bool", "Abbreviation": "T", - "Bounds": [ + "Bound": [ 0, 1 ], @@ -59,7 +59,7 @@ { "Name": "bigint", "Abbreviation": "I", - "Bounds": [ + "Bound": [ 0, 64 ], @@ -68,7 +68,7 @@ { "Name": "any", "Abbreviation": "a", - "Bounds": [ + "Bound": [ 0, 0 ], @@ -77,7 +77,7 @@ { "Name": "addr", "Abbreviation": "A", - "Bounds": [ + "Bound": [ 32, 32 ], @@ -86,7 +86,7 @@ { "Name": "[]byte", "Abbreviation": "b", - "Bounds": [ + "Bound": [ 0, 4096 ], @@ -95,7 +95,7 @@ { "Name": "[32]byte", "Abbreviation": "H", - "Bounds": [ + "Bound": [ 32, 32 ], From 88a3cb7bedf590bb4d2b236593fc6897a55eb272 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 12:05:44 -0400 Subject: [PATCH 31/41] Update cmd/opdoc/opdoc.go Co-authored-by: John Jannotti --- cmd/opdoc/opdoc.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index 3eb5e92fb2..1ee18ad28d 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -281,10 +281,8 @@ type LanguageSpec struct { } func typeStrings(types logic.StackTypes) []string { - var ( - out = make([]string, len(types)) - allNones = true - ) + out := make([]string, len(types)) + allNones := true for idx, t := range types { out[idx] = t.String() if out[idx] != "none" { From 3d2d4f309113ab96b4ebe591474d89a97a6a5fbc Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 12:07:52 -0400 Subject: [PATCH 32/41] Apply suggestions from code review Co-authored-by: John Jannotti --- cmd/opdoc/opdoc.go | 4 ++-- data/transactions/logic/assembler.go | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index 1ee18ad28d..ad7e3bd22c 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -295,7 +295,7 @@ func typeStrings(types logic.StackTypes) []string { // in so we don't break the indices by omitting // a valid none in a fields array if allNones { - return []string{} + return nil } return out @@ -405,7 +405,7 @@ func main() { integerConstantsTableMarkdown(constants) constants.Close() - named := []namedType{} + named := make([]namedType, 0, len(logic.AllStackTypes)) for abbr, t := range logic.AllStackTypes { named = append(named, namedType{ Name: t.String(), diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 1c49f7e449..0964614699 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1438,8 +1438,7 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e // If the index of the scratch slot is a const // we can modify only that scratch slots type if top >= 1 { - idx, isConst := pgm.stack[top-1].constant() - if isConst { + if idx, isConst := pgm.stack[top-1].constant(); isConst { pgm.scratchSpace[idx] = pgm.stack[top] return nil, nil, nil } From 99ea31bafe34efa2254458e12fac2bf9f50a5900 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 12:43:19 -0400 Subject: [PATCH 33/41] regen files --- data/transactions/logic/TEAL_opcodes.md | 26 ++++++++++----------- data/transactions/logic/langspec.json | 30 ++++++++++++------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 1b555f9852..5c55a2f087 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -843,7 +843,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address). Retu ## app_opted_in - Opcode: 0x61 -- Stack: ..., A, B: uint64 → ..., bool +- Stack: ..., A, B: uint64 → ..., uint64 - 1 if account A is opted in to application B, else 0 - Availability: v2 - Mode: Application @@ -853,7 +853,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), _ava ## app_local_get - Opcode: 0x62 -- Stack: ..., A, B: key → ..., any +- Stack: ..., A, B: []byte → ..., any - local state of the key B in the current application in account A - Availability: v2 - Mode: Application @@ -863,7 +863,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_local_get_ex - Opcode: 0x63 -- Stack: ..., A, B: uint64, C: key → ..., X: any, Y: bool +- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: uint64 - X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 - Availability: v2 - Mode: Application @@ -873,7 +873,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), _ava ## app_global_get - Opcode: 0x64 -- Stack: ..., A: key → ..., any +- Stack: ..., A: []byte → ..., any - global state of the key A in the current application - Availability: v2 - Mode: Application @@ -883,7 +883,7 @@ params: state key. Return: value. The value is zero (of type uint64) if the key ## app_global_get_ex - Opcode: 0x65 -- Stack: ..., A: uint64, B: key → ..., X: any, Y: bool +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: uint64 - X is the global state of application A, key B. Y is 1 if key existed, else 0 - Availability: v2 - Mode: Application @@ -893,7 +893,7 @@ params: Txn.ForeignApps offset (or, since v4, an _available_ application id), st ## app_local_put - Opcode: 0x66 -- Stack: ..., A, B: key, C → ... +- Stack: ..., A, B: []byte, C → ... - write C to key B in account A's local state of the current application - Availability: v2 - Mode: Application @@ -903,7 +903,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_global_put - Opcode: 0x67 -- Stack: ..., A: key, B → ... +- Stack: ..., A: []byte, B → ... - write B to key A in the global state of the current application - Availability: v2 - Mode: Application @@ -911,7 +911,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_local_del - Opcode: 0x68 -- Stack: ..., A, B: key → ... +- Stack: ..., A, B: []byte → ... - delete key B from account A's local state of the current application - Availability: v2 - Mode: Application @@ -923,7 +923,7 @@ Deleting a key which is already absent has no effect on the application local st ## app_global_del - Opcode: 0x69 -- Stack: ..., A: key → ... +- Stack: ..., A: []byte → ... - delete key A from the global state of the current application - Availability: v2 - Mode: Application @@ -935,7 +935,7 @@ Deleting a key which is already absent has no effect on the application global s ## asset_holding_get f - Opcode: 0x70 {uint8 asset holding field index} -- Stack: ..., A, B: uint64 → ..., X: any, Y: bool +- Stack: ..., A, B: uint64 → ..., X: any, Y: uint64 - X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 - Availability: v2 - Mode: Application @@ -953,7 +953,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or ## asset_params_get f - Opcode: 0x71 {uint8 asset params field index} -- Stack: ..., A: uint64 → ..., X: any, Y: bool +- Stack: ..., A: uint64 → ..., X: any, Y: uint64 - X is field F from asset A. Y is 1 if A exists, else 0 - Availability: v2 - Mode: Application @@ -981,7 +981,7 @@ params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: ## app_params_get f - Opcode: 0x72 {uint8 app params field index} -- Stack: ..., A: uint64 → ..., X: any, Y: bool +- Stack: ..., A: uint64 → ..., X: any, Y: uint64 - X is field F from app A. Y is 1 if A exists, else 0 - Availability: v5 - Mode: Application @@ -1006,7 +1006,7 @@ params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag ## acct_params_get f - Opcode: 0x73 {uint8 account params field index} -- Stack: ..., A → ..., X: any, Y: bool +- Stack: ..., A → ..., X: any, Y: uint64 - X is field F from account A. Y is 1 if A owns positive algos, else 0 - Availability: v6 - Mode: Application diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index b1de906c9f..497f42b267 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -2201,7 +2201,7 @@ "uint64" ], "Returns": [ - "bool" + "uint64" ], "Size": 1, "Doc": "1 if account A is opted in to application B, else 0", @@ -2216,7 +2216,7 @@ "Name": "app_local_get", "Args": [ "any", - "key" + "[]byte" ], "Returns": [ "any" @@ -2235,11 +2235,11 @@ "Args": [ "any", "uint64", - "key" + "[]byte" ], "Returns": [ "any", - "bool" + "uint64" ], "Size": 1, "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", @@ -2253,7 +2253,7 @@ "Opcode": 100, "Name": "app_global_get", "Args": [ - "key" + "[]byte" ], "Returns": [ "any" @@ -2271,11 +2271,11 @@ "Name": "app_global_get_ex", "Args": [ "uint64", - "key" + "[]byte" ], "Returns": [ "any", - "bool" + "uint64" ], "Size": 1, "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", @@ -2290,7 +2290,7 @@ "Name": "app_local_put", "Args": [ "any", - "key", + "[]byte", "any" ], "Size": 1, @@ -2305,7 +2305,7 @@ "Opcode": 103, "Name": "app_global_put", "Args": [ - "key", + "[]byte", "any" ], "Size": 1, @@ -2320,7 +2320,7 @@ "Name": "app_local_del", "Args": [ "any", - "key" + "[]byte" ], "Size": 1, "Doc": "delete key B from account A's local state of the current application", @@ -2334,7 +2334,7 @@ "Opcode": 105, "Name": "app_global_del", "Args": [ - "key" + "[]byte" ], "Size": 1, "Doc": "delete key A from the global state of the current application", @@ -2353,7 +2353,7 @@ ], "Returns": [ "any", - "bool" + "uint64" ], "Size": 2, "ArgEnum": [ @@ -2380,7 +2380,7 @@ ], "Returns": [ "any", - "bool" + "uint64" ], "Size": 2, "ArgEnum": [ @@ -2427,7 +2427,7 @@ ], "Returns": [ "any", - "bool" + "uint64" ], "Size": 2, "ArgEnum": [ @@ -2468,7 +2468,7 @@ ], "Returns": [ "any", - "bool" + "uint64" ], "Size": 2, "ArgEnum": [ From aff63f56ee2450f2dc65f52eb92156b89b92c7d1 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 13:23:52 -0400 Subject: [PATCH 34/41] cleanup bounds in docs --- cmd/opdoc/opdoc.go | 34 +++- data/transactions/logic/README.md | 74 ++++----- data/transactions/logic/TEAL_opcodes.md | 50 +++--- data/transactions/logic/eval.go | 2 +- data/transactions/logic/eval_test.go | 2 +- data/transactions/logic/langspec.json | 198 ++++++++++++------------ 6 files changed, 194 insertions(+), 166 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index e36b7ece72..bb47ace5f8 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -52,13 +52,12 @@ func markdownTableEscape(x string) string { } func namedStackTypesMarkdown(out io.Writer, stackTypes []namedType) { - fmt.Fprintf(out, "#### StackType Definitions\n\n") + fmt.Fprintf(out, "#### Definitions\n\n") fmt.Fprintf(out, "| Name | Bound | AVM Type |\n") fmt.Fprintf(out, "| ---- | ---- | -------- |\n") for _, st := range stackTypes { - bound := fmt.Sprintf("%d - %d", st.Bound[0], st.Bound[1]) - fmt.Fprintf(out, "| %s | %s | %s |\n", st.Name, bound, st.AVMType) + fmt.Fprintf(out, "| %s | %s | %s |\n", st.Name, st.boundString(), st.AVMType) } out.Write([]byte("\n")) } @@ -272,6 +271,35 @@ type namedType struct { AVMType string } +func (nt namedType) boundString() string { + if nt.Bound[0] == 0 && nt.Bound[1] == 0 { + return "" + } + + val := "x" + // if its bytes, the length is bounded + if nt.AVMType == "[]byte" { + val = "len(x)" + } + + // If they're equal, the val should match exactly + if nt.Bound[0] > 0 && nt.Bound[0] == nt.Bound[1] { + return fmt.Sprintf("%s == %d", val, nt.Bound[0]) + } + + // otherwise, provide min/max bounds as lte expression + minLen, maxLen := "", "" + if nt.Bound[0] > 0 { + minLen = fmt.Sprintf("%d <= ", nt.Bound[0]) + } + if nt.Bound[1] > 0 { + maxLen = fmt.Sprintf(" <= %d", nt.Bound[1]) + } + + return fmt.Sprintf("%s%s%s", minLen, val, maxLen) + +} + // LanguageSpec records the ops of the language at some version type LanguageSpec struct { EvalMaxVersion int diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index a083bef773..5b43a5be46 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -48,21 +48,21 @@ named to provide more semantic information in the documentation. They're also us assembly time to do type checking and to provide more informative error messages. -#### StackType Definitions +#### Definitions | Name | Bound | AVM Type | | ---- | ---- | -------- | -| uint64 | 0 - 18446744073709551615 | uint64 | -| none | 0 - 0 | none | -| name | 1 - 64 | []byte | -| method | 4 - 4 | []byte | -| key | 0 - 64 | []byte | -| bool | 0 - 1 | uint64 | -| bigint | 0 - 64 | []byte | -| any | 0 - 0 | any | -| addr | 32 - 32 | []byte | -| []byte | 0 - 4096 | []byte | -| [32]byte | 32 - 32 | []byte | +| uint64 | x <= 18446744073709551615 | uint64 | +| none | | none | +| name | 1 <= len(x) <= 64 | []byte | +| method | len(x) == 4 | []byte | +| key | len(x) <= 64 | []byte | +| bool | x <= 1 | uint64 | +| bigint | len(x) <= 64 | []byte | +| any | | any | +| address | len(x) == 32 | []byte | +| []byte | len(x) <= 4096 | []byte | +| [32]byte | len(x) == 32 | []byte | @@ -517,16 +517,16 @@ Some of these have immediate data in the byte or bytes after the opcode. ##### Scalar Fields | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | -| 0 | Sender | addr | | 32 byte address | +| 0 | Sender | address | | 32 byte address | | 1 | Fee | uint64 | | microalgos | | 2 | FirstValid | uint64 | | round number | | 3 | FirstValidTime | uint64 | v7 | UNIX timestamp of block before txn.FirstValid. Fails if negative | | 4 | LastValid | uint64 | | round number | | 5 | Note | []byte | | Any data up to 1024 bytes | | 6 | Lease | [32]byte | | 32 byte lease value | -| 7 | Receiver | addr | | 32 byte address | +| 7 | Receiver | address | | 32 byte address | | 8 | Amount | uint64 | | microalgos | -| 9 | CloseRemainderTo | addr | | 32 byte address | +| 9 | CloseRemainderTo | address | | 32 byte address | | 10 | VotePK | [32]byte | | 32 byte address | | 11 | SelectionPK | [32]byte | | 32 byte address | | 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | @@ -536,9 +536,9 @@ Some of these have immediate data in the byte or bytes after the opcode. | 16 | TypeEnum | uint64 | | Transaction type as integer | | 17 | XferAsset | uint64 | | Asset ID | | 18 | AssetAmount | uint64 | | value in Asset's units | -| 19 | AssetSender | addr | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | -| 20 | AssetReceiver | addr | | 32 byte address | -| 21 | AssetCloseTo | addr | | 32 byte address | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | | 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | | 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | | 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | @@ -547,7 +547,7 @@ Some of these have immediate data in the byte or bytes after the opcode. | 29 | NumAccounts | uint64 | v2 | Number of Accounts | | 30 | ApprovalProgram | []byte | v2 | Approval program | | 31 | ClearStateProgram | []byte | v2 | Clear state program | -| 32 | RekeyTo | addr | v2 | 32 byte Sender's new AuthAddr | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | | 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | | 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | | 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | @@ -556,12 +556,12 @@ Some of these have immediate data in the byte or bytes after the opcode. | 38 | ConfigAssetName | []byte | v2 | The asset name | | 39 | ConfigAssetURL | []byte | v2 | URL | | 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | -| 41 | ConfigAssetManager | addr | v2 | 32 byte address | -| 42 | ConfigAssetReserve | addr | v2 | 32 byte address | -| 43 | ConfigAssetFreeze | addr | v2 | 32 byte address | -| 44 | ConfigAssetClawback | addr | v2 | 32 byte address | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | | 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | -| 46 | FreezeAssetAccount | addr | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | | 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | | 49 | NumAssets | uint64 | v3 | Number of Assets | | 51 | NumApplications | uint64 | v3 | Number of Applications | @@ -583,7 +583,7 @@ Some of these have immediate data in the byte or bytes after the opcode. | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | | 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | -| 28 | Accounts | addr | v2 | Accounts listed in the ApplicationCall transaction | +| 28 | Accounts | address | v2 | Accounts listed in the ApplicationCall transaction | | 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | | 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | | 58 | Logs | []byte | v5 | Log messages emitted by an application call (only with `itxn` in v5). Application mode only | @@ -602,18 +602,18 @@ Global fields are fields that are common to all the transactions in the group. I | 0 | MinTxnFee | uint64 | | microalgos | | 1 | MinBalance | uint64 | | microalgos | | 2 | MaxTxnLife | uint64 | | rounds | -| 3 | ZeroAddress | addr | | 32 byte address of all zero bytes | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | | 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | | 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | | 6 | Round | uint64 | v2 | Current round number. Application mode only. | | 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | | 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | -| 9 | CreatorAddress | addr | v3 | Address of the creator of the current application. Application mode only. | -| 10 | CurrentApplicationAddress | addr | v5 | Address that the current application controls. Application mode only. | +| 9 | CreatorAddress | address | v3 | Address of the creator of the current application. Application mode only. | +| 10 | CurrentApplicationAddress | address | v5 | Address that the current application controls. Application mode only. | | 11 | GroupID | [32]byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | | 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | | 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | -| 14 | CallerApplicationAddress | addr | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | +| 14 | CallerApplicationAddress | address | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | **Asset Fields** @@ -635,11 +635,11 @@ Asset fields include `AssetHolding` and `AssetParam` fields that are used in the | 4 | AssetName | []byte | | Asset name | | 5 | AssetURL | []byte | | URL with additional info about the asset | | 6 | AssetMetadataHash | [32]byte | | Arbitrary commitment | -| 7 | AssetManager | addr | | Manager address | -| 8 | AssetReserve | addr | | Reserve address | -| 9 | AssetFreeze | addr | | Freeze address | -| 10 | AssetClawback | addr | | Clawback address | -| 11 | AssetCreator | addr | v5 | Creator address | +| 7 | AssetManager | address | | Manager address | +| 8 | AssetReserve | address | | Reserve address | +| 9 | AssetFreeze | address | | Freeze address | +| 10 | AssetClawback | address | | Clawback address | +| 11 | AssetCreator | address | v5 | Creator address | **App Fields** @@ -655,8 +655,8 @@ App fields used in the `app_params_get` opcode. | 4 | AppLocalNumUint | uint64 | Number of uint64 values allowed in Local State | | 5 | AppLocalNumByteSlice | uint64 | Number of byte array values allowed in Local State | | 6 | AppExtraProgramPages | uint64 | Number of Extra Program Pages of code space | -| 7 | AppCreator | addr | Creator address | -| 8 | AppAddress | addr | Address for which this application has authority | +| 7 | AppCreator | address | Creator address | +| 8 | AppAddress | address | Address for which this application has authority | **Account Fields** @@ -667,7 +667,7 @@ Account fields used in the `acct_params_get` opcode. | - | ------ | -- | - | --------- | | 0 | AcctBalance | uint64 | | Account balance in microalgos | | 1 | AcctMinBalance | uint64 | | Minimum required balance for account, in microalgos | -| 2 | AcctAuthAddr | addr | | Address the account is rekeyed to. | +| 2 | AcctAuthAddr | address | | Address the account is rekeyed to. | | 3 | AcctTotalNumUint | uint64 | v8 | The total number of uint64 values allocated by this account in Global and Local States. | | 4 | AcctTotalNumByteSlice | uint64 | v8 | The total number of byte array values allocated by this account in Global and Local States. | | 5 | AcctTotalExtraAppPages | uint64 | v8 | The number of extra app code pages used by this account. | diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 5c55a2f087..d3b1595298 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -361,16 +361,16 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | -| 0 | Sender | addr | | 32 byte address | +| 0 | Sender | address | | 32 byte address | | 1 | Fee | uint64 | | microalgos | | 2 | FirstValid | uint64 | | round number | | 3 | FirstValidTime | uint64 | v7 | UNIX timestamp of block before txn.FirstValid. Fails if negative | | 4 | LastValid | uint64 | | round number | | 5 | Note | []byte | | Any data up to 1024 bytes | | 6 | Lease | [32]byte | | 32 byte lease value | -| 7 | Receiver | addr | | 32 byte address | +| 7 | Receiver | address | | 32 byte address | | 8 | Amount | uint64 | | microalgos | -| 9 | CloseRemainderTo | addr | | 32 byte address | +| 9 | CloseRemainderTo | address | | 32 byte address | | 10 | VotePK | [32]byte | | 32 byte address | | 11 | SelectionPK | [32]byte | | 32 byte address | | 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | @@ -380,9 +380,9 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 16 | TypeEnum | uint64 | | Transaction type as integer | | 17 | XferAsset | uint64 | | Asset ID | | 18 | AssetAmount | uint64 | | value in Asset's units | -| 19 | AssetSender | addr | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | -| 20 | AssetReceiver | addr | | 32 byte address | -| 21 | AssetCloseTo | addr | | 32 byte address | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | | 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | | 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | | 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | @@ -391,7 +391,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 29 | NumAccounts | uint64 | v2 | Number of Accounts | | 30 | ApprovalProgram | []byte | v2 | Approval program | | 31 | ClearStateProgram | []byte | v2 | Clear state program | -| 32 | RekeyTo | addr | v2 | 32 byte Sender's new AuthAddr | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | | 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | | 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | | 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | @@ -400,12 +400,12 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 38 | ConfigAssetName | []byte | v2 | The asset name | | 39 | ConfigAssetURL | []byte | v2 | URL | | 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | -| 41 | ConfigAssetManager | addr | v2 | 32 byte address | -| 42 | ConfigAssetReserve | addr | v2 | 32 byte address | -| 43 | ConfigAssetFreeze | addr | v2 | 32 byte address | -| 44 | ConfigAssetClawback | addr | v2 | 32 byte address | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | | 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | -| 46 | FreezeAssetAccount | addr | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | | 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | | 49 | NumAssets | uint64 | v3 | Number of Assets | | 51 | NumApplications | uint64 | v3 | Number of Applications | @@ -437,18 +437,18 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u | 0 | MinTxnFee | uint64 | | microalgos | | 1 | MinBalance | uint64 | | microalgos | | 2 | MaxTxnLife | uint64 | | rounds | -| 3 | ZeroAddress | addr | | 32 byte address of all zero bytes | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | | 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | | 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | | 6 | Round | uint64 | v2 | Current round number. Application mode only. | | 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | | 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | -| 9 | CreatorAddress | addr | v3 | Address of the creator of the current application. Application mode only. | -| 10 | CurrentApplicationAddress | addr | v5 | Address that the current application controls. Application mode only. | +| 9 | CreatorAddress | address | v3 | Address of the creator of the current application. Application mode only. | +| 10 | CurrentApplicationAddress | address | v5 | Address that the current application controls. Application mode only. | | 11 | GroupID | [32]byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | | 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | | 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | -| 14 | CallerApplicationAddress | addr | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | +| 14 | CallerApplicationAddress | address | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | ## gtxn t f @@ -483,7 +483,7 @@ for notes on transaction fields available, see `txn`. If this transaction is _i_ | Index | Name | Type | In | Notes | | - | ------ | -- | - | --------- | | 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | -| 28 | Accounts | addr | v2 | Accounts listed in the ApplicationCall transaction | +| 28 | Accounts | address | v2 | Accounts listed in the ApplicationCall transaction | | 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | | 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | | 58 | Logs | []byte | v5 | Log messages emitted by an application call (only with `itxn` in v5). Application mode only | @@ -969,11 +969,11 @@ params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or | 4 | AssetName | []byte | | Asset name | | 5 | AssetURL | []byte | | URL with additional info about the asset | | 6 | AssetMetadataHash | [32]byte | | Arbitrary commitment | -| 7 | AssetManager | addr | | Manager address | -| 8 | AssetReserve | addr | | Reserve address | -| 9 | AssetFreeze | addr | | Freeze address | -| 10 | AssetClawback | addr | | Clawback address | -| 11 | AssetCreator | addr | v5 | Creator address | +| 7 | AssetManager | address | | Manager address | +| 8 | AssetReserve | address | | Reserve address | +| 9 | AssetFreeze | address | | Freeze address | +| 10 | AssetClawback | address | | Clawback address | +| 11 | AssetCreator | address | v5 | Creator address | params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. @@ -997,8 +997,8 @@ params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: | 4 | AppLocalNumUint | uint64 | Number of uint64 values allowed in Local State | | 5 | AppLocalNumByteSlice | uint64 | Number of byte array values allowed in Local State | | 6 | AppExtraProgramPages | uint64 | Number of Extra Program Pages of code space | -| 7 | AppCreator | addr | Creator address | -| 8 | AppAddress | addr | Address for which this application has authority | +| 7 | AppCreator | address | Creator address | +| 8 | AppAddress | address | Address for which this application has authority | params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value. @@ -1017,7 +1017,7 @@ params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag | - | ------ | -- | - | --------- | | 0 | AcctBalance | uint64 | | Account balance in microalgos | | 1 | AcctMinBalance | uint64 | | Minimum required balance for account, in microalgos | -| 2 | AcctAuthAddr | addr | | Address the account is rekeyed to. | +| 2 | AcctAuthAddr | address | | Address the account is rekeyed to. | | 3 | AcctTotalNumUint | uint64 | v8 | The total number of uint64 values allocated by this account in Global and Local States. | | 4 | AcctTotalNumByteSlice | uint64 | v8 | The total number of byte array values allocated by this account in Global and Local States. | | 5 | AcctTotalExtraAppPages | uint64 | v8 | The number of extra app code pages used by this account. | diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 8c31538f7b..8b74ef66da 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -689,7 +689,7 @@ var ( // StackBoolean constrains the int to 1 or 0, representing True or False StackBoolean = NewStackType(avmUint64, bound(0, 1), "bool") // StackAddress represents an address - StackAddress = NewStackType(avmBytes, static(32), "addr") + StackAddress = NewStackType(avmBytes, static(32), "address") // StackBytes32 represents a bytestring that should have exactly 32 bytes StackBytes32 = NewStackType(avmBytes, static(32), "[32]byte") // StackBigInt represents a bytestring that should be treated like an int diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 0e8a86e7e2..9317be9203 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -4533,7 +4533,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 0xbffff4; ==", 3) + testPanics(t, "byte 0xfffff4; int 24; int 0; setbit; byte 0xbf; ==", 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) diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index 497f42b267..6837191f76 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -75,7 +75,7 @@ "AVMType": "any" }, { - "Name": "addr", + "Name": "address", "Abbreviation": "A", "Bound": [ 32, @@ -970,16 +970,16 @@ "NumClearStateProgramPages" ], "ArgEnumTypes": [ - "addr", + "address", "uint64", "uint64", "uint64", "uint64", "[]byte", "[32]byte", - "addr", + "address", "uint64", - "addr", + "address", "[32]byte", "[32]byte", "uint64", @@ -989,20 +989,20 @@ "uint64", "uint64", "uint64", - "addr", - "addr", - "addr", + "address", + "address", + "address", "uint64", "[32]byte", "uint64", "uint64", "[]byte", "uint64", - "addr", + "address", "uint64", "[]byte", "[]byte", - "addr", + "address", "uint64", "uint64", "uint64", @@ -1011,12 +1011,12 @@ "[]byte", "[]byte", "[32]byte", - "addr", - "addr", - "addr", - "addr", + "address", + "address", + "address", + "address", "uint64", - "addr", + "address", "bool", "uint64", "uint64", @@ -1074,18 +1074,18 @@ "uint64", "uint64", "uint64", - "addr", + "address", "uint64", "uint64", "uint64", "uint64", "uint64", - "addr", - "addr", + "address", + "address", "[32]byte", "uint64", "uint64", - "addr" + "address" ], "Doc": "global field F", "ImmediateNote": "{uint8 global field index}", @@ -1172,16 +1172,16 @@ "NumClearStateProgramPages" ], "ArgEnumTypes": [ - "addr", + "address", "uint64", "uint64", "uint64", "uint64", "[]byte", "[32]byte", - "addr", + "address", "uint64", - "addr", + "address", "[32]byte", "[32]byte", "uint64", @@ -1191,20 +1191,20 @@ "uint64", "uint64", "uint64", - "addr", - "addr", - "addr", + "address", + "address", + "address", "uint64", "[32]byte", "uint64", "uint64", "[]byte", "uint64", - "addr", + "address", "uint64", "[]byte", "[]byte", - "addr", + "address", "uint64", "uint64", "uint64", @@ -1213,12 +1213,12 @@ "[]byte", "[]byte", "[32]byte", - "addr", - "addr", - "addr", - "addr", + "address", + "address", + "address", + "address", "uint64", - "addr", + "address", "bool", "uint64", "uint64", @@ -1295,7 +1295,7 @@ ], "ArgEnumTypes": [ "[]byte", - "addr", + "address", "uint64", "uint64", "[]byte", @@ -1327,7 +1327,7 @@ ], "ArgEnumTypes": [ "[]byte", - "addr", + "address", "uint64", "uint64", "[]byte", @@ -1422,16 +1422,16 @@ "NumClearStateProgramPages" ], "ArgEnumTypes": [ - "addr", + "address", "uint64", "uint64", "uint64", "uint64", "[]byte", "[32]byte", - "addr", + "address", "uint64", - "addr", + "address", "[32]byte", "[32]byte", "uint64", @@ -1441,20 +1441,20 @@ "uint64", "uint64", "uint64", - "addr", - "addr", - "addr", + "address", + "address", + "address", "uint64", "[32]byte", "uint64", "uint64", "[]byte", "uint64", - "addr", + "address", "uint64", "[]byte", "[]byte", - "addr", + "address", "uint64", "uint64", "uint64", @@ -1463,12 +1463,12 @@ "[]byte", "[]byte", "[32]byte", - "addr", - "addr", - "addr", - "addr", + "address", + "address", + "address", + "address", "uint64", - "addr", + "address", "bool", "uint64", "uint64", @@ -1520,7 +1520,7 @@ ], "ArgEnumTypes": [ "[]byte", - "addr", + "address", "uint64", "uint64", "[]byte", @@ -2405,11 +2405,11 @@ "[]byte", "[]byte", "[32]byte", - "addr", - "addr", - "addr", - "addr", - "addr" + "address", + "address", + "address", + "address", + "address" ], "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", @@ -2449,8 +2449,8 @@ "uint64", "uint64", "uint64", - "addr", - "addr" + "address", + "address" ], "Doc": "X is field F from app A. Y is 1 if A exists, else 0", "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", @@ -2488,7 +2488,7 @@ "ArgEnumTypes": [ "uint64", "uint64", - "addr", + "address", "uint64", "uint64", "uint64", @@ -3193,12 +3193,12 @@ "ClearStateProgramPages" ], "ArgEnumTypes": [ - "addr", + "address", "uint64", "[]byte", - "addr", + "address", "uint64", - "addr", + "address", "[32]byte", "[32]byte", "uint64", @@ -3208,16 +3208,16 @@ "uint64", "uint64", "uint64", - "addr", - "addr", - "addr", + "address", + "address", + "address", "uint64", "uint64", "[]byte", - "addr", + "address", "[]byte", "[]byte", - "addr", + "address", "uint64", "uint64", "uint64", @@ -3226,12 +3226,12 @@ "[]byte", "[]byte", "[32]byte", - "addr", - "addr", - "addr", - "addr", + "address", + "address", + "address", + "address", "uint64", - "addr", + "address", "bool", "uint64", "uint64", @@ -3342,16 +3342,16 @@ "NumClearStateProgramPages" ], "ArgEnumTypes": [ - "addr", + "address", "uint64", "uint64", "uint64", "uint64", "[]byte", "[32]byte", - "addr", + "address", "uint64", - "addr", + "address", "[32]byte", "[32]byte", "uint64", @@ -3361,20 +3361,20 @@ "uint64", "uint64", "uint64", - "addr", - "addr", - "addr", + "address", + "address", + "address", "uint64", "[32]byte", "uint64", "uint64", "[]byte", "uint64", - "addr", + "address", "uint64", "[]byte", "[]byte", - "addr", + "address", "uint64", "uint64", "uint64", @@ -3383,12 +3383,12 @@ "[]byte", "[]byte", "[32]byte", - "addr", - "addr", - "addr", - "addr", + "address", + "address", + "address", + "address", "uint64", - "addr", + "address", "bool", "uint64", "uint64", @@ -3436,7 +3436,7 @@ ], "ArgEnumTypes": [ "[]byte", - "addr", + "address", "uint64", "uint64", "[]byte", @@ -3539,16 +3539,16 @@ "NumClearStateProgramPages" ], "ArgEnumTypes": [ - "addr", + "address", "uint64", "uint64", "uint64", "uint64", "[]byte", "[32]byte", - "addr", + "address", "uint64", - "addr", + "address", "[32]byte", "[32]byte", "uint64", @@ -3558,20 +3558,20 @@ "uint64", "uint64", "uint64", - "addr", - "addr", - "addr", + "address", + "address", + "address", "uint64", "[32]byte", "uint64", "uint64", "[]byte", "uint64", - "addr", + "address", "uint64", "[]byte", "[]byte", - "addr", + "address", "uint64", "uint64", "uint64", @@ -3580,12 +3580,12 @@ "[]byte", "[]byte", "[32]byte", - "addr", - "addr", - "addr", - "addr", + "address", + "address", + "address", + "address", "uint64", - "addr", + "address", "bool", "uint64", "uint64", @@ -3633,7 +3633,7 @@ ], "ArgEnumTypes": [ "[]byte", - "addr", + "address", "uint64", "uint64", "[]byte", @@ -3785,7 +3785,7 @@ ], "ArgEnumTypes": [ "[]byte", - "addr", + "address", "uint64", "uint64", "[]byte", @@ -3820,7 +3820,7 @@ ], "ArgEnumTypes": [ "[]byte", - "addr", + "address", "uint64", "uint64", "[]byte", @@ -3856,7 +3856,7 @@ ], "ArgEnumTypes": [ "[]byte", - "addr", + "address", "uint64", "uint64", "[]byte", From 9f1c07ae65eeefb11d6cc3f42b1014338a5f90e5 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 13:26:26 -0400 Subject: [PATCH 35/41] Apply suggestions from code review Co-authored-by: Bob Broderick <118225939+bbroder-algo@users.noreply.github.com> --- data/transactions/logic/assembler.go | 2 +- data/transactions/logic/eval.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index dc2e90389a..b737712d0a 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1446,7 +1446,7 @@ func typeStores(pgm *ProgramKnowledge, args []string) (StackTypes, StackTypes, e for i := range pgm.scratchSpace { // We can't know what slot stacktop is being stored in - // so we union it into all scratch slots + // so we adjust the bounds and type of each slot as if the stacktop type were stored there. pgm.scratchSpace[i] = pgm.scratchSpace[i].union(pgm.stack[top]) } return nil, nil, nil diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 8b74ef66da..1eb64daa09 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -621,6 +621,7 @@ func (cx *EvalContext) RunMode() RunMode { } // avmType describes the type of a value on the operand stack +// avmTypes are a subset of StackTypes type avmType byte const ( From 7d5e89c3d8284e60c5e4ee579bd63aeb7abec092 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 13:35:41 -0400 Subject: [PATCH 36/41] fix test expecting short name, tweak var names in opdoc --- cmd/opdoc/opdoc.go | 8 ++++---- data/transactions/logic/assembler_test.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index bb47ace5f8..32a0ecab4e 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -288,15 +288,15 @@ func (nt namedType) boundString() string { } // otherwise, provide min/max bounds as lte expression - minLen, maxLen := "", "" + minBound, maxBound := "", "" if nt.Bound[0] > 0 { - minLen = fmt.Sprintf("%d <= ", nt.Bound[0]) + minBound = fmt.Sprintf("%d <= ", nt.Bound[0]) } if nt.Bound[1] > 0 { - maxLen = fmt.Sprintf(" <= %d", nt.Bound[1]) + maxBound = fmt.Sprintf(" <= %d", nt.Bound[1]) } - return fmt.Sprintf("%s%s%s", minLen, val, maxLen) + return fmt.Sprintf("%s%s%s", minBound, val, maxBound) } diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 417905fd5a..dbc665e26e 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -2685,8 +2685,8 @@ func TestTxTypes(t *testing.T) { partitiontest.PartitionTest(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 1"}) - testProg(t, "itxn_begin; byte 0x56127823; itxn_field Sender", 5, Expect{1, "...wanted type addr got [4]byte"}) + testProg(t, "itxn_begin; int 1; itxn_field Sender", 5, Expect{1, "...wanted type address got 1"}) + testProg(t, "itxn_begin; byte 0x56127823; itxn_field Sender", 5, Expect{1, "...wanted type address 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..."}) From 133c916ebfdcb0142c9c0a002f7f85ad469b69f3 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 13:40:32 -0400 Subject: [PATCH 37/41] use names directly, omit 'FieldGroup' --- cmd/opdoc/opdoc.go | 2 +- data/transactions/logic/TEAL_opcodes.md | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index 8b13b1433f..acd110d36f 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -261,7 +261,7 @@ func opToMarkdown(out io.Writer, op *logic.OpSpec, groupDocWritten map[string]bo for i := range op.OpDetails.Immediates { group := op.OpDetails.Immediates[i].Group if group != nil && group.Doc != "" && !groupDocWritten[group.Name] { - fmt.Fprintf(out, "\n### Field Group %s\n\n%s\n\n", group.Name, group.Doc) + fmt.Fprintf(out, "\n### %s\n\n%s\n\n", group.Name, group.Doc) fieldGroupMarkdown(out, group) groupDocWritten[group.Name] = true } diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 47780e8dfc..38529fb01c 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -54,7 +54,7 @@ The 32 byte public key is the last element on the stack, preceded by the 64 byte - **Cost**: Secp256k1=1700 Secp256r1=2500 - Availability: v5 -### Field Group ECDSA +### ECDSA Curves @@ -368,7 +368,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u - Stack: ... → ..., any - field F of current transaction -### Field Group txn +### txn Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) @@ -444,7 +444,7 @@ Fields (see [transaction reference](https://developer.algorand.org/docs/referenc - Stack: ... → ..., any - global field F -### Field Group global +### global Fields @@ -498,7 +498,7 @@ for notes on transaction fields available, see `txn`. If this transaction is _i_ - Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. - Availability: v2 -### Field Group txna +### txna Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) @@ -838,7 +838,7 @@ When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on - **Cost**: 1 + 1 per 16 bytes of A - Availability: v7 -### Field Group base64 +### base64 Encodings @@ -861,7 +861,7 @@ Encodings - **Cost**: 25 + 2 per 7 bytes of A - Availability: v7 -### Field Group json_ref +### json_ref Types @@ -987,7 +987,7 @@ Deleting a key which is already absent has no effect on the application global s - Availability: v2 - Mode: Application -### Field Group asset_holding +### asset_holding Fields @@ -1008,7 +1008,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or - Availability: v2 - Mode: Application -### Field Group asset_params +### asset_params Fields @@ -1039,7 +1039,7 @@ params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: - Availability: v5 - Mode: Application -### Field Group app_params +### app_params Fields @@ -1067,7 +1067,7 @@ params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag - Availability: v6 - Mode: Application -### Field Group acct_params +### acct_params Fields @@ -1616,7 +1616,7 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo - **Cost**: 5700 - Availability: v7 -### Field Group vrf_verify +### vrf_verify Standards @@ -1635,7 +1635,7 @@ Standards - field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive) - Availability: v7 -### Field Group block +### block Fields From aa9a277acaffd876ea66a877fd04d45c938fd442 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 14:07:38 -0400 Subject: [PATCH 38/41] fix bools for asset/app/acct params that were lost during merge --- data/transactions/logic/TEAL_opcodes.md | 14 +++++++------- data/transactions/logic/langspec.json | 14 +++++++------- data/transactions/logic/opcodes.go | 20 ++++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index d3b1595298..efbb71ce08 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -843,7 +843,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address). Retu ## app_opted_in - Opcode: 0x61 -- Stack: ..., A, B: uint64 → ..., uint64 +- Stack: ..., A, B: uint64 → ..., bool - 1 if account A is opted in to application B, else 0 - Availability: v2 - Mode: Application @@ -863,7 +863,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ account address), stat ## app_local_get_ex - Opcode: 0x63 -- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: uint64 +- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: bool - X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 - Availability: v2 - Mode: Application @@ -883,7 +883,7 @@ params: state key. Return: value. The value is zero (of type uint64) if the key ## app_global_get_ex - Opcode: 0x65 -- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: uint64 +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: bool - X is the global state of application A, key B. Y is 1 if key existed, else 0 - Availability: v2 - Mode: Application @@ -935,7 +935,7 @@ Deleting a key which is already absent has no effect on the application global s ## asset_holding_get f - Opcode: 0x70 {uint8 asset holding field index} -- Stack: ..., A, B: uint64 → ..., X: any, Y: uint64 +- Stack: ..., A, B: uint64 → ..., X: any, Y: bool - X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 - Availability: v2 - Mode: Application @@ -953,7 +953,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or ## asset_params_get f - Opcode: 0x71 {uint8 asset params field index} -- Stack: ..., A: uint64 → ..., X: any, Y: uint64 +- Stack: ..., A: uint64 → ..., X: any, Y: bool - X is field F from asset A. Y is 1 if A exists, else 0 - Availability: v2 - Mode: Application @@ -981,7 +981,7 @@ params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: ## app_params_get f - Opcode: 0x72 {uint8 app params field index} -- Stack: ..., A: uint64 → ..., X: any, Y: uint64 +- Stack: ..., A: uint64 → ..., X: any, Y: bool - X is field F from app A. Y is 1 if A exists, else 0 - Availability: v5 - Mode: Application @@ -1006,7 +1006,7 @@ params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag ## acct_params_get f - Opcode: 0x73 {uint8 account params field index} -- Stack: ..., A → ..., X: any, Y: uint64 +- Stack: ..., A → ..., X: any, Y: bool - X is field F from account A. Y is 1 if A owns positive algos, else 0 - Availability: v6 - Mode: Application diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index 6837191f76..f292d3d0c7 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -2201,7 +2201,7 @@ "uint64" ], "Returns": [ - "uint64" + "bool" ], "Size": 1, "Doc": "1 if account A is opted in to application B, else 0", @@ -2239,7 +2239,7 @@ ], "Returns": [ "any", - "uint64" + "bool" ], "Size": 1, "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", @@ -2275,7 +2275,7 @@ ], "Returns": [ "any", - "uint64" + "bool" ], "Size": 1, "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", @@ -2353,7 +2353,7 @@ ], "Returns": [ "any", - "uint64" + "bool" ], "Size": 2, "ArgEnum": [ @@ -2380,7 +2380,7 @@ ], "Returns": [ "any", - "uint64" + "bool" ], "Size": 2, "ArgEnum": [ @@ -2427,7 +2427,7 @@ ], "Returns": [ "any", - "uint64" + "bool" ], "Size": 2, "ArgEnum": [ @@ -2468,7 +2468,7 @@ ], "Returns": [ "any", - "uint64" + "bool" ], "Size": 2, "ArgEnum": [ diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index c80aefdc69..433d69b7e8 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -530,25 +530,25 @@ var OpSpecs = []OpSpec{ {0x60, "balance", opBalance, proto("i:i"), 2, only(ModeApp)}, {0x60, "balance", opBalance, proto("a:i"), directRefEnabledVersion, only(ModeApp)}, - {0x61, "app_opted_in", opAppOptedIn, proto("ii:i"), 2, only(ModeApp)}, - {0x61, "app_opted_in", opAppOptedIn, proto("ai:i"), directRefEnabledVersion, only(ModeApp)}, + {0x61, "app_opted_in", opAppOptedIn, proto("ii:T"), 2, only(ModeApp)}, + {0x61, "app_opted_in", opAppOptedIn, proto("ai:T"), directRefEnabledVersion, only(ModeApp)}, {0x62, "app_local_get", opAppLocalGet, proto("ib:a"), 2, only(ModeApp)}, {0x62, "app_local_get", opAppLocalGet, proto("ab:a"), directRefEnabledVersion, only(ModeApp)}, - {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iib:ai"), 2, only(ModeApp)}, - {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aib:ai"), directRefEnabledVersion, only(ModeApp)}, + {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iib:aT"), 2, only(ModeApp)}, + {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aib:aT"), directRefEnabledVersion, only(ModeApp)}, {0x64, "app_global_get", opAppGlobalGet, proto("b:a"), 2, only(ModeApp)}, - {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("ib:ai"), 2, only(ModeApp)}, + {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("ib:aT"), 2, only(ModeApp)}, {0x66, "app_local_put", opAppLocalPut, proto("iba:"), 2, only(ModeApp)}, {0x66, "app_local_put", opAppLocalPut, proto("aba:"), directRefEnabledVersion, only(ModeApp)}, {0x67, "app_global_put", opAppGlobalPut, proto("ba:"), 2, only(ModeApp)}, {0x68, "app_local_del", opAppLocalDel, proto("ib:"), 2, only(ModeApp)}, {0x68, "app_local_del", opAppLocalDel, proto("ab:"), directRefEnabledVersion, only(ModeApp)}, {0x69, "app_global_del", opAppGlobalDel, proto("b:"), 2, only(ModeApp)}, - {0x70, "asset_holding_get", opAssetHoldingGet, proto("ii:ai"), 2, field("f", &AssetHoldingFields).only(ModeApp)}, - {0x70, "asset_holding_get", opAssetHoldingGet, proto("ai:ai"), directRefEnabledVersion, field("f", &AssetHoldingFields).only(ModeApp)}, - {0x71, "asset_params_get", opAssetParamsGet, proto("i:ai"), 2, field("f", &AssetParamsFields).only(ModeApp)}, - {0x72, "app_params_get", opAppParamsGet, proto("i:ai"), 5, field("f", &AppParamsFields).only(ModeApp)}, - {0x73, "acct_params_get", opAcctParamsGet, proto("a:ai"), 6, field("f", &AcctParamsFields).only(ModeApp)}, + {0x70, "asset_holding_get", opAssetHoldingGet, proto("ii:aT"), 2, field("f", &AssetHoldingFields).only(ModeApp)}, + {0x70, "asset_holding_get", opAssetHoldingGet, proto("ai:aT"), directRefEnabledVersion, field("f", &AssetHoldingFields).only(ModeApp)}, + {0x71, "asset_params_get", opAssetParamsGet, proto("i:aT"), 2, field("f", &AssetParamsFields).only(ModeApp)}, + {0x72, "app_params_get", opAppParamsGet, proto("i:aT"), 5, field("f", &AppParamsFields).only(ModeApp)}, + {0x73, "acct_params_get", opAcctParamsGet, proto("a:aT"), 6, field("f", &AcctParamsFields).only(ModeApp)}, {0x78, "min_balance", opMinBalance, proto("i:i"), 3, only(ModeApp)}, {0x78, "min_balance", opMinBalance, proto("a:i"), directRefEnabledVersion, only(ModeApp)}, From 00f611cf1374b3268e780a845f6d8cffef381654 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 15:23:06 -0400 Subject: [PATCH 39/41] fix spacing in immediate notes --- data/transactions/logic/TEAL_opcodes.md | 4 ++-- data/transactions/logic/doc.go | 4 ++-- data/transactions/logic/langspec.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index 41c0f842fb..c0bdd344e9 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -287,7 +287,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u ## bytecblock -- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte const values +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values - Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} - Stack: ... → ... - prepare block of byte-array constants for use by bytec @@ -296,7 +296,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u ## bytec -- Syntax: `bytec I` ∋ I: an index in the bytec block +- Syntax: `bytec I` ∋ I: an index in the bytecblock - Bytecode: 0x27 {uint8} - Stack: ... → ..., []byte - Ith constant from bytecblock diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index 62fc33b3b5..e4e5248802 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -226,8 +226,8 @@ var opcodeImmediateNotes = map[string][]string{ "intc": {"an index in the intcblock"}, "pushint": {"an int constant"}, "pushints": {"a list of int constants"}, - "bytecblock": {"a block of byte const values"}, - "bytec": {"an index in the bytec block"}, + "bytecblock": {"a block of byte constant values"}, + "bytec": {"an index in the bytecblock"}, "pushbytes": {"a byte constant"}, "pushbytess": {"a list of byte constants"}, diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index cf833d55f8..6a2c74aee0 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -789,7 +789,7 @@ "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", "ImmediateNote": [ { - "Comment": "a block of byte const values", + "Comment": "a block of byte constant values", "Encoding": "varuint count, [varuint length, bytes ...]", "Name": "BYTES ..." } @@ -809,7 +809,7 @@ "Doc": "Ith constant from bytecblock", "ImmediateNote": [ { - "Comment": "an index in the bytec block", + "Comment": "an index in the bytecblock", "Encoding": "uint8", "Name": "I" } From 682557e552f82aa22e0da7802529d814f57cce51 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Mon, 8 May 2023 15:26:16 -0400 Subject: [PATCH 40/41] adding descriptive comment for bounds in StackType --- data/transactions/logic/eval.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 1eb64daa09..6a49d00435 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -747,9 +747,9 @@ func union(a, b [2]uint64) [2]uint64 { // StackType describes the type of a value on the operand stack type StackType struct { - Name string + Name string // alias (address, boolean, ...) or derived name [5]byte AVMType avmType - Bound [2]uint64 + Bound [2]uint64 // represents max/min value for uint64 or max/min length for byte[] } // NewStackType Initializes a new StackType with fields passed From d9ca502e72c271f5000b11c395c948e8dadaf235 Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Tue, 9 May 2023 12:16:35 -0400 Subject: [PATCH 41/41] changed names to be more descriptive --- data/transactions/logic/README.md | 4 +-- data/transactions/logic/TEAL_opcodes.md | 14 ++++----- data/transactions/logic/eval.go | 8 +++--- data/transactions/logic/langspec.json | 38 ++++++++++++------------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index 5b43a5be46..7c51a9c233 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -53,10 +53,10 @@ assembly time to do type checking and to provide more informative error messages | Name | Bound | AVM Type | | ---- | ---- | -------- | | uint64 | x <= 18446744073709551615 | uint64 | +| stateKey | len(x) <= 64 | []byte | | none | | none | -| name | 1 <= len(x) <= 64 | []byte | | method | len(x) == 4 | []byte | -| key | len(x) <= 64 | []byte | +| boxName | 1 <= len(x) <= 64 | []byte | | bool | x <= 1 | uint64 | | bigint | len(x) <= 64 | []byte | | any | | any | diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md index c0bdd344e9..5a30ce35c7 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes.md @@ -1490,7 +1490,7 @@ The notation A,B indicates that A and B are interpreted as a uint128 value, with ## box_create - Bytecode: 0xb9 -- Stack: ..., A: name, B: uint64 → ..., bool +- Stack: ..., A: boxName, B: uint64 → ..., bool - create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1 - Availability: v8 - Mode: Application @@ -1500,7 +1500,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_extract - Bytecode: 0xba -- Stack: ..., A: name, B: uint64, C: uint64 → ..., []byte +- Stack: ..., A: boxName, B: uint64, C: uint64 → ..., []byte - read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. - Availability: v8 - Mode: Application @@ -1508,7 +1508,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_replace - Bytecode: 0xbb -- Stack: ..., A: name, B: uint64, C: []byte → ... +- Stack: ..., A: boxName, B: uint64, C: []byte → ... - write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. - Availability: v8 - Mode: Application @@ -1516,7 +1516,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_del - Bytecode: 0xbc -- Stack: ..., A: name → ..., bool +- Stack: ..., A: boxName → ..., bool - delete box named A if it exists. Return 1 if A existed, 0 otherwise - Availability: v8 - Mode: Application @@ -1524,7 +1524,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_len - Bytecode: 0xbd -- Stack: ..., A: name → ..., X: uint64, Y: bool +- Stack: ..., A: boxName → ..., X: uint64, Y: bool - X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0. - Availability: v8 - Mode: Application @@ -1532,7 +1532,7 @@ Newly created boxes are filled with 0 bytes. `box_create` will fail if the refer ## box_get - Bytecode: 0xbe -- Stack: ..., A: name → ..., X: []byte, Y: bool +- Stack: ..., A: boxName → ..., X: []byte, Y: bool - X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0. - Availability: v8 - Mode: Application @@ -1542,7 +1542,7 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo ## box_put - Bytecode: 0xbf -- Stack: ..., A: name, B: []byte → ... +- Stack: ..., A: boxName, B: []byte → ... - replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist - Availability: v8 - Mode: Application diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 6a49d00435..af54a23f93 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -697,10 +697,10 @@ var ( StackBigInt = NewStackType(avmBytes, bound(0, maxByteMathSize), "bigint") // StackMethodSelector represents a bytestring that should be treated like a method selector StackMethodSelector = NewStackType(avmBytes, static(4), "method") - // StackStorageKey represents a bytestring that can be used as a key to some storage (global/local/box) - StackStorageKey = NewStackType(avmBytes, bound(0, 64), "key") + // StackStateKey represents a bytestring that can be used as a key to some storage (global/local/box) + StackStateKey = NewStackType(avmBytes, bound(0, 64), "stateKey") // StackBoxName represents a bytestring that can be used as a key to a box - StackBoxName = NewStackType(avmBytes, bound(1, 64), "name") + StackBoxName = NewStackType(avmBytes, bound(1, 64), "boxName") // StackZeroUint64 is a StackUint64 with a minimum value of 0 and a maximum value of 0 StackZeroUint64 = NewStackType(avmUint64, bound(0, 0), "0") @@ -720,7 +720,7 @@ var ( 'T': StackBoolean, 'H': StackBytes32, 'M': StackMethodSelector, - 'K': StackStorageKey, + 'K': StackStateKey, 'N': StackBoxName, } ) diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json index 6a2c74aee0..921a64943e 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec.json @@ -12,22 +12,22 @@ "AVMType": "uint64" }, { - "Name": "none", - "Abbreviation": "x", + "Name": "stateKey", + "Abbreviation": "K", "Bound": [ 0, - 0 + 64 ], - "AVMType": "none" + "AVMType": "[]byte" }, { - "Name": "name", - "Abbreviation": "N", + "Name": "none", + "Abbreviation": "x", "Bound": [ - 1, - 64 + 0, + 0 ], - "AVMType": "[]byte" + "AVMType": "none" }, { "Name": "method", @@ -39,10 +39,10 @@ "AVMType": "[]byte" }, { - "Name": "key", - "Abbreviation": "K", + "Name": "boxName", + "Abbreviation": "N", "Bound": [ - 0, + 1, 64 ], "AVMType": "[]byte" @@ -4055,7 +4055,7 @@ "Opcode": 185, "Name": "box_create", "Args": [ - "name", + "boxName", "uint64" ], "Returns": [ @@ -4073,7 +4073,7 @@ "Opcode": 186, "Name": "box_extract", "Args": [ - "name", + "boxName", "uint64", "uint64" ], @@ -4091,7 +4091,7 @@ "Opcode": 187, "Name": "box_replace", "Args": [ - "name", + "boxName", "uint64", "[]byte" ], @@ -4106,7 +4106,7 @@ "Opcode": 188, "Name": "box_del", "Args": [ - "name" + "boxName" ], "Returns": [ "bool" @@ -4122,7 +4122,7 @@ "Opcode": 189, "Name": "box_len", "Args": [ - "name" + "boxName" ], "Returns": [ "uint64", @@ -4139,7 +4139,7 @@ "Opcode": 190, "Name": "box_get", "Args": [ - "name" + "boxName" ], "Returns": [ "[]byte", @@ -4157,7 +4157,7 @@ "Opcode": 191, "Name": "box_put", "Args": [ - "name", + "boxName", "[]byte" ], "Size": 1,