summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Lee <64482439+algojohnlee@users.noreply.github.com>2021-08-24 20:19:11 -0400
committerGitHub <noreply@github.com>2021-08-24 20:19:11 -0400
commitd1aca92d36fd2a69d44338e566fc76214a089778 (patch)
tree9a9ded7c8b1f5edd22bdae8ed8e7de26ec0949ba
parent1f0544885dccab47b0ed06e09b48a2aa8c8e70e7 (diff)
parentf2be951910d5c6d227e7c45b70b5634b38c98ba2 (diff)
Merge pull request #2800 from algojack/jack/relbeta2.10.1v2.10.1-beta
go-algorand 2.10.1-beta
-rw-r--r--buildnumber.dat2
-rw-r--r--data/transactions/logic/assembler.go61
-rw-r--r--data/transactions/logic/assembler_test.go4
-rw-r--r--data/transactions/logic/debugger.go8
-rw-r--r--data/transactions/logic/eval.go87
-rw-r--r--data/transactions/logic/evalStateful_test.go83
-rw-r--r--data/transactions/logic/fields.go167
-rw-r--r--data/transactions/logic/fields_test.go66
8 files changed, 318 insertions, 160 deletions
diff --git a/buildnumber.dat b/buildnumber.dat
index 573541ac9..d00491fd7 100644
--- a/buildnumber.dat
+++ b/buildnumber.dat
@@ -1 +1 @@
-0
+1
diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go
index 6b8f5793b..c5e38d632 100644
--- a/data/transactions/logic/assembler.go
+++ b/data/transactions/logic/assembler.go
@@ -798,7 +798,7 @@ func assembleTxn(ops *OpStream, spec *OpSpec, args []string) error {
}
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(fs.field))
- ops.returns(TxnFieldTypes[fs.field])
+ ops.returns(fs.ftype)
return nil
}
@@ -840,7 +840,7 @@ func assembleTxna(ops *OpStream, spec *OpSpec, args []string) error {
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(fs.field))
ops.pending.WriteByte(uint8(arrayFieldIdx))
- ops.returns(TxnFieldTypes[fs.field])
+ ops.returns(fs.ftype)
return nil
}
@@ -871,7 +871,7 @@ func assembleGtxn(ops *OpStream, spec *OpSpec, args []string) error {
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(slot))
ops.pending.WriteByte(uint8(fs.field))
- ops.returns(TxnFieldTypes[fs.field])
+ ops.returns(fs.ftype)
return nil
}
@@ -921,7 +921,7 @@ func assembleGtxna(ops *OpStream, spec *OpSpec, args []string) error {
ops.pending.WriteByte(uint8(slot))
ops.pending.WriteByte(uint8(fs.field))
ops.pending.WriteByte(uint8(arrayFieldIdx))
- ops.returns(TxnFieldTypes[fs.field])
+ ops.returns(fs.ftype)
return nil
}
@@ -947,7 +947,7 @@ func assembleGtxns(ops *OpStream, spec *OpSpec, args []string) error {
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(fs.field))
- ops.returns(TxnFieldTypes[fs.field])
+ ops.returns(fs.ftype)
return nil
}
@@ -976,7 +976,7 @@ func assembleGtxnsa(ops *OpStream, spec *OpSpec, args []string) error {
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(fs.field))
ops.pending.WriteByte(uint8(arrayFieldIdx))
- ops.returns(TxnFieldTypes[fs.field])
+ ops.returns(fs.ftype)
return nil
}
@@ -989,15 +989,15 @@ func assembleGlobal(ops *OpStream, spec *OpSpec, args []string) error {
return ops.errorf("%s unknown field: %#v", spec.Name, args[0])
}
if fs.version > ops.Version {
- // no return here. we may as well continue to maintain typestack
- ops.errorf("global %s available in version %d. Missed #pragma version?", args[0], fs.version)
+ //nolint:errcheck // we continue to maintain typestack
+ ops.errorf("%s %s available in version %d. Missed #pragma version?", spec.Name, args[0], fs.version)
}
- val := fs.gfield
+ val := fs.field
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(val))
- ops.trace("%s (%s)", GlobalFieldNames[val], GlobalFieldTypes[val].String())
- ops.returns(GlobalFieldTypes[val])
+ ops.trace("%s (%s)", fs.field.String(), fs.ftype.String())
+ ops.returns(fs.ftype)
return nil
}
@@ -1005,13 +1005,20 @@ func assembleAssetHolding(ops *OpStream, spec *OpSpec, args []string) error {
if len(args) != 1 {
return ops.errorf("%s expects one argument", spec.Name)
}
- val, ok := assetHoldingFields[args[0]]
+ fs, ok := assetHoldingFieldSpecByName[args[0]]
if !ok {
- return ops.errorf("%s unknown arg: %#v", spec.Name, args[0])
+ return ops.errorf("%s unknown field: %#v", spec.Name, args[0])
+ }
+ if fs.version > ops.Version {
+ //nolint:errcheck // we continue to maintain typestack
+ ops.errorf("%s %s available in version %d. Missed #pragma version?", spec.Name, args[0], fs.version)
}
+
+ val := fs.field
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(val))
- ops.returns(AssetHoldingFieldTypes[val], StackUint64)
+ ops.trace("%s (%s)", fs.field.String(), fs.ftype.String())
+ ops.returns(fs.ftype, StackUint64)
return nil
}
@@ -1019,13 +1026,20 @@ func assembleAssetParams(ops *OpStream, spec *OpSpec, args []string) error {
if len(args) != 1 {
return ops.errorf("%s expects one argument", spec.Name)
}
- val, ok := assetParamsFields[args[0]]
+ fs, ok := assetParamsFieldSpecByName[args[0]]
if !ok {
- return ops.errorf("%s unknown arg: %#v", spec.Name, args[0])
+ return ops.errorf("%s unknown field: %#v", spec.Name, args[0])
+ }
+ if fs.version > ops.Version {
+ //nolint:errcheck // we continue to maintain typestack
+ ops.errorf("%s %s available in version %d. Missed #pragma version?", spec.Name, args[0], fs.version)
}
+
+ val := fs.field
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(val))
- ops.returns(AssetParamsFieldTypes[val], StackUint64)
+ ops.trace("%s (%s)", fs.field.String(), fs.ftype.String())
+ ops.returns(fs.ftype, StackUint64)
return nil
}
@@ -1033,13 +1047,20 @@ func assembleAppParams(ops *OpStream, spec *OpSpec, args []string) error {
if len(args) != 1 {
return ops.errorf("%s expects one argument", spec.Name)
}
- val, ok := appParamsFields[args[0]]
+ fs, ok := appParamsFieldSpecByName[args[0]]
if !ok {
- return ops.errorf("%s unknown arg: %#v", spec.Name, args[0])
+ return ops.errorf("%s unknown field: %#v", spec.Name, args[0])
}
+ if fs.version > ops.Version {
+ //nolint:errcheck // we continue to maintain typestack
+ ops.errorf("%s %s available in version %d. Missed #pragma version?", spec.Name, args[0], fs.version)
+ }
+
+ val := fs.field
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(val))
- ops.returns(AppParamsFieldTypes[val], StackUint64)
+ ops.trace("%s (%s)", fs.field.String(), fs.ftype.String())
+ ops.returns(fs.ftype, StackUint64)
return nil
}
diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go
index a8cd151f7..37c6333cd 100644
--- a/data/transactions/logic/assembler_test.go
+++ b/data/transactions/logic/assembler_test.go
@@ -1578,13 +1578,13 @@ func TestAssembleAsset(t *testing.T) {
testProg(t, "int 1; int 1; asset_holding_get ABC 1", v,
expect{3, "asset_holding_get expects one argument"})
testProg(t, "int 1; int 1; asset_holding_get ABC", v,
- expect{3, "asset_holding_get unknown arg: \"ABC\""})
+ expect{3, "asset_holding_get unknown field: \"ABC\""})
testProg(t, "byte 0x1234; asset_params_get ABC 1", v,
expect{2, "asset_params_get ABC 1 arg 0 wanted type uint64..."})
testLine(t, "asset_params_get ABC 1", v, "asset_params_get expects one argument")
- testLine(t, "asset_params_get ABC", v, "asset_params_get unknown arg: \"ABC\"")
+ testLine(t, "asset_params_get ABC", v, "asset_params_get unknown field: \"ABC\"")
}
}
diff --git a/data/transactions/logic/debugger.go b/data/transactions/logic/debugger.go
index 021a53faf..b591aad01 100644
--- a/data/transactions/logic/debugger.go
+++ b/data/transactions/logic/debugger.go
@@ -103,13 +103,13 @@ func makeDebugState(cx *evalContext) DebugState {
Proto: cx.Proto,
}
- globals := make([]basics.TealValue, len(GlobalFieldNames))
- for fieldIdx := range GlobalFieldNames {
- sv, err := cx.globalFieldToStack(GlobalField(fieldIdx))
+ globals := make([]basics.TealValue, len(globalFieldSpecs))
+ for _, fs := range globalFieldSpecs {
+ sv, err := cx.globalFieldToValue(fs)
if err != nil {
sv = stackValue{Bytes: []byte(err.Error())}
}
- globals[fieldIdx] = stackValueToTealValue(&sv)
+ globals[fs.field] = stackValueToTealValue(&sv)
}
ds.Globals = globals
diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go
index ced3baed3..ac06dc701 100644
--- a/data/transactions/logic/eval.go
+++ b/data/transactions/logic/eval.go
@@ -1751,27 +1751,25 @@ func opUncover(cx *evalContext) {
cx.stack[topIdx] = sv
}
-func (cx *evalContext) assetHoldingEnumToValue(holding *basics.AssetHolding, field uint64) (sv stackValue, err error) {
- switch AssetHoldingField(field) {
+func (cx *evalContext) assetHoldingToValue(holding *basics.AssetHolding, fs assetHoldingFieldSpec) (sv stackValue, err error) {
+ switch fs.field {
case AssetBalance:
sv.Uint = holding.Amount
case AssetFrozen:
sv.Uint = boolToUint(holding.Frozen)
default:
- err = fmt.Errorf("invalid asset holding field %d", field)
+ err = fmt.Errorf("invalid asset_holding_get field %d", fs.field)
return
}
- assetHoldingField := AssetHoldingField(field)
- assetHoldingFieldType := AssetHoldingFieldTypes[assetHoldingField]
- if !typecheck(assetHoldingFieldType, sv.argType()) {
- err = fmt.Errorf("%s expected field type is %s but got %s", assetHoldingField.String(), assetHoldingFieldType.String(), sv.argType().String())
+ if !typecheck(fs.ftype, sv.argType()) {
+ err = fmt.Errorf("%s expected field type is %s but got %s", fs.field.String(), fs.ftype.String(), sv.argType().String())
}
return
}
-func (cx *evalContext) assetParamsEnumToValue(params *basics.AssetParams, creator basics.Address, field uint64) (sv stackValue, err error) {
- switch AssetParamsField(field) {
+func (cx *evalContext) assetParamsToValue(params *basics.AssetParams, creator basics.Address, fs assetParamsFieldSpec) (sv stackValue, err error) {
+ switch fs.field {
case AssetTotal:
sv.Uint = params.Total
case AssetDecimals:
@@ -1797,20 +1795,18 @@ func (cx *evalContext) assetParamsEnumToValue(params *basics.AssetParams, creato
case AssetCreator:
sv.Bytes = creator[:]
default:
- err = fmt.Errorf("invalid asset params field %d", field)
+ err = fmt.Errorf("invalid asset_params_get field %d", fs.field)
return
}
- assetParamsField := AssetParamsField(field)
- assetParamsFieldType := AssetParamsFieldTypes[assetParamsField]
- if !typecheck(assetParamsFieldType, sv.argType()) {
- err = fmt.Errorf("%s expected field type is %s but got %s", assetParamsField.String(), assetParamsFieldType.String(), sv.argType().String())
+ if !typecheck(fs.ftype, sv.argType()) {
+ err = fmt.Errorf("%s expected field type is %s but got %s", fs.field.String(), fs.ftype.String(), sv.argType().String())
}
return
}
-func (cx *evalContext) appParamsEnumToValue(params *basics.AppParams, creator basics.Address, field uint64) (sv stackValue, err error) {
- switch AppParamsField(field) {
+func (cx *evalContext) appParamsToValue(params *basics.AppParams, creator basics.Address, fs appParamsFieldSpec) (sv stackValue, err error) {
+ switch fs.field {
case AppApprovalProgram:
sv.Bytes = params.ApprovalProgram[:]
case AppClearStateProgram:
@@ -1828,14 +1824,12 @@ func (cx *evalContext) appParamsEnumToValue(params *basics.AppParams, creator ba
case AppCreator:
sv.Bytes = creator[:]
default:
- err = fmt.Errorf("invalid app params field %d", field)
+ err = fmt.Errorf("invalid app_params_get field %d", fs.field)
return
}
- appParamsField := AppParamsField(field)
- appParamsFieldType := AppParamsFieldTypes[appParamsField]
- if !typecheck(appParamsFieldType, sv.argType()) {
- err = fmt.Errorf("%s expected field type is %s but got %s", appParamsField.String(), appParamsFieldType.String(), sv.argType().String())
+ if !typecheck(fs.ftype, sv.argType()) {
+ err = fmt.Errorf("%s expected field type is %s but got %s", fs.field.String(), fs.ftype.String(), sv.argType().String())
}
return
}
@@ -2285,8 +2279,8 @@ func (cx *evalContext) getCreatorAddress() ([]byte, error) {
var zeroAddress basics.Address
-func (cx *evalContext) globalFieldToStack(field GlobalField) (sv stackValue, err error) {
- switch field {
+func (cx *evalContext) globalFieldToValue(fs globalFieldSpec) (sv stackValue, err error) {
+ switch fs.field {
case MinTxnFee:
sv.Uint = cx.Proto.MinTxnFee
case MinBalance:
@@ -2308,17 +2302,21 @@ func (cx *evalContext) globalFieldToStack(field GlobalField) (sv stackValue, err
case CreatorAddress:
sv.Bytes, err = cx.getCreatorAddress()
default:
- err = fmt.Errorf("invalid global[%d]", field)
+ err = fmt.Errorf("invalid global field %d", fs.field)
+ }
+
+ if !typecheck(fs.ftype, sv.argType()) {
+ err = fmt.Errorf("%s expected field type is %s but got %s", fs.field.String(), fs.ftype.String(), sv.argType().String())
}
+
return sv, err
}
func opGlobal(cx *evalContext) {
- gindex := uint64(cx.program[cx.pc+1])
- globalField := GlobalField(gindex)
+ globalField := GlobalField(cx.program[cx.pc+1])
fs, ok := globalFieldSpecByField[globalField]
if !ok || fs.version > cx.version {
- cx.err = fmt.Errorf("invalid global[%d]", globalField)
+ cx.err = fmt.Errorf("invalid global field %d", globalField)
return
}
if (cx.runModeFlags & fs.mode) == 0 {
@@ -2326,18 +2324,12 @@ func opGlobal(cx *evalContext) {
return
}
- sv, err := cx.globalFieldToStack(globalField)
+ sv, err := cx.globalFieldToValue(fs)
if err != nil {
cx.err = err
return
}
- globalFieldType := GlobalFieldTypes[globalField]
- if !typecheck(globalFieldType, sv.argType()) {
- cx.err = fmt.Errorf("%s expected field type is %s but got %s", globalField.String(), globalFieldType.String(), sv.argType().String())
- return
- }
-
cx.stack = append(cx.stack, sv)
}
@@ -3082,7 +3074,12 @@ func opAssetHoldingGet(cx *evalContext) {
return
}
- fieldIdx := uint64(cx.program[cx.pc+1])
+ holdingField := AssetHoldingField(cx.program[cx.pc+1])
+ fs, ok := assetHoldingFieldSpecByField[holdingField]
+ if !ok || fs.version > cx.version {
+ cx.err = fmt.Errorf("invalid asset_holding_get field %d", holdingField)
+ return
+ }
addr, _, err := accountReference(cx, cx.stack[prev])
if err != nil {
@@ -3101,7 +3098,7 @@ func opAssetHoldingGet(cx *evalContext) {
if holding, err := cx.Ledger.AssetHolding(addr, asset); err == nil {
// the holding exist, read the value
exist = 1
- value, err = cx.assetHoldingEnumToValue(&holding, fieldIdx)
+ value, err = cx.assetHoldingToValue(&holding, fs)
if err != nil {
cx.err = err
return
@@ -3120,7 +3117,12 @@ func opAssetParamsGet(cx *evalContext) {
return
}
- paramIdx := uint64(cx.program[cx.pc+1])
+ paramField := AssetParamsField(cx.program[cx.pc+1])
+ fs, ok := assetParamsFieldSpecByField[paramField]
+ if !ok || fs.version > cx.version {
+ cx.err = fmt.Errorf("invalid asset_params_get field %d", paramField)
+ return
+ }
asset, err := asaReference(cx, cx.stack[last].Uint, true)
if err != nil {
@@ -3133,7 +3135,7 @@ func opAssetParamsGet(cx *evalContext) {
if params, creator, err := cx.Ledger.AssetParams(asset); err == nil {
// params exist, read the value
exist = 1
- value, err = cx.assetParamsEnumToValue(&params, creator, paramIdx)
+ value, err = cx.assetParamsToValue(&params, creator, fs)
if err != nil {
cx.err = err
return
@@ -3152,7 +3154,12 @@ func opAppParamsGet(cx *evalContext) {
return
}
- paramIdx := uint64(cx.program[cx.pc+1])
+ paramField := AppParamsField(cx.program[cx.pc+1])
+ fs, ok := appParamsFieldSpecByField[paramField]
+ if !ok || fs.version > cx.version {
+ cx.err = fmt.Errorf("invalid app_params_get field %d", paramField)
+ return
+ }
app, err := appReference(cx, cx.stack[last].Uint, true)
if err != nil {
@@ -3165,7 +3172,7 @@ func opAppParamsGet(cx *evalContext) {
if params, creator, err := cx.Ledger.AppParams(app); err == nil {
// params exist, read the value
exist = 1
- value, err = cx.appParamsEnumToValue(&params, creator, paramIdx)
+ value, err = cx.appParamsToValue(&params, creator, fs)
if err != nil {
cx.err = err
return
diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go
index d1c53a34a..f782fcce9 100644
--- a/data/transactions/logic/evalStateful_test.go
+++ b/data/transactions/logic/evalStateful_test.go
@@ -1173,7 +1173,7 @@ int 4141
testApp(t, strings.Replace(text, "int 1 // ForeignApps index", "global CurrentApplicationID", -1), now)
}
-const assetsTestProgram = `int 0//account
+const assetsTestTemplate = `int 0//account
int 55
asset_holding_get AssetBalance
!
@@ -1270,33 +1270,51 @@ bnz ok
error:
err
ok:
+%s
+int 1
+`
+
+const v5extras = `
int 0//params
asset_params_get AssetCreator
pop
txn Sender
==
assert
-int 1
`
func TestAssets(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
+ tests := map[uint64]string{
+ 4: fmt.Sprintf(assetsTestTemplate, ""),
+ 5: fmt.Sprintf(assetsTestTemplate, v5extras),
+ }
+
+ for v, source := range tests {
+ testAssetsByVersion(t, source, v)
+ }
+}
+
+func testAssetsByVersion(t *testing.T, assetsTestProgram string, version uint64) {
for _, field := range AssetHoldingFieldNames {
- if !strings.Contains(assetsTestProgram, field) {
+ fs := assetHoldingFieldSpecByName[field]
+ if fs.version <= version && !strings.Contains(assetsTestProgram, field) {
t.Errorf("TestAssets missing field %v", field)
}
}
for _, field := range AssetParamsFieldNames {
- if !strings.Contains(assetsTestProgram, field) {
+ fs := assetParamsFieldSpecByName[field]
+ if fs.version <= version && !strings.Contains(assetsTestProgram, field) {
t.Errorf("TestAssets missing field %v", field)
}
}
txn := makeSampleTxn()
pre := defaultEvalParamsWithVersion(nil, &txn, directRefEnabledVersion-1)
- now := defaultEvalParams(nil, &txn)
+ require.GreaterOrEqual(t, version, uint64(directRefEnabledVersion))
+ now := defaultEvalParamsWithVersion(nil, &txn, version)
ledger := makeTestLedger(
map[basics.Address]uint64{
txn.Txn.Sender: 1,
@@ -1358,8 +1376,12 @@ func TestAssets(t *testing.T) {
// but old code cannot
testProg(t, strings.Replace(assetsTestProgram, "int 0//account", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), directRefEnabledVersion-1, expect{3, "asset_holding_get AssetBalance arg 0 wanted type uint64..."})
- testApp(t, strings.Replace(assetsTestProgram, "int 0//params", "int 55", -1), pre, "invalid Asset ref")
- testApp(t, strings.Replace(assetsTestProgram, "int 55", "int 0", -1), pre, "err opcode")
+
+ if version < 5 {
+ // Can't run these with AppCreator anyway
+ testApp(t, strings.Replace(assetsTestProgram, "int 0//params", "int 55", -1), pre, "invalid Asset ref")
+ testApp(t, strings.Replace(assetsTestProgram, "int 55", "int 0", -1), pre, "err opcode")
+ }
// check holdings bool value
source := `intcblock 0 55 1
@@ -1380,12 +1402,12 @@ intc_2 // 1
testApp(t, source, now)
// check holdings invalid offsets
- ops := testProg(t, source, AssemblerMaxVersion)
+ ops := testProg(t, source, version)
require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_holding_get"].Opcode, ops.Program[8])
ops.Program[9] = 0x02
_, err := EvalStateful(ops.Program, now)
require.Error(t, err)
- require.Contains(t, err.Error(), "invalid asset holding field 2")
+ require.Contains(t, err.Error(), "invalid asset_holding_get field 2")
// check holdings bool value
source = `intcblock 0 1
@@ -1405,12 +1427,12 @@ intc_1
ledger.newAsset(txn.Txn.Sender, 55, params)
testApp(t, source, now)
// check holdings invalid offsets
- ops = testProg(t, source, AssemblerMaxVersion)
+ ops = testProg(t, source, version)
require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_params_get"].Opcode, ops.Program[6])
ops.Program[7] = 0x20
_, err = EvalStateful(ops.Program, now)
require.Error(t, err)
- require.Contains(t, err.Error(), "invalid asset params field 32")
+ require.Contains(t, err.Error(), "invalid asset_params_get field 32")
// check empty string
source = `intcblock 0 1
@@ -2645,9 +2667,8 @@ func TestEnumFieldErrors(t *testing.T) {
TxnFieldTypes[Amount] = origTxnType
}()
- ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion)
- require.NoError(t, err)
- _, err = Eval(ops.Program, ep)
+ ops := testProg(t, source, AssemblerMaxVersion)
+ _, err := Eval(ops.Program, ep)
require.Error(t, err)
require.Contains(t, err.Error(), "Amount expected field type is []byte but got uint64")
_, err = EvalStateful(ops.Program, ep)
@@ -2655,14 +2676,16 @@ func TestEnumFieldErrors(t *testing.T) {
require.Contains(t, err.Error(), "Amount expected field type is []byte but got uint64")
source = `global MinTxnFee`
- origGlobalType := GlobalFieldTypes[MinTxnFee]
- GlobalFieldTypes[MinTxnFee] = StackBytes
+
+ origMinTxnFs := globalFieldSpecByField[MinTxnFee]
+ badMinTxnFs := origMinTxnFs
+ badMinTxnFs.ftype = StackBytes
+ globalFieldSpecByField[MinTxnFee] = badMinTxnFs
defer func() {
- GlobalFieldTypes[MinTxnFee] = origGlobalType
+ globalFieldSpecByField[MinTxnFee] = origMinTxnFs
}()
- ops, err = AssembleStringWithVersion(source, AssemblerMaxVersion)
- require.NoError(t, err)
+ ops = testProg(t, source, AssemblerMaxVersion)
_, err = Eval(ops.Program, ep)
require.Error(t, err)
require.Contains(t, err.Error(), "MinTxnFee expected field type is []byte but got uint64")
@@ -2698,14 +2721,15 @@ int 55
asset_holding_get AssetBalance
pop
`
- origAssetHoldingType := AssetHoldingFieldTypes[AssetBalance]
- AssetHoldingFieldTypes[AssetBalance] = StackBytes
+ origBalanceFs := assetHoldingFieldSpecByField[AssetBalance]
+ badBalanceFs := origBalanceFs
+ badBalanceFs.ftype = StackBytes
+ assetHoldingFieldSpecByField[AssetBalance] = badBalanceFs
defer func() {
- AssetHoldingFieldTypes[AssetBalance] = origAssetHoldingType
+ assetHoldingFieldSpecByField[AssetBalance] = origBalanceFs
}()
- ops, err = AssembleStringWithVersion(source, AssemblerMaxVersion)
- require.NoError(t, err)
+ ops = testProg(t, source, AssemblerMaxVersion)
_, err = EvalStateful(ops.Program, ep)
require.Error(t, err)
require.Contains(t, err.Error(), "AssetBalance expected field type is []byte but got uint64")
@@ -2714,14 +2738,15 @@ pop
asset_params_get AssetTotal
pop
`
- origAssetTotalType := AssetParamsFieldTypes[AssetTotal]
- AssetParamsFieldTypes[AssetTotal] = StackBytes
+ origTotalFs := assetParamsFieldSpecByField[AssetTotal]
+ badTotalFs := origTotalFs
+ badTotalFs.ftype = StackBytes
+ assetParamsFieldSpecByField[AssetTotal] = badTotalFs
defer func() {
- AssetParamsFieldTypes[AssetTotal] = origAssetTotalType
+ assetParamsFieldSpecByField[AssetTotal] = origTotalFs
}()
- ops, err = AssembleStringWithVersion(source, AssemblerMaxVersion)
- require.NoError(t, err)
+ ops = testProg(t, source, AssemblerMaxVersion)
_, err = EvalStateful(ops.Program, ep)
require.Error(t, err)
require.Contains(t, err.Error(), "AssetTotal expected field type is []byte but got uint64")
diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go
index 9bc37547c..bc242859e 100644
--- a/data/transactions/logic/fields.go
+++ b/data/transactions/logic/fields.go
@@ -337,7 +337,7 @@ var GlobalFieldNames []string
var GlobalFieldTypes []StackType
type globalFieldSpec struct {
- gfield GlobalField
+ field GlobalField
ftype StackType
mode runMode
version uint64
@@ -384,20 +384,33 @@ const (
// AssetHoldingFieldNames are arguments to the 'asset_holding_get' opcode
var AssetHoldingFieldNames []string
-type assetHoldingFieldType struct {
- field AssetHoldingField
- ftype StackType
+// AssetHoldingFieldTypes is StackUint64 StackBytes in parallel with AssetHoldingFieldNames
+var AssetHoldingFieldTypes []StackType
+
+type assetHoldingFieldSpec struct {
+ field AssetHoldingField
+ ftype StackType
+ version uint64
}
-var assetHoldingFieldTypeList = []assetHoldingFieldType{
- {AssetBalance, StackUint64},
- {AssetFrozen, StackUint64},
+var assetHoldingFieldSpecs = []assetHoldingFieldSpec{
+ {AssetBalance, StackUint64, 2},
+ {AssetFrozen, StackUint64, 2},
}
-// AssetHoldingFieldTypes is StackUint64 StackBytes in parallel with AssetHoldingFieldNames
-var AssetHoldingFieldTypes []StackType
+var assetHoldingFieldSpecByField map[AssetHoldingField]assetHoldingFieldSpec
+var assetHoldingFieldSpecByName ahfNameSpecMap
-var assetHoldingFields map[string]uint64
+// simple interface used by doc generator for fields versioning
+type ahfNameSpecMap map[string]assetHoldingFieldSpec
+
+func (s ahfNameSpecMap) getExtraFor(name string) (extra string) {
+ // Uses 2 here because asset fields were introduced in 2
+ if s[name].version > 2 {
+ extra = fmt.Sprintf("LogicSigVersion >= %d.", s[name].version)
+ }
+ return
+}
// AssetParamsField is an enum for `asset_params_get` opcode
type AssetParamsField int
@@ -435,30 +448,43 @@ const (
// AssetParamsFieldNames are arguments to the 'asset_params_get' opcode
var AssetParamsFieldNames []string
-type assetParamsFieldType struct {
- field AssetParamsField
- ftype StackType
+// AssetParamsFieldTypes is StackUint64 StackBytes in parallel with AssetParamsFieldNames
+var AssetParamsFieldTypes []StackType
+
+type assetParamsFieldSpec struct {
+ field AssetParamsField
+ ftype StackType
+ version uint64
}
-var assetParamsFieldTypeList = []assetParamsFieldType{
- {AssetTotal, StackUint64},
- {AssetDecimals, StackUint64},
- {AssetDefaultFrozen, StackUint64},
- {AssetUnitName, StackBytes},
- {AssetName, StackBytes},
- {AssetURL, StackBytes},
- {AssetMetadataHash, StackBytes},
- {AssetManager, StackBytes},
- {AssetReserve, StackBytes},
- {AssetFreeze, StackBytes},
- {AssetClawback, StackBytes},
- {AssetCreator, StackBytes},
+var assetParamsFieldSpecs = []assetParamsFieldSpec{
+ {AssetTotal, StackUint64, 2},
+ {AssetDecimals, StackUint64, 2},
+ {AssetDefaultFrozen, StackUint64, 2},
+ {AssetUnitName, StackBytes, 2},
+ {AssetName, StackBytes, 2},
+ {AssetURL, StackBytes, 2},
+ {AssetMetadataHash, StackBytes, 2},
+ {AssetManager, StackBytes, 2},
+ {AssetReserve, StackBytes, 2},
+ {AssetFreeze, StackBytes, 2},
+ {AssetClawback, StackBytes, 2},
+ {AssetCreator, StackBytes, 5},
}
-// AssetParamsFieldTypes is StackUint64 StackBytes in parallel with AssetParamsFieldNames
-var AssetParamsFieldTypes []StackType
+var assetParamsFieldSpecByField map[AssetParamsField]assetParamsFieldSpec
+var assetParamsFieldSpecByName apfNameSpecMap
-var assetParamsFields map[string]uint64
+// simple interface used by doc generator for fields versioning
+type apfNameSpecMap map[string]assetParamsFieldSpec
+
+func (s apfNameSpecMap) getExtraFor(name string) (extra string) {
+ // Uses 2 here because asset fields were introduced in 2
+ if s[name].version > 2 {
+ extra = fmt.Sprintf("LogicSigVersion >= %d.", s[name].version)
+ }
+ return
+}
// AppParamsField is an enum for `app_params_get` opcode
type AppParamsField int
@@ -488,26 +514,39 @@ const (
// AppParamsFieldNames are arguments to the 'app_params_get' opcode
var AppParamsFieldNames []string
-type appParamsFieldType struct {
- field AppParamsField
- ftype StackType
+// AppParamsFieldTypes is StackUint64 StackBytes in parallel with AppParamsFieldNames
+var AppParamsFieldTypes []StackType
+
+type appParamsFieldSpec struct {
+ field AppParamsField
+ ftype StackType
+ version uint64
}
-var appParamsFieldTypeList = []appParamsFieldType{
- {AppApprovalProgram, StackBytes},
- {AppClearStateProgram, StackBytes},
- {AppGlobalNumUint, StackUint64},
- {AppGlobalNumByteSlice, StackUint64},
- {AppLocalNumUint, StackUint64},
- {AppLocalNumByteSlice, StackUint64},
- {AppExtraProgramPages, StackUint64},
- {AppCreator, StackBytes},
+var appParamsFieldSpecs = []appParamsFieldSpec{
+ {AppApprovalProgram, StackBytes, 5},
+ {AppClearStateProgram, StackBytes, 5},
+ {AppGlobalNumUint, StackUint64, 5},
+ {AppGlobalNumByteSlice, StackUint64, 5},
+ {AppLocalNumUint, StackUint64, 5},
+ {AppLocalNumByteSlice, StackUint64, 5},
+ {AppExtraProgramPages, StackUint64, 5},
+ {AppCreator, StackBytes, 5},
}
-// AppParamsFieldTypes is StackUint64 StackBytes in parallel with AppParamsFieldNames
-var AppParamsFieldTypes []StackType
+var appParamsFieldSpecByField map[AppParamsField]appParamsFieldSpec
+var appParamsFieldSpecByName appNameSpecMap
+
+// simple interface used by doc generator for fields versioning
+type appNameSpecMap map[string]appParamsFieldSpec
-var appParamsFields map[string]uint64
+func (s appNameSpecMap) getExtraFor(name string) (extra string) {
+ // Uses 2 here because app fields were introduced in 5
+ if s[name].version > 5 {
+ extra = fmt.Sprintf("LogicSigVersion >= %d.", s[name].version)
+ }
+ return
+}
func init() {
TxnFieldNames = make([]string, int(invalidTxnField))
@@ -535,8 +574,8 @@ func init() {
GlobalFieldTypes = make([]StackType, len(GlobalFieldNames))
globalFieldSpecByField = make(map[GlobalField]globalFieldSpec, len(GlobalFieldNames))
for _, s := range globalFieldSpecs {
- GlobalFieldTypes[int(s.gfield)] = s.ftype
- globalFieldSpecByField[s.gfield] = s
+ GlobalFieldTypes[int(s.field)] = s.ftype
+ globalFieldSpecByField[s.field] = s
}
globalFieldSpecByName = make(gfNameSpecMap, len(GlobalFieldNames))
for i, gfn := range GlobalFieldNames {
@@ -548,12 +587,14 @@ func init() {
AssetHoldingFieldNames[int(i)] = i.String()
}
AssetHoldingFieldTypes = make([]StackType, len(AssetHoldingFieldNames))
- for _, ft := range assetHoldingFieldTypeList {
- AssetHoldingFieldTypes[int(ft.field)] = ft.ftype
+ assetHoldingFieldSpecByField = make(map[AssetHoldingField]assetHoldingFieldSpec, len(AssetHoldingFieldNames))
+ for _, s := range assetHoldingFieldSpecs {
+ AssetHoldingFieldTypes[int(s.field)] = s.ftype
+ assetHoldingFieldSpecByField[s.field] = s
}
- assetHoldingFields = make(map[string]uint64)
- for i, fn := range AssetHoldingFieldNames {
- assetHoldingFields[fn] = uint64(i)
+ assetHoldingFieldSpecByName = make(ahfNameSpecMap, len(AssetHoldingFieldNames))
+ for i, ahfn := range AssetHoldingFieldNames {
+ assetHoldingFieldSpecByName[ahfn] = assetHoldingFieldSpecByField[AssetHoldingField(i)]
}
AssetParamsFieldNames = make([]string, int(invalidAssetParamsField))
@@ -561,12 +602,14 @@ func init() {
AssetParamsFieldNames[int(i)] = i.String()
}
AssetParamsFieldTypes = make([]StackType, len(AssetParamsFieldNames))
- for _, ft := range assetParamsFieldTypeList {
- AssetParamsFieldTypes[int(ft.field)] = ft.ftype
+ assetParamsFieldSpecByField = make(map[AssetParamsField]assetParamsFieldSpec, len(AssetParamsFieldNames))
+ for _, s := range assetParamsFieldSpecs {
+ AssetParamsFieldTypes[int(s.field)] = s.ftype
+ assetParamsFieldSpecByField[s.field] = s
}
- assetParamsFields = make(map[string]uint64)
- for i, fn := range AssetParamsFieldNames {
- assetParamsFields[fn] = uint64(i)
+ assetParamsFieldSpecByName = make(apfNameSpecMap, len(AssetParamsFieldNames))
+ for i, apfn := range AssetParamsFieldNames {
+ assetParamsFieldSpecByName[apfn] = assetParamsFieldSpecByField[AssetParamsField(i)]
}
AppParamsFieldNames = make([]string, int(invalidAppParamsField))
@@ -574,12 +617,14 @@ func init() {
AppParamsFieldNames[int(i)] = i.String()
}
AppParamsFieldTypes = make([]StackType, len(AppParamsFieldNames))
- for _, ft := range appParamsFieldTypeList {
- AppParamsFieldTypes[int(ft.field)] = ft.ftype
+ appParamsFieldSpecByField = make(map[AppParamsField]appParamsFieldSpec, len(AppParamsFieldNames))
+ for _, s := range appParamsFieldSpecs {
+ AppParamsFieldTypes[int(s.field)] = s.ftype
+ appParamsFieldSpecByField[s.field] = s
}
- appParamsFields = make(map[string]uint64)
- for i, fn := range AppParamsFieldNames {
- appParamsFields[fn] = uint64(i)
+ appParamsFieldSpecByName = make(appNameSpecMap, len(AppParamsFieldNames))
+ for i, apfn := range AppParamsFieldNames {
+ appParamsFieldSpecByName[apfn] = appParamsFieldSpecByField[AppParamsField(i)]
}
txnTypeIndexes = make(map[string]uint64, len(TxnTypeNames))
diff --git a/data/transactions/logic/fields_test.go b/data/transactions/logic/fields_test.go
index b36e715e3..e88f3820f 100644
--- a/data/transactions/logic/fields_test.go
+++ b/data/transactions/logic/fields_test.go
@@ -47,7 +47,7 @@ func TestGlobalFieldsVersions(t *testing.T) {
ledger := makeTestLedger(nil)
for _, field := range fields {
- text := fmt.Sprintf("global %s", field.gfield.String())
+ text := fmt.Sprintf("global %s", field.field.String())
// check assembler fails if version before introduction
testLine(t, text, assemblerNoVersion, "...available in version...")
for v := uint64(0); v < field.version; v++ {
@@ -76,13 +76,13 @@ func TestGlobalFieldsVersions(t *testing.T) {
ops.Program[0] = byte(preLogicVersion) // set version
_, err = Eval(ops.Program, ep)
require.Error(t, err)
- require.Contains(t, err.Error(), "invalid global[")
+ require.Contains(t, err.Error(), "invalid global field")
// check opcodes failures on 0 version
ops.Program[0] = 0 // set version to 0
_, err = Eval(ops.Program, ep)
require.Error(t, err)
- require.Contains(t, err.Error(), "invalid global[")
+ require.Contains(t, err.Error(), "invalid global field")
}
}
@@ -178,3 +178,63 @@ func TestTxnFieldVersions(t *testing.T) {
}
}
}
+
+func TestAssetParamsFieldsVersions(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ var fields []assetParamsFieldSpec
+ for _, fs := range assetParamsFieldSpecs {
+ if fs.version > 2 {
+ fields = append(fields, fs)
+ }
+ }
+ require.Greater(t, len(fields), 0)
+
+ for _, field := range fields {
+ // 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; pop; pop; intc_1", field.field.String())
+ // check assembler fails if version before introduction
+ for v := uint64(2); v <= AssemblerMaxVersion; v++ {
+ ep, _ := makeSampleEnv()
+ ep.Proto.LogicSigVersion = v
+ if field.version > v {
+ testProg(t, text, v, expect{3, "...available in version..."})
+ ops := testProg(t, text, field.version) // assemble in the future
+ scratch := ops.Program
+ scratch[0] = byte(v) // but we'll tweak the version byte back to v
+ err := CheckStateful(scratch, ep)
+ require.NoError(t, err)
+ pass, err := EvalStateful(scratch, ep) // so eval fails on future field
+ require.False(t, pass)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "invalid asset_params_get field")
+ } else {
+ testProg(t, text, v)
+ testApp(t, text, ep)
+ }
+ }
+
+ }
+}
+
+func TestFieldVersions(t *testing.T) {
+ // This test is weird, it confirms that we don't need to
+ // bother with a "good" test for AssetHolding and AppParams
+ // fields. It will fail if we add a field that has a
+ // different teal debut version, and then we'll need a test
+ // like TestAssetParamsFieldsVersions that checks the field is
+ // unavailable before its debut.
+
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ for _, fs := range assetHoldingFieldSpecs {
+ require.Equal(t, uint64(2), fs.version)
+ }
+
+ for _, fs := range appParamsFieldSpecs {
+ require.Equal(t, uint64(5), fs.version)
+ }
+}