Skip to content

Commit 7126597

Browse files
authored
AVM: Rework around opcode fields for more flexible costs (#3832)
1 parent 70b8050 commit 7126597

20 files changed

+1175
-1191
lines changed

cmd/opdoc/opdoc.go

Lines changed: 44 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ func integerConstantsTableMarkdown(out io.Writer) {
7777
out.Write([]byte("\n"))
7878
}
7979

80-
type speccer interface {
81-
SpecByName(name string) logic.FieldSpec
80+
func fieldGroupMarkdown(out io.Writer, group logic.FieldGroup) {
81+
fieldSpecsMarkdown(out, group.Names, group.Specs)
8282
}
8383

84-
func fieldSpecsMarkdown(out io.Writer, names []string, specs speccer) {
84+
func fieldSpecsMarkdown(out io.Writer, names []string, specs logic.FieldSpecMap) {
8585
showTypes := false
8686
showVers := false
8787
spec0 := specs.SpecByName(names[0])
@@ -127,37 +127,37 @@ func fieldSpecsMarkdown(out io.Writer, names []string, specs speccer) {
127127

128128
func transactionFieldsMarkdown(out io.Writer) {
129129
fmt.Fprintf(out, "\n`txn` Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)):\n\n")
130-
fieldSpecsMarkdown(out, logic.TxnFieldNames, logic.TxnFieldSpecByName)
130+
fieldGroupMarkdown(out, logic.TxnFields)
131131
}
132132

133133
func globalFieldsMarkdown(out io.Writer) {
134134
fmt.Fprintf(out, "\n`global` Fields:\n\n")
135-
fieldSpecsMarkdown(out, logic.GlobalFieldNames, logic.GlobalFieldSpecByName)
135+
fieldGroupMarkdown(out, logic.GlobalFields)
136136
}
137137

138138
func assetHoldingFieldsMarkdown(out io.Writer) {
139139
fmt.Fprintf(out, "\n`asset_holding_get` Fields:\n\n")
140-
fieldSpecsMarkdown(out, logic.AssetHoldingFieldNames, logic.AssetHoldingFieldSpecByName)
140+
fieldGroupMarkdown(out, logic.AssetHoldingFields)
141141
}
142142

143143
func assetParamsFieldsMarkdown(out io.Writer) {
144144
fmt.Fprintf(out, "\n`asset_params_get` Fields:\n\n")
145-
fieldSpecsMarkdown(out, logic.AssetParamsFieldNames, logic.AssetParamsFieldSpecByName)
145+
fieldGroupMarkdown(out, logic.AssetParamsFields)
146146
}
147147

148148
func appParamsFieldsMarkdown(out io.Writer) {
149149
fmt.Fprintf(out, "\n`app_params_get` Fields:\n\n")
150-
fieldSpecsMarkdown(out, logic.AppParamsFieldNames, logic.AppParamsFieldSpecByName)
150+
fieldGroupMarkdown(out, logic.AppParamsFields)
151151
}
152152

153153
func acctParamsFieldsMarkdown(out io.Writer) {
154154
fmt.Fprintf(out, "\n`acct_params_get` Fields:\n\n")
155-
fieldSpecsMarkdown(out, logic.AcctParamsFieldNames, logic.AcctParamsFieldSpecByName)
155+
fieldGroupMarkdown(out, logic.AcctParamsFields)
156156
}
157157

158158
func ecDsaCurvesMarkdown(out io.Writer) {
159159
fmt.Fprintf(out, "\n`ECDSA` Curves:\n\n")
160-
fieldSpecsMarkdown(out, logic.EcdsaCurveNames, logic.EcdsaCurveSpecByName)
160+
fieldGroupMarkdown(out, logic.EcdsaCurves)
161161
}
162162

163163
func immediateMarkdown(op *logic.OpSpec) string {
@@ -214,19 +214,19 @@ func opToMarkdown(out io.Writer, op *logic.OpSpec) (err error) {
214214
fmt.Fprintf(out, "- **Cost**:\n")
215215
for _, cost := range costs {
216216
if cost.From == cost.To {
217-
fmt.Fprintf(out, " - %d (v%d)\n", cost.Cost, cost.To)
217+
fmt.Fprintf(out, " - %s (v%d)\n", cost.Cost, cost.To)
218218
} else {
219219
if cost.To < logic.LogicVersion {
220-
fmt.Fprintf(out, " - %d (v%d - v%d)\n", cost.Cost, cost.From, cost.To)
220+
fmt.Fprintf(out, " - %s (v%d - v%d)\n", cost.Cost, cost.From, cost.To)
221221
} else {
222-
fmt.Fprintf(out, " - %d (since v%d)\n", cost.Cost, cost.From)
222+
fmt.Fprintf(out, " - %s (since v%d)\n", cost.Cost, cost.From)
223223
}
224224
}
225225
}
226226
} else {
227227
cost := costs[0].Cost
228-
if cost != 1 {
229-
fmt.Fprintf(out, "- **Cost**: %d\n", cost)
228+
if cost != "1" {
229+
fmt.Fprintf(out, "- **Cost**: %s\n", cost)
230230
}
231231
}
232232
if op.Version > 1 {
@@ -250,10 +250,8 @@ func opToMarkdown(out io.Writer, op *logic.OpSpec) (err error) {
250250
appParamsFieldsMarkdown(out)
251251
case "acct_params_get":
252252
acctParamsFieldsMarkdown(out)
253-
default:
254-
if strings.HasPrefix(op.Name, "ecdsa") {
255-
ecDsaCurvesMarkdown(out)
256-
}
253+
case "ecdsa_verify":
254+
ecDsaCurvesMarkdown(out)
257255
}
258256
ode := logic.OpDocExtra(op.Name)
259257
if ode != "" {
@@ -280,7 +278,6 @@ type OpRecord struct {
280278
Name string
281279
Args string `json:",omitempty"`
282280
Returns string `json:",omitempty"`
283-
Cost int
284281
Size int
285282

286283
ArgEnum []string `json:",omitempty"`
@@ -321,29 +318,33 @@ func typeString(types []logic.StackType) string {
321318
return string(out)
322319
}
323320

324-
func fieldsAndTypes(names []string, specs speccer) ([]string, string) {
325-
types := make([]logic.StackType, len(names))
326-
for i, name := range names {
327-
types[i] = specs.SpecByName(name).Type()
321+
func fieldsAndTypes(group logic.FieldGroup) ([]string, string) {
322+
// reminder: group.Names can be "sparse" See: logic.TxnaFields
323+
fields := make([]string, 0, len(group.Names))
324+
types := make([]logic.StackType, 0, len(group.Names))
325+
for _, name := range group.Names {
326+
if name != "" {
327+
fields = append(fields, name)
328+
types = append(types, group.Specs.SpecByName(name).Type())
329+
}
328330
}
329-
return names, typeString(types)
331+
return fields, typeString(types)
330332
}
331333

332334
func argEnums(name string) (names []string, types string) {
333335
switch name {
334336
case "txn", "gtxn", "gtxns", "itxn", "gitxn", "itxn_field":
335-
return fieldsAndTypes(logic.TxnFieldNames, logic.TxnFieldSpecByName)
337+
return fieldsAndTypes(logic.TxnFields)
336338
case "global":
337339
return
338340
case "txna", "gtxna", "gtxnsa", "txnas", "gtxnas", "gtxnsas", "itxna", "gitxna":
339-
// Map is the whole txn field spec map. That's fine, we only lookup the given names.
340-
return fieldsAndTypes(logic.TxnaFieldNames(), logic.TxnFieldSpecByName)
341+
return fieldsAndTypes(logic.TxnaFields)
341342
case "asset_holding_get":
342-
return fieldsAndTypes(logic.AssetHoldingFieldNames, logic.AssetHoldingFieldSpecByName)
343+
return fieldsAndTypes(logic.AssetHoldingFields)
343344
case "asset_params_get":
344-
return fieldsAndTypes(logic.AssetParamsFieldNames, logic.AssetParamsFieldSpecByName)
345+
return fieldsAndTypes(logic.AssetParamsFields)
345346
case "app_params_get":
346-
return fieldsAndTypes(logic.AppParamsFieldNames, logic.AppParamsFieldSpecByName)
347+
return fieldsAndTypes(logic.AppParamsFields)
347348
default:
348349
return nil, ""
349350
}
@@ -357,7 +358,6 @@ func buildLanguageSpec(opGroups map[string][]string) *LanguageSpec {
357358
records[i].Name = spec.Name
358359
records[i].Args = typeString(spec.Args)
359360
records[i].Returns = typeString(spec.Returns)
360-
records[i].Cost = spec.Details.Cost
361361
records[i].Size = spec.Details.Size
362362
records[i].ArgEnum, records[i].ArgEnumTypes = argEnums(spec.Name)
363363
records[i].Doc = logic.OpDoc(spec.Name)
@@ -400,29 +400,18 @@ func main() {
400400
integerConstantsTableMarkdown(constants)
401401
constants.Close()
402402

403-
txnfields := create("txn_fields.md")
404-
fieldSpecsMarkdown(txnfields, logic.TxnFieldNames, logic.TxnFieldSpecByName)
405-
txnfields.Close()
406-
407-
globalfields := create("global_fields.md")
408-
fieldSpecsMarkdown(globalfields, logic.GlobalFieldNames, logic.GlobalFieldSpecByName)
409-
globalfields.Close()
410-
411-
assetholding := create("asset_holding_fields.md")
412-
fieldSpecsMarkdown(assetholding, logic.AssetHoldingFieldNames, logic.AssetHoldingFieldSpecByName)
413-
assetholding.Close()
414-
415-
assetparams := create("asset_params_fields.md")
416-
fieldSpecsMarkdown(assetparams, logic.AssetParamsFieldNames, logic.AssetParamsFieldSpecByName)
417-
assetparams.Close()
418-
419-
appparams := create("app_params_fields.md")
420-
fieldSpecsMarkdown(appparams, logic.AppParamsFieldNames, logic.AppParamsFieldSpecByName)
421-
appparams.Close()
422-
423-
acctparams, _ := os.Create("acct_params_fields.md")
424-
fieldSpecsMarkdown(acctparams, logic.AcctParamsFieldNames, logic.AcctParamsFieldSpecByName)
425-
acctparams.Close()
403+
written := make(map[string]bool)
404+
opSpecs := logic.OpcodesByVersion(logic.LogicVersion)
405+
for _, spec := range opSpecs {
406+
for _, imm := range spec.Details.Immediates {
407+
if imm.Group != nil && !written[imm.Group.Name] {
408+
out := create(imm.Group.Name + "_fields.md")
409+
fieldSpecsMarkdown(out, imm.Group.Names, imm.Group.Specs)
410+
out.Close()
411+
written[imm.Group.Name] = true
412+
}
413+
}
414+
}
426415

427416
langspecjs := create("langspec.json")
428417
enc := json.NewEncoder(langspecjs)

cmd/opdoc/tmLanguage.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package main
1818

1919
import (
2020
"fmt"
21+
"sort"
2122
"strings"
2223

2324
"github.com/algorand/go-algorand/data/transactions/logic"
@@ -122,11 +123,18 @@ func buildSyntaxHighlight() *tmLanguage {
122123
},
123124
}
124125
var allNamedFields []string
125-
allNamedFields = append(allNamedFields, logic.TxnFieldNames...)
126-
allNamedFields = append(allNamedFields, logic.GlobalFieldNames...)
127-
allNamedFields = append(allNamedFields, logic.AssetHoldingFieldNames...)
128-
allNamedFields = append(allNamedFields, logic.AssetParamsFieldNames...)
129-
allNamedFields = append(allNamedFields, logic.OnCompletionNames...)
126+
allNamedFields = append(allNamedFields, logic.TxnTypeNames[:]...)
127+
allNamedFields = append(allNamedFields, logic.OnCompletionNames[:]...)
128+
accumulated := make(map[string]bool)
129+
opSpecs := logic.OpcodesByVersion(logic.LogicVersion)
130+
for _, spec := range opSpecs {
131+
for _, imm := range spec.Details.Immediates {
132+
if imm.Group != nil && !accumulated[imm.Group.Name] {
133+
allNamedFields = append(allNamedFields, imm.Group.Names[:]...)
134+
accumulated[imm.Group.Name] = true
135+
}
136+
}
137+
}
130138

131139
literals.Patterns = append(literals.Patterns, pattern{
132140
Name: "variable.parameter.teal",
@@ -153,7 +161,15 @@ func buildSyntaxHighlight() *tmLanguage {
153161
},
154162
}
155163
var allArithmetics []string
156-
for grp, names := range logic.OpGroups {
164+
165+
var keys []string
166+
for key := range logic.OpGroups {
167+
keys = append(keys, key)
168+
}
169+
sort.Strings(keys)
170+
for _, grp := range keys {
171+
names := logic.OpGroups[grp]
172+
sort.Strings(names)
157173
switch grp {
158174
case "Flow Control":
159175
keywords.Patterns = append(keywords.Patterns, pattern{

data/transactions/logic/.gitignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

data/transactions/logic/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ Some of these have immediate data in the byte or bytes after the opcode.
436436
| 16 | TypeEnum | uint64 | | See table below |
437437
| 17 | XferAsset | uint64 | | Asset ID |
438438
| 18 | AssetAmount | uint64 | | value in Asset's units |
439-
| 19 | AssetSender | []byte | | 32 byte address. Causes clawback of all value of asset from AssetSender if Sender is the Clawback address of the asset. |
439+
| 19 | AssetSender | []byte | | 32 byte address. Moves asset from AssetSender if Sender is the Clawback address of the asset. |
440440
| 20 | AssetReceiver | []byte | | 32 byte address |
441441
| 21 | AssetCloseTo | []byte | | 32 byte address |
442442
| 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 |
@@ -457,7 +457,7 @@ Some of these have immediate data in the byte or bytes after the opcode.
457457
| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset |
458458
| 38 | ConfigAssetName | []byte | v2 | The asset name |
459459
| 39 | ConfigAssetURL | []byte | v2 | URL |
460-
| 40 | ConfigAssetMetadataHash | []byte | v2 | 32 byte commitment to some unspecified asset metadata |
460+
| 40 | ConfigAssetMetadataHash | []byte | v2 | 32 byte commitment to unspecified asset metadata |
461461
| 41 | ConfigAssetManager | []byte | v2 | 32 byte address |
462462
| 42 | ConfigAssetReserve | []byte | v2 | 32 byte address |
463463
| 43 | ConfigAssetFreeze | []byte | v2 | 32 byte address |
@@ -527,7 +527,7 @@ Asset fields include `AssetHolding` and `AssetParam` fields that are used in the
527527
| 4 | AssetName | []byte | | Asset name |
528528
| 5 | AssetURL | []byte | | URL with additional info about the asset |
529529
| 6 | AssetMetadataHash | []byte | | Arbitrary commitment |
530-
| 7 | AssetManager | []byte | | Manager commitment |
530+
| 7 | AssetManager | []byte | | Manager address |
531531
| 8 | AssetReserve | []byte | | Reserve address |
532532
| 9 | AssetFreeze | []byte | | Freeze address |
533533
| 10 | AssetClawback | []byte | | Clawback address |

data/transactions/logic/TEAL_opcodes.md

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,15 @@ The 32 byte public key is the last element on the stack, preceded by the 64 byte
5050
- Opcode: 0x05 {uint8 curve index}
5151
- Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte &rarr; ..., uint64
5252
- for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1}
53-
- **Cost**: 1700
53+
- **Cost**: Secp256k1=1700 Secp256r1=2500
5454
- Availability: v5
5555

5656
`ECDSA` Curves:
5757

5858
| Index | Name | In | Notes |
5959
| - | ------ | - | --------- |
60-
| 0 | Secp256k1 | | secp256k1 curve |
61-
| 1 | Secp256r1 | v7 | secp256r1 curve |
60+
| 0 | Secp256k1 | | secp256k1 curve, used in Bitcoin |
61+
| 1 | Secp256r1 | v7 | secp256r1 curve, NIST standard |
6262

6363

6464
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.
@@ -68,17 +68,9 @@ The 32 byte Y-component of a public key is the last element on the stack, preced
6868
- Opcode: 0x06 {uint8 curve index}
6969
- Stack: ..., A: []byte &rarr; ..., X: []byte, Y: []byte
7070
- decompress pubkey A into components X, Y
71-
- **Cost**: 650
71+
- **Cost**: Secp256k1=650 Secp256r1=2400
7272
- Availability: v5
7373

74-
`ECDSA` Curves:
75-
76-
| Index | Name | In | Notes |
77-
| - | ------ | - | --------- |
78-
| 0 | Secp256k1 | | secp256k1 curve |
79-
| 1 | Secp256r1 | v7 | secp256r1 curve |
80-
81-
8274
The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.
8375

8476
## ecdsa_pk_recover v
@@ -89,14 +81,6 @@ The 33 byte public key in a compressed form to be decompressed into X and Y (top
8981
- **Cost**: 2000
9082
- Availability: v5
9183

92-
`ECDSA` Curves:
93-
94-
| Index | Name | In | Notes |
95-
| - | ------ | - | --------- |
96-
| 0 | Secp256k1 | | secp256k1 curve |
97-
| 1 | Secp256r1 | v7 | secp256r1 curve |
98-
99-
10084
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.
10185

10286
## +
@@ -396,7 +380,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u
396380
| 16 | TypeEnum | uint64 | | See table below |
397381
| 17 | XferAsset | uint64 | | Asset ID |
398382
| 18 | AssetAmount | uint64 | | value in Asset's units |
399-
| 19 | AssetSender | []byte | | 32 byte address. Causes clawback of all value of asset from AssetSender if Sender is the Clawback address of the asset. |
383+
| 19 | AssetSender | []byte | | 32 byte address. Moves asset from AssetSender if Sender is the Clawback address of the asset. |
400384
| 20 | AssetReceiver | []byte | | 32 byte address |
401385
| 21 | AssetCloseTo | []byte | | 32 byte address |
402386
| 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 |
@@ -417,7 +401,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u
417401
| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset |
418402
| 38 | ConfigAssetName | []byte | v2 | The asset name |
419403
| 39 | ConfigAssetURL | []byte | v2 | URL |
420-
| 40 | ConfigAssetMetadataHash | []byte | v2 | 32 byte commitment to some unspecified asset metadata |
404+
| 40 | ConfigAssetMetadataHash | []byte | v2 | 32 byte commitment to unspecified asset metadata |
421405
| 41 | ConfigAssetManager | []byte | v2 | 32 byte address |
422406
| 42 | ConfigAssetReserve | []byte | v2 | 32 byte address |
423407
| 43 | ConfigAssetFreeze | []byte | v2 | 32 byte address |
@@ -933,7 +917,7 @@ params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or
933917
| 4 | AssetName | []byte | | Asset name |
934918
| 5 | AssetURL | []byte | | URL with additional info about the asset |
935919
| 6 | AssetMetadataHash | []byte | | Arbitrary commitment |
936-
| 7 | AssetManager | []byte | | Manager commitment |
920+
| 7 | AssetManager | []byte | | Manager address |
937921
| 8 | AssetReserve | []byte | | Reserve address |
938922
| 9 | AssetFreeze | []byte | | Freeze address |
939923
| 10 | AssetClawback | []byte | | Clawback address |

0 commit comments

Comments
 (0)