diff --git a/cmd/goal/account.go b/cmd/goal/account.go index d104d048ae..9c6d8a56ec 100644 --- a/cmd/goal/account.go +++ b/cmd/goal/account.go @@ -921,14 +921,14 @@ var changeOnlineCmd = &cobra.Command{ firstTxRound, lastTxRound, _, err := client.ComputeValidityRounds(firstValid, lastValid, numValidRounds) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } err = changeAccountOnlineStatus( accountAddress, online, statusChangeTxFile, walletName, firstTxRound, lastTxRound, transactionFee, scLeaseBytes(cmd), dataDir, client, ) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } }, } @@ -1097,7 +1097,7 @@ var renewParticipationKeyCmd = &cobra.Command{ err = generateAndRegisterPartKey(accountAddress, currentRound, roundLastValid, txRoundLastValid, transactionFee, scLeaseBytes(cmd), keyDilution, walletName, dataDir, client) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } version := config.GetCurrentVersion() diff --git a/cmd/goal/application.go b/cmd/goal/application.go index f263e0bf04..f3f2ba95df 100644 --- a/cmd/goal/application.go +++ b/cmd/goal/application.go @@ -27,6 +27,7 @@ import ( "os" "strconv" "strings" + "time" "github.com/spf13/cobra" @@ -40,6 +41,7 @@ import ( "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/util" ) var ( @@ -72,9 +74,16 @@ var ( // platform seems not so far-fetched? foreignApps []string foreignAssets []string - appBoxes []string // parse these as we do app args, with optional number and comma in front + appStrBoxes []string // parse these as we do app args, with optional number and comma in front appStrAccounts []string + // for these, an omitted addr is the sender. an omitted app is the called app. + appStrHoldings []string // format: asset,addr OR asset -> 5245,XQJEJECPWUOXSKMIC5TCSARPVGHQJIIOKHO7WTKEPPLJMKG3D7VWWID66E + appStrLocals []string // format: app,addr OR app OR addr OR + + // controls whether all these refs put into the old-style "foreign arrays" or the new-style tx.Access + appUseAccess bool + appArgs []string appInputFilename string @@ -100,9 +109,12 @@ func init() { appCmd.PersistentFlags().StringArrayVar(&appArgs, "app-arg", nil, "Args to encode for application transactions (all will be encoded to a byte slice). For ints, use the form 'int:1234'. For raw bytes, use the form 'b64:A=='. For printable strings, use the form 'str:hello'. For addresses, use the form 'addr:XYZ...'.") appCmd.PersistentFlags().StringSliceVar(&foreignApps, "foreign-app", nil, "Indexes of other apps whose global state is read in this transaction") appCmd.PersistentFlags().StringSliceVar(&foreignAssets, "foreign-asset", nil, "Indexes of assets whose parameters are read in this transaction") - appCmd.PersistentFlags().StringArrayVar(&appBoxes, "box", nil, "Boxes that may be accessed by this transaction. Use the same form as app-arg to name the box, preceded by an optional app-id and comma. No app-id indicates the box is accessible by the app being called.") + appCmd.PersistentFlags().StringArrayVar(&appStrBoxes, "box", nil, "A Box that may be accessed by this transaction. Use the same form as app-arg to name the box, preceded by an optional app-id and comma. Zero or omitted app-id indicates the box is accessible by the app being called.") appCmd.PersistentFlags().StringSliceVar(&appStrAccounts, "app-account", nil, "Accounts that may be accessed from application logic") - appCmd.PersistentFlags().StringVarP(&appInputFilename, "app-input", "i", "", "JSON file containing encoded arguments and inputs (mutually exclusive with app-arg, app-account, foreign-app, foreign-asset, and box)") + appCmd.PersistentFlags().StringArrayVar(&appStrHoldings, "holding", nil, "A Holding that may be accessed from application logic. An asset-id followed by a comma and an address") + appCmd.PersistentFlags().StringArrayVar(&appStrLocals, "local", nil, "A Local State that may be accessed from application logic. An optional app-id and comma, followed by an address. Zero or omitted app-id indicates the local state for app being called.") + appCmd.PersistentFlags().BoolVar(&appUseAccess, "access", false, "Put references into the transaction's access list, instead of foreign arrays.") + appCmd.PersistentFlags().StringVarP(&appInputFilename, "app-input", "i", "", "JSON file containing encoded arguments and inputs (mutually exclusive with app-arg, app-account, foreign-app, foreign-asset, local, holding, and box)") appCmd.PersistentFlags().StringVar(&approvalProgFile, "approval-prog", "", "(Uncompiled) TEAL assembly program filename for approving/rejecting transactions") appCmd.PersistentFlags().StringVar(&clearProgFile, "clear-prog", "", "(Uncompiled) TEAL assembly program filename for updating application state when a user clears their local state") @@ -202,7 +214,7 @@ func panicIfErr(err error) { func newAppCallBytes(arg string) apps.AppCallBytes { appBytes, err := apps.NewAppCallBytes(arg) if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } return appBytes } @@ -212,6 +224,9 @@ type appCallInputs struct { ForeignApps []uint64 `codec:"foreignapps"` ForeignAssets []uint64 `codec:"foreignassets"` Boxes []boxRef `codec:"boxes"` + Holdings []holdingRef `codec:"holdings"` + Locals []localRef `codec:"locals"` + UseAccess bool `codec:"access"` Args []apps.AppCallBytes `codec:"args"` } @@ -220,9 +235,35 @@ type boxRef struct { name apps.AppCallBytes `codec:"name"` } -// newBoxRef parses a command-line box ref, which is an optional appId, a comma, +type holdingRef struct { + assetID uint64 `codec:"asset"` + address string `codec:"account"` +} + +type localRef struct { + appID uint64 `codec:"app"` + address string `codec:"account"` +} + +// parseUInt64 parses a string into a uint64. It must succeed or the error is +// reprted and `goal` exits. It accepts extra arguments to create a more +// helpful error message. +func parseUInt64(number string, thing string, context ...string) uint64 { + n, err := strconv.ParseUint(number, 10, 64) + if err != nil { + extra := "" + if len(context) == 1 { + extra = " in " + context[0] + } + reportErrorf("Could not parse '%s' as %s%s: %s", + number, thing, extra, errors.Unwrap(err)) + } + return n +} + +// parseBoxRef parses a command-line box ref, which is an optional appId, a comma, // and then the same format as an app call arg. -func newBoxRef(arg string) boxRef { +func parseBoxRef(arg string) boxRef { encoding, value, found := strings.Cut(arg, ":") if !found { reportErrorf("box refs should be of the form '[,]encoding:value'") @@ -232,11 +273,7 @@ func newBoxRef(arg string) boxRef { if appStr, enc, found := strings.Cut(encoding, ","); found { // There was a comma in the part before the ":" encoding = enc - var err error - appID, err = strconv.ParseUint(appStr, 10, 64) - if err != nil { - reportErrorf("Could not parse app id in box ref: %v", err) - } + appID = parseUInt64(appStr, "app id", "box ref") } return boxRef{ appID: appID, @@ -244,86 +281,119 @@ func newBoxRef(arg string) boxRef { } } -func stringsToUint64(strs []string) []uint64 { - out := make([]uint64, len(strs)) - for i, idstr := range strs { - parsed, err := strconv.ParseUint(idstr, 10, 64) - if err != nil { - reportErrorf("Could not parse foreign app id: %v", err) - } - out[i] = parsed - } - return out -} +// parseHoldingRef parses a command-line box ref, which is an assetId and an +// optional address, separated by a comma. No address means Sender. +func parseHoldingRef(arg string) holdingRef { + assetStr, address, _ := strings.Cut(arg, ",") + assetID := parseUInt64(assetStr, "asset id", "holding ref") -func stringsToBoxRefs(strs []string) []boxRef { - out := make([]boxRef, len(strs)) - for i, brstr := range strs { - out[i] = newBoxRef(brstr) + return holdingRef{ + assetID: assetID, + address: address, // "" would mean Sender } - return out } -func translateBoxRefs(input []boxRef, foreignApps []uint64) []transactions.BoxRef { - output := make([]transactions.BoxRef, len(input)) - for i, tbr := range input { - rawName, err := tbr.name.Raw() - if err != nil { - reportErrorf("Could not decode box name %s: %v", tbr.name, err) - } +// parseLocalRef parses a command-line local state ref, which is an optional appId +// and an optional address, separated by a comma. No appId means the called app, +// No address means Sender. They can not _both_ be omitted, as that is a +// non-sensical LocalRef - it would make the local state of the sender for the +// current app available. That is implicitly available already. +func parseLocalRef(arg string) localRef { + one, two, both := strings.Cut(arg, ",") - index := uint64(0) - if tbr.appID != 0 { - found := false - for a, id := range foreignApps { - if tbr.appID == id { - index = uint64(a + 1) - found = true - break - } - } - // Check appIdx after the foreignApps check. If the user actually - // put the appIdx in foreignApps, and then used the appIdx here - // (rather than 0), then maybe they really want to use it in the - // transaction as the full number. Though it's hard to see why. - if !found && tbr.appID == appIdx { - index = 0 - found = true - } - if !found { - reportErrorf("Box ref with appId (%d) not in foreign-apps", tbr.appID) - } + if both { + appID := parseUInt64(one, "app id", "local ref") + return localRef{ + appID: appID, + address: two, } - output[i] = transactions.BoxRef{ - Index: index, - Name: rawName, + } + + // one is missing, so we should have a number or an address. Try to parse + // it as a number. If it fails, assume an address, because at this stage we + // don't parse addresses. + + if appID, err := strconv.ParseUint(one, 10, 64); err == nil { + return localRef{ + appID: appID, + address: "", } } - return output + + return localRef{ + appID: 0, + address: one, + } } -func parseAppInputs(inputs appCallInputs) (args [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef) { - accounts = inputs.Accounts - foreignApps = inputs.ForeignApps - foreignAssets = inputs.ForeignAssets - boxes = translateBoxRefs(inputs.Boxes, foreignApps) - args = make([][]byte, len(inputs.Args)) - for i, arg := range inputs.Args { +// parseAppInputs converts inputs from a very textual input form (coming from +// CLI or a JSON file), to a more strongly typed form, using the various "real" +// types from `basics`. +func parseAppInputs(inputs appCallInputs) ([][]byte, libgoal.RefBundle) { + args := make([][]byte, 0, len(inputs.Args)) + for _, arg := range inputs.Args { rawValue, err := arg.Raw() if err != nil { - reportErrorf("Could not decode input at index %d: %v", i, err) + reportErrorf("Could not decode app-arg %s:%s: %v", arg.Encoding, arg.Value, err) } - args[i] = rawValue + args = append(args, rawValue) } - return + locals := util.Map(inputs.Locals, func(hr localRef) basics.LocalRef { + return basics.LocalRef{ + App: basics.AppIndex(hr.appID), + Address: cliAddress(hr.address)} + }) + holdings := util.Map(inputs.Holdings, func(hr holdingRef) basics.HoldingRef { + return basics.HoldingRef{ + Asset: basics.AssetIndex(hr.assetID), + Address: cliAddress(hr.address)} + }) + boxes := util.Map(inputs.Boxes, func(br boxRef) basics.BoxRef { + rawName, err := br.name.Raw() + if err != nil { + reportErrorf("Could not decode box name %s: %v", br.name, err) + } + return basics.BoxRef{App: basics.AppIndex(br.appID), Name: string(rawName)} + }) + refs := libgoal.RefBundle{ + UseAccess: inputs.UseAccess, + Accounts: util.Map(inputs.Accounts, cliAddress), + Apps: util.Map(inputs.ForeignApps, func(idx uint64) basics.AppIndex { return basics.AppIndex(idx) }), + Assets: util.Map(inputs.ForeignAssets, func(idx uint64) basics.AssetIndex { return basics.AssetIndex(idx) }), + + Locals: locals, + Holdings: holdings, + Boxes: boxes, + } + return args, refs +} + +func cliAddress(acct string) basics.Address { + if acct == "" { + return basics.Address{} // will be interpreted as Sender + } + if strings.HasPrefix(acct, "app(") && strings.HasSuffix(acct, ")") { + appStr := acct[4 : len(acct)-1] + appID := parseUInt64(appStr, "app id", acct) + return basics.AppIndex(appID).Address() + } + addr, err := basics.UnmarshalChecksumAddress(acct) + if err != nil { + reportErrorln(err) + } + return addr } -func processAppInputFile() (args [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef) { +func getAppInputsFromFile() appCallInputs { + reportWarnf("Using a JSON app input file is deprecated and will be removed soon. Please speak up if the feature matters to you.") + time.Sleep(5 * time.Second) + var inputs appCallInputs f, err := os.Open(appInputFilename) if err != nil { reportErrorf("Could not open app input JSON file: %v", err) } + defer f.Close() dec := protocol.NewJSONDecoder(f) err = dec.Decode(&inputs) @@ -331,22 +401,14 @@ func processAppInputFile() (args [][]byte, accounts []string, foreignApps []uint reportErrorf("Could not decode app input JSON file: %v", err) } - return parseAppInputs(inputs) + return inputs } -func getAppInputs() (args [][]byte, accounts []string, _ []uint64, assets []uint64, boxes []transactions.BoxRef) { - if appInputFilename != "" { - if appArgs != nil || appStrAccounts != nil || foreignApps != nil || foreignAssets != nil { - reportErrorf("Cannot specify both command-line arguments/resources and JSON input filename") - } - return processAppInputFile() - } - +func getAppInputsFromCLI() appCallInputs { // we need to ignore empty strings from appArgs because app-arg was // previously a StringSliceVar, which also does that, and some test depend // on it. appArgs became `StringArrayVar` in order to support abi arguments // which contain commas. - var encodedArgs []apps.AppCallBytes for _, arg := range appArgs { if len(arg) > 0 { @@ -354,12 +416,33 @@ func getAppInputs() (args [][]byte, accounts []string, _ []uint64, assets []uint } } - inputs := appCallInputs{ - Accounts: appStrAccounts, - ForeignApps: stringsToUint64(foreignApps), - ForeignAssets: stringsToUint64(foreignAssets), - Boxes: stringsToBoxRefs(appBoxes), - Args: encodedArgs, + return appCallInputs{ + UseAccess: appUseAccess, + Accounts: appStrAccounts, + ForeignApps: util.Map(foreignApps, func(s string) uint64 { + return parseUInt64(s, "app id", "foreign-app") + }), + ForeignAssets: util.Map(foreignAssets, func(s string) uint64 { + return parseUInt64(s, "asset id", "foreign-asset") + }), + Boxes: util.Map(appStrBoxes, parseBoxRef), + Holdings: util.Map(appStrHoldings, parseHoldingRef), + Locals: util.Map(appStrLocals, parseLocalRef), + Args: encodedArgs, + } +} + +func getAppInputs() ([][]byte, libgoal.RefBundle) { + var inputs appCallInputs + if appInputFilename != "" { + if appArgs != nil || appStrAccounts != nil || + foreignApps != nil || foreignAssets != nil || appStrBoxes != nil || + appStrHoldings != nil || appStrLocals != nil { + reportErrorf("Cannot specify both command-line arguments/resources and JSON input filename") + } + inputs = getAppInputsFromFile() + } else { + inputs = getAppInputsFromCLI() } return parseAppInputs(inputs) @@ -447,14 +530,15 @@ var createAppCmd = &cobra.Command{ // Parse transaction parameters approvalProg, clearProg := mustParseProgArgs() onCompletionEnum := mustParseOnCompletion(onCompletion) - appArgs, appAccounts, foreignApps, foreignAssets, boxes := getAppInputs() + appArgs, refs := getAppInputs() switch onCompletionEnum { case transactions.CloseOutOC, transactions.ClearStateOC: reportWarnf("'--on-completion %s' may be ill-formed for 'goal app create'", onCompletion) } - tx, err := client.MakeUnsignedAppCreateTx(onCompletionEnum, approvalProg, clearProg, globalSchema, localSchema, appArgs, appAccounts, foreignApps, foreignAssets, boxes, extraPages) + tx, err := client.MakeUnsignedAppCreateTx(onCompletionEnum, approvalProg, clearProg, globalSchema, localSchema, + appArgs, refs, extraPages) if err != nil { reportErrorf("Cannot create application txn: %v", err) } @@ -497,7 +581,7 @@ var createAppCmd = &cobra.Command{ if !noWaitAfterSend { txn, err := waitForCommit(client, txid, lv) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } if txn.ApplicationIndex != nil && *txn.ApplicationIndex != 0 { reportInfof("Created app with app index %d", *txn.ApplicationIndex) @@ -511,7 +595,7 @@ var createAppCmd = &cobra.Command{ err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) } if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } } }, @@ -527,9 +611,9 @@ var updateAppCmd = &cobra.Command{ // Parse transaction parameters approvalProg, clearProg := mustParseProgArgs() - appArgs, appAccounts, foreignApps, foreignAssets, boxes := getAppInputs() + appArgs, refs := getAppInputs() - tx, err := client.MakeUnsignedAppUpdateTx(appIdx, appArgs, appAccounts, foreignApps, foreignAssets, boxes, approvalProg, clearProg, rejectVersion) + tx, err := client.MakeUnsignedAppUpdateTx(appIdx, appArgs, approvalProg, clearProg, refs, rejectVersion) if err != nil { reportErrorf("Cannot create application txn: %v", err) } @@ -572,7 +656,7 @@ var updateAppCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lv) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { @@ -582,7 +666,7 @@ var updateAppCmd = &cobra.Command{ err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) } if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } } }, @@ -597,9 +681,9 @@ var optInAppCmd = &cobra.Command{ dataDir, client := getDataDirAndClient() // Parse transaction parameters - appArgs, appAccounts, foreignApps, foreignAssets, boxes := getAppInputs() + appArgs, refs := getAppInputs() - tx, err := client.MakeUnsignedAppOptInTx(appIdx, appArgs, appAccounts, foreignApps, foreignAssets, boxes, rejectVersion) + tx, err := client.MakeUnsignedAppOptInTx(appIdx, appArgs, refs, rejectVersion) if err != nil { reportErrorf("Cannot create application txn: %v", err) } @@ -642,7 +726,7 @@ var optInAppCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lv) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { @@ -652,7 +736,7 @@ var optInAppCmd = &cobra.Command{ err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) } if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } } }, @@ -667,9 +751,9 @@ var closeOutAppCmd = &cobra.Command{ dataDir, client := getDataDirAndClient() // Parse transaction parameters - appArgs, appAccounts, foreignApps, foreignAssets, boxes := getAppInputs() + appArgs, refs := getAppInputs() - tx, err := client.MakeUnsignedAppCloseOutTx(appIdx, appArgs, appAccounts, foreignApps, foreignAssets, boxes, rejectVersion) + tx, err := client.MakeUnsignedAppCloseOutTx(appIdx, appArgs, refs, rejectVersion) if err != nil { reportErrorf("Cannot create application txn: %v", err) } @@ -712,7 +796,7 @@ var closeOutAppCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lv) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { @@ -722,7 +806,7 @@ var closeOutAppCmd = &cobra.Command{ err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) } if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } } }, @@ -737,9 +821,9 @@ var clearAppCmd = &cobra.Command{ dataDir, client := getDataDirAndClient() // Parse transaction parameters - appArgs, appAccounts, foreignApps, foreignAssets, boxes := getAppInputs() + appArgs, refs := getAppInputs() - tx, err := client.MakeUnsignedAppClearStateTx(appIdx, appArgs, appAccounts, foreignApps, foreignAssets, boxes, rejectVersion) + tx, err := client.MakeUnsignedAppClearStateTx(appIdx, appArgs, refs, rejectVersion) if err != nil { reportErrorf("Cannot create application txn: %v", err) } @@ -782,7 +866,7 @@ var clearAppCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lv) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { @@ -792,7 +876,7 @@ var clearAppCmd = &cobra.Command{ err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) } if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } } }, @@ -804,12 +888,11 @@ var callAppCmd = &cobra.Command{ Long: `Call an application, invoking application-specific functionality`, Args: validateNoPosArgsFn, Run: func(cmd *cobra.Command, _ []string) { - dataDir, client := getDataDirAndClient() - // Parse transaction parameters - appArgs, appAccounts, foreignApps, foreignAssets, boxes := getAppInputs() + appArgs, refs := getAppInputs() + dataDir, client := getDataDirAndClient() - tx, err := client.MakeUnsignedAppNoOpTx(appIdx, appArgs, appAccounts, foreignApps, foreignAssets, boxes, rejectVersion) + tx, err := client.MakeUnsignedAppNoOpTx(appIdx, appArgs, refs, rejectVersion) if err != nil { reportErrorf("Cannot create application txn: %v", err) } @@ -852,7 +935,7 @@ var callAppCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lv) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { @@ -862,7 +945,7 @@ var callAppCmd = &cobra.Command{ err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) } if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } } }, @@ -877,9 +960,9 @@ var deleteAppCmd = &cobra.Command{ dataDir, client := getDataDirAndClient() // Parse transaction parameters - appArgs, appAccounts, foreignApps, foreignAssets, boxes := getAppInputs() + appArgs, refs := getAppInputs() - tx, err := client.MakeUnsignedAppDeleteTx(appIdx, appArgs, appAccounts, foreignApps, foreignAssets, boxes, rejectVersion) + tx, err := client.MakeUnsignedAppDeleteTx(appIdx, appArgs, refs, rejectVersion) if err != nil { reportErrorf("Cannot create application txn: %v", err) } @@ -922,7 +1005,7 @@ var deleteAppCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lv) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { @@ -932,7 +1015,7 @@ var deleteAppCmd = &cobra.Command{ err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) } if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } } }, @@ -1104,7 +1187,7 @@ func populateMethodCallTxnArgs(types []string, values []string) ([]transactions. // into the appropriate foreign array. Their placement will be as compact as possible, which means // values will be deduplicated and any value that is the sender or the current app will not be added // to the foreign array. -func populateMethodCallReferenceArgs(sender string, currentApp uint64, types []string, values []string, accounts *[]string, apps *[]uint64, assets *[]uint64) ([]int, error) { +func populateMethodCallReferenceArgs(sender string, currentApp basics.AppIndex, types []string, values []string, refs *libgoal.RefBundle) ([]int, error) { resolvedIndexes := make([]int, len(types)) for i, value := range values { @@ -1115,29 +1198,28 @@ func populateMethodCallReferenceArgs(sender string, currentApp uint64, types []s if value == sender { resolved = 0 } else { + valAddress := cliAddress(value) duplicate := false - for j, account := range *accounts { - if value == account { + for j, account := range refs.Accounts { + if valAddress == account { resolved = j + 1 // + 1 because 0 is the sender duplicate = true break } } if !duplicate { - resolved = len(*accounts) + 1 - *accounts = append(*accounts, value) + resolved = len(refs.Accounts) + 1 + refs.Accounts = append(refs.Accounts, valAddress) } } case abi.ApplicationReferenceType: - appID, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return nil, fmt.Errorf("Unable to parse application ID '%s': %s", value, err) - } + ui := parseUInt64(value, "app id") + appID := basics.AppIndex(ui) if appID == currentApp { resolved = 0 } else { duplicate := false - for j, app := range *apps { + for j, app := range refs.Apps { if appID == app { resolved = j + 1 // + 1 because 0 is the current app duplicate = true @@ -1145,17 +1227,15 @@ func populateMethodCallReferenceArgs(sender string, currentApp uint64, types []s } } if !duplicate { - resolved = len(*apps) + 1 - *apps = append(*apps, appID) + resolved = len(refs.Apps) + 1 + refs.Apps = append(refs.Apps, appID) } } case abi.AssetReferenceType: - assetID, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return nil, fmt.Errorf("Unable to parse asset ID '%s': %s", value, err) - } + ui := parseUInt64(value, "asset id") + assetID := basics.AssetIndex(ui) duplicate := false - for j, asset := range *assets { + for j, asset := range refs.Assets { if assetID == asset { resolved = j duplicate = true @@ -1163,8 +1243,8 @@ func populateMethodCallReferenceArgs(sender string, currentApp uint64, types []s } } if !duplicate { - resolved = len(*assets) - *assets = append(*assets, assetID) + resolved = len(refs.Assets) + refs.Assets = append(refs.Assets, assetID) } default: return nil, fmt.Errorf("Unknown reference type: %s", types[i]) @@ -1255,7 +1335,7 @@ var methodAppCmd = &cobra.Command{ dataDir, client := getDataDirAndClient() // Parse transaction parameters - appArgsParsed, appAccounts, foreignApps, foreignAssets, boxes := getAppInputs() + appArgsParsed, refs := getAppInputs() if len(appArgsParsed) > 0 { reportErrorf("--arg and --app-arg are mutually exclusive, do not use --app-arg") } @@ -1355,7 +1435,7 @@ var methodAppCmd = &cobra.Command{ } } - refArgsResolved, err := populateMethodCallReferenceArgs(account, appIdx, refArgTypes, refArgValues, &appAccounts, &foreignApps, &foreignAssets) + refArgsResolved, err := populateMethodCallReferenceArgs(account, basics.AppIndex(appIdx), refArgTypes, refArgValues, &refs) if err != nil { reportErrorf("error populating reference arguments: %v", err) } @@ -1376,7 +1456,7 @@ var methodAppCmd = &cobra.Command{ } appCallTxn, err := client.MakeUnsignedApplicationCallTx( - appIdx, applicationArgs, appAccounts, foreignApps, foreignAssets, boxes, + appIdx, applicationArgs, refs, onCompletionEnum, approvalProg, clearProg, globalSchema, localSchema, extraPages, rejectVersion) if err != nil { @@ -1453,7 +1533,7 @@ var methodAppCmd = &cobra.Command{ err = writeSignedTxnsToFile(signedTxnGroup, outFilename) } if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } return } @@ -1483,12 +1563,12 @@ var methodAppCmd = &cobra.Command{ if !noWaitAfterSend { _, err := waitForCommit(client, txid, lv) if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } resp, err := client.PendingTransactionInformation(txid) if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } if methodCreatesApp && resp.ApplicationIndex != nil && *resp.ApplicationIndex != 0 { diff --git a/cmd/goal/asset.go b/cmd/goal/asset.go index 1625b13676..842af074a3 100644 --- a/cmd/goal/asset.go +++ b/cmd/goal/asset.go @@ -323,7 +323,7 @@ var createAssetCmd = &cobra.Command{ if !noWaitAfterSend { txn, err := waitForCommit(client, txid, lv) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } if txn.AssetIndex != nil && *txn.AssetIndex != 0 { reportInfof("Created asset with asset index %d", *txn.AssetIndex) @@ -332,7 +332,7 @@ var createAssetCmd = &cobra.Command{ } else { err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } } }, @@ -402,13 +402,13 @@ var destroyAssetCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lastValid) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } } }, @@ -495,13 +495,13 @@ var configAssetCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lastValid) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } } }, @@ -582,13 +582,13 @@ var sendAssetCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lastValid) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } } }, @@ -651,13 +651,13 @@ var freezeAssetCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lastValid) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } } }, @@ -740,13 +740,13 @@ var optinAssetCmd = &cobra.Command{ if !noWaitAfterSend { _, err2 = waitForCommit(client, txid, lastValid) if err2 != nil { - reportErrorln(err2.Error()) + reportErrorln(err2) } } } else { err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } } }, diff --git a/cmd/goal/clerk.go b/cmd/goal/clerk.go index fbd0788941..d7b187f91f 100644 --- a/cmd/goal/clerk.go +++ b/cmd/goal/clerk.go @@ -40,6 +40,7 @@ import ( "github.com/algorand/go-algorand/ledger/simulation" "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/util" "github.com/spf13/cobra" ) @@ -411,13 +412,13 @@ var sendCmd = &cobra.Command{ var err error rekeyTo, err = basics.UnmarshalChecksumAddress(rekeyToAddress) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } } client := ensureFullClient(dataDir) firstValid, lastValid, _, err = client.ComputeValidityRounds(firstValid, lastValid, numValidRounds) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } payment, err := client.ConstructPayment( fromAddressResolved, toAddressResolved, fee, amount, noteBytes, closeToAddressResolved, @@ -548,7 +549,7 @@ var sendCmd = &cobra.Command{ if !noWaitAfterSend { _, err = waitForCommit(client, txid, lastValid) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } } } else { @@ -558,7 +559,7 @@ var sendCmd = &cobra.Command{ err = writeFile(outFilename, protocol.Encode(&stx), 0600) } if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } } }, @@ -1156,13 +1157,10 @@ var dryrunCmd = &cobra.Command{ // Write dryrun data to file dataDir := datadir.EnsureSingleDataDir() client := ensureFullClient(dataDir) - accts, err := unmarshalSlice(dumpForDryrunAccts) - if err != nil { - reportErrorln(err.Error()) - } + accts := util.Map(dumpForDryrunAccts, cliAddress) data, err := libgoal.MakeDryrunStateBytes(client, nil, stxns, accts, string(proto), dumpForDryrunFormat.String()) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } writeFile(outFilename, data, 0600) return @@ -1375,19 +1373,6 @@ var simulateCmd = &cobra.Command{ }, } -// unmarshalSlice converts string addresses to basics.Address -func unmarshalSlice(accts []string) ([]basics.Address, error) { - result := make([]basics.Address, 0, len(accts)) - for _, acct := range accts { - addr, err := basics.UnmarshalChecksumAddress(acct) - if err != nil { - return nil, err - } - result = append(result, addr) - } - return result, nil -} - func decodeTxnsFromFile(file string) []transactions.SignedTxn { data, err := readFile(file) if err != nil { diff --git a/cmd/goal/commands.go b/cmd/goal/commands.go index 8054884af9..9a94f17b09 100644 --- a/cmd/goal/commands.go +++ b/cmd/goal/commands.go @@ -38,6 +38,7 @@ import ( "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/util" ) var log = logging.Base() @@ -527,13 +528,10 @@ func writeFile(filename string, data []byte, perm os.FileMode) error { // writeDryrunReqToFile creates dryrun request object and writes to a file func writeDryrunReqToFile(client libgoal.Client, txnOrStxn interface{}, outFilename string) (err error) { proto, _ := getProto(protoVersion) - accts, err := unmarshalSlice(dumpForDryrunAccts) - if err != nil { - reportErrorln(err.Error()) - } + accts := util.Map(dumpForDryrunAccts, cliAddress) data, err := libgoal.MakeDryrunStateBytes(client, txnOrStxn, []transactions.SignedTxn{}, accts, string(proto), dumpForDryrunFormat.String()) if err != nil { - reportErrorln(err.Error()) + reportErrorln(err) } err = writeFile(outFilename, data, 0600) return diff --git a/cmd/goal/formatting_test.go b/cmd/goal/formatting_test.go index d113e3225b..dafeb0cda5 100644 --- a/cmd/goal/formatting_test.go +++ b/cmd/goal/formatting_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -58,49 +59,56 @@ func TestNewAppCallBytes(t *testing.T) { require.Panics(t, func() { newAppCallBytes("hello") }) } -func TestNewBoxRef(t *testing.T) { +func TestParseBoxRef(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - br := newBoxRef("str:hello") + br := parseBoxRef("str:hello") require.EqualValues(t, 0, br.appID) require.Equal(t, "str", br.name.Encoding) require.Equal(t, "hello", br.name.Value) - require.Panics(t, func() { newBoxRef("1,hello") }) - require.Panics(t, func() { newBoxRef("hello") }) + require.Panics(t, func() { parseBoxRef("1,hello") }) + require.Panics(t, func() { parseBoxRef("hello") }) - br = newBoxRef("2,str:hello") + br = parseBoxRef("2,str:hello") require.EqualValues(t, 2, br.appID) require.Equal(t, "str", br.name.Encoding) require.Equal(t, "hello", br.name.Value) } -func TestStringsToBoxRefs(t *testing.T) { +func TestParseHoldingRef(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - brs := stringsToBoxRefs([]string{"77,str:hello", "55,int:6", "int:88"}) - require.EqualValues(t, 77, brs[0].appID) - require.EqualValues(t, 55, brs[1].appID) - require.EqualValues(t, 0, brs[2].appID) + hr := parseHoldingRef("12") + require.EqualValues(t, 12, hr.assetID) + require.Zero(t, hr.address) - tbrs := translateBoxRefs(brs, []uint64{55, 77}) - require.EqualValues(t, 2, tbrs[0].Index) - require.EqualValues(t, 1, tbrs[1].Index) - require.EqualValues(t, 0, tbrs[2].Index) + hr = parseHoldingRef("1232,junk") + require.EqualValues(t, 1232, hr.assetID) + require.Equal(t, "junk", hr.address) +} + +func TestParseLocalRef(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + lr := parseLocalRef("12") + assert.EqualValues(t, 12, lr.appID) + assert.Zero(t, lr.address) - require.Panics(t, func() { translateBoxRefs(stringsToBoxRefs([]string{"addr:88"}), nil) }) - translateBoxRefs(stringsToBoxRefs([]string{"addr:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"}), nil) - // if we're here, that didn't panic/exit + lr = parseLocalRef("1232,junk") + assert.EqualValues(t, 1232, lr.appID) + assert.Equal(t, "junk", lr.address) - tbrs = translateBoxRefs(brs, []uint64{77, 55}) - require.EqualValues(t, 1, tbrs[0].Index) - require.EqualValues(t, 2, tbrs[1].Index) - require.EqualValues(t, 0, tbrs[2].Index) + lr = parseLocalRef("0,junk") + assert.Zero(t, lr.appID) + assert.Equal(t, "junk", lr.address) - require.Panics(t, func() { translateBoxRefs(brs, []uint64{55, 78}) }) - require.Panics(t, func() { translateBoxRefs(brs, []uint64{51, 77}) }) + lr = parseLocalRef("stuff") + assert.Zero(t, lr.appID) + assert.Equal(t, "stuff", lr.address) } func TestBytesToAppCallBytes(t *testing.T) { diff --git a/cmd/goal/interact.go b/cmd/goal/interact.go index 4b3a93c314..de907a5478 100644 --- a/cmd/goal/interact.go +++ b/cmd/goal/interact.go @@ -26,6 +26,7 @@ import ( "os" "strconv" "strings" + "time" "github.com/spf13/cobra" @@ -36,6 +37,8 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" + "github.com/algorand/go-algorand/libgoal" + "github.com/algorand/go-algorand/util" ) var ( @@ -456,6 +459,7 @@ func parseAppHeader() (header appInteractHeader) { if err != nil { reportErrorf("Could not open app header file %s: %v", appHdr, err) } + defer f.Close() dec := json.NewDecoder(f) err = dec.Decode(&header) @@ -479,6 +483,9 @@ var appExecuteCmd = &cobra.Command{ Short: "Execute a procedure on an application", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { + reportWarnf("app interact is deprecated and will be removed soon. Please speak up if the feature matters to you.") + time.Sleep(5 * time.Second) + dataDir := datadir.EnsureSingleDataDir() client := ensureFullClient(dataDir) @@ -561,9 +568,12 @@ var appExecuteCmd = &cobra.Command{ proc.OnCompletion = "NoOp" } onCompletion := mustParseOnCompletion(proc.OnCompletion) - appAccounts := inputs.Accounts - foreignApps := inputs.ForeignApps - foreignAssets := inputs.ForeignAssets + refs := libgoal.RefBundle{ + Accounts: util.Map(inputs.Accounts, cliAddress), + Apps: util.Map(inputs.ForeignApps, func(idx uint64) basics.AppIndex { return basics.AppIndex(idx) }), + Assets: util.Map(inputs.ForeignAssets, func(idx uint64) basics.AssetIndex { return basics.AssetIndex(idx) }), + // goal app interact is old and doesn't know about holdings, locals, boxes. + } appArgs := make([][]byte, len(inputs.Args)) for i, arg := range inputs.Args { @@ -588,7 +598,7 @@ var appExecuteCmd = &cobra.Command{ localSchema = header.Query.Local.ToStateSchema() globalSchema = header.Query.Global.ToStateSchema() } - tx, err := client.MakeUnsignedApplicationCallTx(appIdx, appArgs, appAccounts, foreignApps, foreignAssets, nil, onCompletion, approvalProg, clearProg, globalSchema, localSchema, 0, rejectVersion) + tx, err := client.MakeUnsignedApplicationCallTx(appIdx, appArgs, refs, onCompletion, approvalProg, clearProg, globalSchema, localSchema, 0, rejectVersion) if err != nil { reportErrorf("Cannot create application txn: %v", err) } @@ -634,7 +644,7 @@ var appExecuteCmd = &cobra.Command{ if !noWaitAfterSend { txn, err := waitForCommit(client, txid, lv) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } if txn.ApplicationIndex != nil && *txn.ApplicationIndex != 0 { reportInfof("Created app with app index %d", *txn.ApplicationIndex) @@ -644,7 +654,7 @@ var appExecuteCmd = &cobra.Command{ // Broadcast or write transaction to file err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) if err != nil { - reportErrorf(err.Error()) + reportErrorln(err) } } }, @@ -655,6 +665,9 @@ var appQueryCmd = &cobra.Command{ Short: "Query local or global state from an application", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { + reportWarnf("app interact is deprecated and will be removed soon. Please speak up if the feature matters to you.") + time.Sleep(5 * time.Second) + dataDir := datadir.EnsureSingleDataDir() client := ensureFullClient(dataDir) diff --git a/config/consensus.go b/config/consensus.go index 61f71e8de9..3ce6c9bc8a 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -278,8 +278,9 @@ type ConsensusParams struct { // be read in the transaction MaxAppTxnForeignAssets int - // maximum number of "foreign references" (accounts, asa, app, boxes) - // that can be attached to a single app call. + // maximum number of "foreign references" (accounts, asa, app, boxes) that + // can be attached to a single app call. Modern transactions can use + // MaxAccess references in txn.Access to access more. MaxAppTotalTxnReferences int // maximum cost of application approval program or clear state program @@ -351,6 +352,9 @@ type ConsensusParams struct { // Number of box references allowed MaxAppBoxReferences int + // Number of references allowed in txn.Access + MaxAppAccess int + // Amount added to a txgroup's box I/O budget per box ref supplied. // For reads: the sum of the sizes of all boxes in the group must be less than I/O budget // For writes: the sum of the sizes of all boxes created or written must be less than I/O budget @@ -1487,6 +1491,11 @@ func initConsensusProtocols() { vFuture.LogicSigVersion = 12 // When moving this to a release, put a new higher LogicSigVersion here vFuture.EnableAppVersioning = true // if not promoted when v12 goes into effect, update logic/field.go + // txn.Access work + vFuture.MaxAppTxnAccounts = 8 // Accounts are no worse than others, they should be the same + vFuture.MaxAppAccess = 16 // Twice as many, though cross products are explicit + vFuture.BytesPerBoxReference = 2048 // Count is more important that bytes, loosen up + Consensus[protocol.ConsensusFuture] = vFuture // vAlphaX versions are an separate series of consensus parameters and versions for alphanet diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index b6d702d008..24d9ceefeb 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -60,6 +60,7 @@ import ( "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/rpcs" "github.com/algorand/go-algorand/stateproof" + "github.com/algorand/go-algorand/util" ) // MaxTealSourceBytes sets a size limit for TEAL source programs for requests @@ -795,7 +796,7 @@ func (v2 *Handlers) GetBlockTxids(ctx echo.Context, round uint64) error { func NewAppCallLogs(txid string, logs []string, appIndex uint64) model.AppCallLogs { return model.AppCallLogs{ TxId: txid, - Logs: convertSlice(logs, func(s string) []byte { return []byte(s) }), + Logs: util.Map(logs, func(s string) []byte { return []byte(s) }), ApplicationIndex: appIndex, } } diff --git a/daemon/algod/api/server/v2/utils.go b/daemon/algod/api/server/v2/utils.go index a560872a2e..d324e7c378 100644 --- a/daemon/algod/api/server/v2/utils.go +++ b/daemon/algod/api/server/v2/utils.go @@ -40,6 +40,7 @@ import ( "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/node" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/util" ) // returnError logs an internal message while returning the encoded response. @@ -77,14 +78,6 @@ func notImplemented(ctx echo.Context, internal error, external string, log loggi return returnError(ctx, http.StatusNotImplemented, internal, external, log) } -func convertSlice[X any, Y any](input []X, fn func(X) Y) []Y { - output := make([]Y, len(input)) - for i := range input { - output[i] = fn(input[i]) - } - return output -} - func convertMap[X comparable, Y, Z any](input map[X]Y, fn func(X, Y) Z) []Z { output := make([]Z, len(input)) counter := 0 @@ -96,11 +89,11 @@ func convertMap[X comparable, Y, Z any](input map[X]Y, fn func(X, Y) Z) []Z { } func uint64Slice[T ~uint64](s []T) []uint64 { - return convertSlice(s, func(t T) uint64 { return uint64(t) }) + return util.Map(s, func(t T) uint64 { return uint64(t) }) } func stringSlice[T fmt.Stringer](s []T) []string { - return convertSlice(s, func(t T) string { return t.String() }) + return util.Map(s, func(t T) string { return t.String() }) } func sliceOrNil[T any](s []T) *[]T { @@ -444,11 +437,11 @@ func convertApplicationStateChange(stateChange simulation.StateOperation) model. func convertOpcodeTraceUnit(opcodeTraceUnit simulation.OpcodeTraceUnit) model.SimulationOpcodeTraceUnit { return model.SimulationOpcodeTraceUnit{ Pc: opcodeTraceUnit.PC, - SpawnedInners: sliceOrNil(convertSlice(opcodeTraceUnit.SpawnedInners, func(v int) uint64 { return uint64(v) })), - StackAdditions: sliceOrNil(convertSlice(opcodeTraceUnit.StackAdded, convertToAVMValue)), + SpawnedInners: sliceOrNil(util.Map(opcodeTraceUnit.SpawnedInners, func(v int) uint64 { return uint64(v) })), + StackAdditions: sliceOrNil(util.Map(opcodeTraceUnit.StackAdded, convertToAVMValue)), StackPopCount: omitEmpty(opcodeTraceUnit.StackPopCount), - ScratchChanges: sliceOrNil(convertSlice(opcodeTraceUnit.ScratchSlotChanges, convertScratchChange)), - StateChanges: sliceOrNil(convertSlice(opcodeTraceUnit.StateChanges, convertApplicationStateChange)), + ScratchChanges: sliceOrNil(util.Map(opcodeTraceUnit.ScratchSlotChanges, convertScratchChange)), + StateChanges: sliceOrNil(util.Map(opcodeTraceUnit.StateChanges, convertApplicationStateChange)), } } @@ -457,15 +450,15 @@ func convertTxnTrace(txnTrace *simulation.TransactionTrace) *model.SimulationTra return nil } return &model.SimulationTransactionExecTrace{ - ApprovalProgramTrace: sliceOrNil(convertSlice(txnTrace.ApprovalProgramTrace, convertOpcodeTraceUnit)), + ApprovalProgramTrace: sliceOrNil(util.Map(txnTrace.ApprovalProgramTrace, convertOpcodeTraceUnit)), ApprovalProgramHash: digestOrNil(txnTrace.ApprovalProgramHash), - ClearStateProgramTrace: sliceOrNil(convertSlice(txnTrace.ClearStateProgramTrace, convertOpcodeTraceUnit)), + ClearStateProgramTrace: sliceOrNil(util.Map(txnTrace.ClearStateProgramTrace, convertOpcodeTraceUnit)), ClearStateProgramHash: digestOrNil(txnTrace.ClearStateProgramHash), ClearStateRollback: omitEmpty(txnTrace.ClearStateRollback), ClearStateRollbackError: omitEmpty(txnTrace.ClearStateRollbackError), - LogicSigTrace: sliceOrNil(convertSlice(txnTrace.LogicSigTrace, convertOpcodeTraceUnit)), + LogicSigTrace: sliceOrNil(util.Map(txnTrace.LogicSigTrace, convertOpcodeTraceUnit)), LogicSigHash: digestOrNil(txnTrace.LogicSigHash), - InnerTrace: sliceOrNil(convertSlice(txnTrace.InnerTraces, + InnerTrace: sliceOrNil(util.Map(txnTrace.InnerTraces, func(trace simulation.TransactionTrace) model.SimulationTransactionExecTrace { return *convertTxnTrace(&trace) }), @@ -498,20 +491,20 @@ func convertUnnamedResourcesAccessed(resources *simulation.ResourceTracker) *mod Accounts: sliceOrNil(stringSlice(slices.Collect(maps.Keys(resources.Accounts)))), Assets: sliceOrNil(uint64Slice(slices.Collect(maps.Keys(resources.Assets)))), Apps: sliceOrNil(uint64Slice(slices.Collect(maps.Keys(resources.Apps)))), - Boxes: sliceOrNil(convertSlice(slices.Collect(maps.Keys(resources.Boxes)), func(box logic.BoxRef) model.BoxReference { + Boxes: sliceOrNil(util.Map(slices.Collect(maps.Keys(resources.Boxes)), func(box basics.BoxRef) model.BoxReference { return model.BoxReference{ App: uint64(box.App), Name: []byte(box.Name), } })), ExtraBoxRefs: omitEmpty(uint64(resources.NumEmptyBoxRefs)), - AssetHoldings: sliceOrNil(convertSlice(slices.Collect(maps.Keys(resources.AssetHoldings)), func(holding ledgercore.AccountAsset) model.AssetHoldingReference { + AssetHoldings: sliceOrNil(util.Map(slices.Collect(maps.Keys(resources.AssetHoldings)), func(holding ledgercore.AccountAsset) model.AssetHoldingReference { return model.AssetHoldingReference{ Account: holding.Address.String(), Asset: uint64(holding.Asset), } })), - AppLocals: sliceOrNil(convertSlice(slices.Collect(maps.Keys(resources.AppLocals)), func(local ledgercore.AccountApp) model.ApplicationLocalReference { + AppLocals: sliceOrNil(util.Map(slices.Collect(maps.Keys(resources.AppLocals)), func(local ledgercore.AccountApp) model.ApplicationLocalReference { return model.ApplicationLocalReference{ Account: local.Address.String(), App: uint64(local.App), @@ -603,7 +596,7 @@ func convertSimulationResult(result simulation.Result) PreEncodedSimulateRespons return PreEncodedSimulateResponse{ Version: result.Version, LastRound: uint64(result.LastRound), - TxnGroups: convertSlice(result.TxnGroups, convertTxnGroupResult), + TxnGroups: util.Map(result.TxnGroups, convertTxnGroupResult), EvalOverrides: evalOverrides, ExecTraceConfig: result.TraceConfig, InitialStates: convertSimulateInitialStates(result.InitialStates), diff --git a/data/basics/address.go b/data/basics/address.go index 8d6e187ffa..c34a800860 100644 --- a/data/basics/address.go +++ b/data/basics/address.go @@ -55,7 +55,7 @@ func UnmarshalChecksumAddress(address string) (Address, error) { decoded, err := base32Encoder.DecodeString(address) if err != nil { - return Address{}, fmt.Errorf("failed to decode address %s to base 32", address) + return Address{}, fmt.Errorf("failed to decode address %s from base 32", address) } var short Address if len(decoded) < len(short) { diff --git a/data/basics/testing/nearzero.go b/data/basics/testing/nearzero.go new file mode 100644 index 0000000000..4620e77c72 --- /dev/null +++ b/data/basics/testing/nearzero.go @@ -0,0 +1,187 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package testing + +import ( + "reflect" + "testing" + "time" +) + +// NearZeros takes a sample in order to retrieve its type. It returns a slice of +// the same type in which each element of the slice is the same type as the +// sample, but exactly one field (or sub-field) is set to a non-zero value. It +// returns one example for every sub-field. +func NearZeros(t *testing.T, sample any) []any { + typ := reflect.TypeOf(sample) + // If sample is a pointer, work with the underlying type. + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + if typ.Kind() != reflect.Struct { + t.Fatalf("NearZeros: sample must be a struct, got %s", typ.Kind()) + } + paths := CollectPaths(typ, []int{}) + var results []any + for _, path := range paths { + inst := makeInstanceWithNonZeroField(typ, path) + results = append(results, inst) + } + return results +} + +// CollectPaths walks over the struct type (recursively) and returns a slice of +// index paths. Each path points to exactly one (exported) sub-field. +func CollectPaths(typ reflect.Type, prefix []int) [][]int { + var paths [][]int + + switch typ.Kind() { + case reflect.Ptr, reflect.Slice, reflect.Array: + // Look through container to the element + return CollectPaths(typ.Elem(), prefix) + + case reflect.Map: + // Record as a leaf because we will just make a single entry in the map + paths = append(paths, prefix) + + case reflect.Struct: + // Special case: skip known value-type structs like time.Time + if typ == reflect.TypeOf(time.Time{}) { + return [][]int{prefix} + } + + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + if !field.IsExported() { + continue + } + newPath := append(append([]int(nil), prefix...), i) + subPaths := CollectPaths(field.Type, newPath) + + // If recursion yielded deeper paths, use them + if len(subPaths) > 0 { + paths = append(paths, subPaths...) + } else { + // Otherwise, it's a leaf field — include it + paths = append(paths, newPath) + } + } + + default: + // Primitive type — record this as a leaf + paths = append(paths, prefix) + } + + return paths +} + +// makeInstanceWithNonZeroField creates a new instance of type typ and sets exactly one +// field (identified by the fieldPath) to a non-zero value. +func makeInstanceWithNonZeroField(typ reflect.Type, fieldPath []int) any { + // Create a new instance (as a value, not pointer). + inst := reflect.New(typ).Elem() + setFieldToNonZero(inst, fieldPath) + return inst.Interface() +} + +// setFieldToNonZero navigates along the given path in the value v and sets that +// field to a non-zero value. The path is a slice of field indices. +func setFieldToNonZero(v reflect.Value, path []int) { + // Walk down the struct fields until the final field. + for i := 0; i < len(path)-1; i++ { + v = v.Field(path[i]) + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + case reflect.Slice: + if v.Len() == 0 { + slice := reflect.MakeSlice(v.Type(), 1, 1) + v.Set(slice) + } + v = v.Index(0) + case reflect.Array: + v = v.Index(0) // Already allocated, just descend + } + } + // Set the final field to an appropriate non-zero value. + field := v.Field(path[len(path)-1]) + if field.CanSet() { + field.Set(exampleValue(field.Type())) + } +} + +// exampleValue returns a non-zero value for a given type. +// For composite types (like arrays), it sets one element. +func exampleValue(t reflect.Type) reflect.Value { + switch t.Kind() { + case reflect.String: + return reflect.ValueOf("non-zero").Convert(t) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return reflect.ValueOf(1).Convert(t) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return reflect.ValueOf(1).Convert(t) + case reflect.Bool: + return reflect.ValueOf(true).Convert(t) + case reflect.Float32, reflect.Float64: + return reflect.ValueOf(1.23).Convert(t) + case reflect.Ptr: + // For pointers, allocate a new element and set it to non-zero. + elem := reflect.New(t.Elem()) + elem.Elem().Set(exampleValue(t.Elem())) + return elem + case reflect.Slice: + // Create a slice with one element. + slice := reflect.MakeSlice(t, 1, 1) + slice.Index(0).Set(exampleValue(t.Elem())) + return slice + case reflect.Map: + // Create a map with one key-value pair. + m := reflect.MakeMap(t) + // We put in an _empty_ value, because we want to ensure that a map with + // a value is considered non-zero. The fact that the value is zero is + // irrelevant. + e := reflect.New(t.Elem()).Elem() + m.SetMapIndex(exampleValue(t.Key()), e) + return m + case reflect.Array: + // Create an array and set the first element. + arr := reflect.New(t).Elem() + if t.Len() > 0 { + arr.Index(0).Set(exampleValue(t.Elem())) + } + return arr + case reflect.Struct: + // For structs, set the first exported field (if any). + s := reflect.New(t).Elem() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.IsExported() { + fv := s.Field(i) + if fv.CanSet() { + fv.Set(exampleValue(f.Type)) + break + } + } + } + return s + default: + panic("unable to make a non-zero: " + t.String()) + } +} diff --git a/data/basics/testing/nearzero_test.go b/data/basics/testing/nearzero_test.go new file mode 100644 index 0000000000..eb22932b5f --- /dev/null +++ b/data/basics/testing/nearzero_test.go @@ -0,0 +1,140 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package testing + +import ( + "testing" + + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/assert" +) + +func TestNearZeros(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + a := assert.New(t) + a.Len(NearZeros(t, struct{}{}), 0) + + type one struct { + B bool + } + a.Len(NearZeros(t, one{}), 1) + + type two struct { + I int + J int + } + a.Len(NearZeros(t, two{}), 2) + + // struct three has structs inside + type three struct { + One one + Two two + } + a.Len(NearZeros(t, three{}), 3) + + // struct four has a pointer and an array + type four struct { + One one + Two *two + Array [3]int + } + a.Len(NearZeros(t, four{}), 4) + + // Show that Two is allocated (twice, once for I, once for J) + count := 0 + for _, nz := range NearZeros(t, four{}) { + f := nz.(four) + if f.Two != nil { + count++ + } + } + a.Equal(2, count) + + // struct five has a slice + type five struct { + Two1 two + Two2 two + Slice []int + } + a.Len(NearZeros(t, five{}), 5) + + // Show that Slice is allocated once + count = 0 + for _, nz := range NearZeros(t, five{}) { + f := nz.(five) + if f.Slice != nil { + count++ + } + } + a.Equal(1, count) + + // struct size has a slice of struct + type six struct { + Slice1 []one + Slice2 []two + Slice3 []three + } + a.Len(NearZeros(t, six{}), 6) + + // Show that Slice2 is allocated twice, in order to fill Slice2[0].{I,J} + count = 0 + for _, nz := range NearZeros(t, six{}) { + f := nz.(six) + if f.Slice2 != nil { + count++ + a.True(f.Slice2[0].I > 0 || f.Slice2[0].J > 0) + } + } + a.Equal(2, count) +} + +func TestUnexported(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + type unexported struct { + a int + B int + } + assert.Len(t, NearZeros(t, unexported{}), 1) +} + +func TestMap(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + type mapint struct { + A map[int]int + } + assert.Len(t, NearZeros(t, mapint{}), 1) + assert.Zero(t, NearZeros(t, mapint{})[0].(mapint).A[1]) + + type mapstring struct { + A map[int]string + } + assert.Len(t, NearZeros(t, mapstring{}), 1) + assert.Zero(t, NearZeros(t, mapstring{})[0].(mapstring).A[1]) + + type mapstruct2 struct { + A map[int]struct{ A, B, C int } + } + assert.Len(t, NearZeros(t, mapstruct2{}), 1) + assert.Len(t, NearZeros(t, mapstruct2{})[0].(mapstruct2).A, 1) + assert.Zero(t, NearZeros(t, mapstruct2{})[0].(mapstruct2).A[1]) +} diff --git a/data/basics/userBalance.go b/data/basics/userBalance.go index 4a2da748a9..44717855c0 100644 --- a/data/basics/userBalance.go +++ b/data/basics/userBalance.go @@ -335,6 +335,27 @@ type AssetIndex uint64 // AppParams type AppIndex uint64 +// BoxRef is the "hydrated" form of a transactions.BoxRef - it has the actual +// app id, not an index +type BoxRef struct { + App AppIndex + Name string +} + +// HoldingRef is the "hydrated" form of a transactions.HoldingRef - it has the +// actual asset id and address, not indices +type HoldingRef struct { + Asset AssetIndex + Address Address +} + +// LocalRef is the "hydrated" form of a transactions.LocalRef - it has the +// actual app id and address, not indices +type LocalRef struct { + App AppIndex + Address Address +} + // CreatableIndex represents either an AssetIndex or AppIndex, which come from // the same namespace of indices as each other (both assets and apps are // "creatables") diff --git a/data/transactions/application.go b/data/transactions/application.go index 21793b1976..0b35a9b4a2 100644 --- a/data/transactions/application.go +++ b/data/transactions/application.go @@ -17,6 +17,7 @@ package transactions import ( + "errors" "fmt" "slices" @@ -54,6 +55,12 @@ const ( // can contain. Its value is verified against consensus parameters in // TestEncodedAppTxnAllocationBounds encodedMaxBoxes = 32 + + // encodedMaxAcces sets the allocation bound for the maximum number of + // references in Access that a transaction decoded off of the wire can + // contain. Its value is verified against consensus parameters in + // TestEncodedAppTxnAllocationBounds + encodedMaxAccess = 64 ) // OnCompletion is an enum representing some layer 1 side effect that an @@ -119,22 +126,29 @@ type ApplicationCallTxnFields struct { // the app or asset id). Accounts []basics.Address `codec:"apat,allocbound=encodedMaxAccounts"` + // ForeignAssets are asset IDs for assets whose AssetParams + // (and since v4, Holdings) may be read by the executing + // ApprovalProgram or ClearStateProgram. + ForeignAssets []basics.AssetIndex `codec:"apas,allocbound=encodedMaxForeignAssets"` + // ForeignApps are application IDs for applications besides // this one whose GlobalState (or Local, since v4) may be read // by the executing ApprovalProgram or ClearStateProgram. ForeignApps []basics.AppIndex `codec:"apfa,allocbound=encodedMaxForeignApps"` + // Access unifies `Accounts`, `ForeignApps`, `ForeignAssets`, and `Boxes` + // under a single list. It removes all implicitly available resources, so + // "cross-product" resources (holdings and locals) must be explicitly + // listed, as well as app accounts, even the app account of the called app! + // Transactions using Access may not use the other lists. + Access []ResourceRef `codec:"al,allocbound=encodedMaxAccess"` + // Boxes are the boxes that can be accessed by this transaction (and others // in the same group). The Index in the BoxRef is the slot of ForeignApps // that the name is associated with (shifted by 1, so 0 indicates "current - // app") + // app") If Access is used, the indexes refer to it. Boxes []BoxRef `codec:"apbx,allocbound=encodedMaxBoxes"` - // ForeignAssets are asset IDs for assets whose AssetParams - // (and since v4, Holdings) may be read by the executing - // ApprovalProgram or ClearStateProgram. - ForeignAssets []basics.AssetIndex `codec:"apas,allocbound=encodedMaxForeignAssets"` - // LocalStateSchema specifies the maximum number of each type that may // appear in the local key/value store of users who opt in to this // application. This field is only used during application creation @@ -174,12 +188,182 @@ type ApplicationCallTxnFields struct { // method below! } -// BoxRef names a box by the slot +// ResourceRef names a single resource +type ResourceRef struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + // Only one of these may be set + Address basics.Address `codec:"d"` + Asset basics.AssetIndex `codec:"s"` + App basics.AppIndex `codec:"p"` + Holding HoldingRef `codec:"h"` + Locals LocalsRef `codec:"l"` + Box BoxRef `codec:"b"` +} + +// Empty ResourceRefs are allowed, as they ask for a box quota bump. +func (rr ResourceRef) Empty() bool { + return rr.Address.IsZero() && rr.Asset == 0 && rr.App == 0 && + rr.Holding.Empty() && rr.Locals.Empty() && rr.Box.Empty() +} + +// wellFormed checks that a ResourceRef is a proper member of `access. `rr` is +// either empty a single kind of resource. Any internal indices point to proper +// locations inside `access`. +func (rr ResourceRef) wellFormed(access []ResourceRef, proto config.ConsensusParams) error { + // Count the number of non-empty fields + count := 0 + // The "basic" resources are inherently wellFormed + if !rr.Address.IsZero() { + count++ + } + if rr.Asset != 0 { + count++ + } + if rr.App != 0 { + count++ + } + // The references that have indices need to be checked + if !rr.Holding.Empty() { + if _, _, err := rr.Holding.Resolve(access, basics.Address{}); err != nil { + return err + } + count++ + } + if !rr.Locals.Empty() { + if _, _, err := rr.Locals.Resolve(access, basics.Address{}); err != nil { + return err + } + count++ + } + if !rr.Box.Empty() { + if proto.EnableBoxRefNameError && len(rr.Box.Name) > proto.MaxAppKeyLen { + return fmt.Errorf("tx.Access box Name too long, max len %d bytes", proto.MaxAppKeyLen) + } + if _, _, err := rr.Box.Resolve(access); err != nil { + return err + } + count++ + } + switch count { + case 0: + if !rr.Empty() { // If it's not one of those, it has to be empty + return fmt.Errorf("tx.Access with unknown content") + } + return nil + case 1: + return nil + default: + return fmt.Errorf("tx.Access element has fields from multiple types") + } +} + +// HoldingRef names a holding by referring to an Address and Asset that appear +// earlier in the Access list (0 is special cased) +type HoldingRef struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + Address uint64 `codec:"d"` // 0=Sender,n-1=index into the Access list, which must be an Address + Asset uint64 `codec:"s"` // n-1=index into the Access list, which must be an Asset +} + +// Empty does the obvious. An empty HoldingRef has no meaning, since we define +// hr.Asset to be a 1-based index for consistency with LocalRef (which does it +// because 0 means "this app") +func (hr HoldingRef) Empty() bool { + return hr == HoldingRef{} +} + +// Resolve looks up the referenced address and asset in the access list +func (hr HoldingRef) Resolve(access []ResourceRef, sender basics.Address) (basics.Address, basics.AssetIndex, error) { + address := sender + if hr.Address != 0 { + if hr.Address > uint64(len(access)) { // recall that Access is 1-based + return basics.Address{}, 0, fmt.Errorf("holding Address reference %d outside tx.Access", hr.Address) + } + address = access[hr.Address-1].Address + if address.IsZero() { + return basics.Address{}, 0, fmt.Errorf("holding Address reference %d is not an Address", hr.Address) + } + } + if hr.Asset == 0 || hr.Asset > uint64(len(access)) { // 1-based + return basics.Address{}, 0, fmt.Errorf("holding Asset reference %d outside tx.Access", hr.Asset) + } + asset := access[hr.Asset-1].Asset + if asset == 0 { + return basics.Address{}, 0, fmt.Errorf("holding Asset reference %d is not an Asset", hr.Asset) + } + return address, asset, nil +} + +// LocalsRef names a local state by referring to an Address and App that appear +// earlier in the Access list (0 is special cased) +type LocalsRef struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + Address uint64 `codec:"d"` // 0=Sender,n-1=index into the Access list, which must be an Address + App uint64 `codec:"p"` // 0=ApplicationID,n-1=index into the Access list, which must be an App +} + +// Empty does the obvious. An empty LocalsRef makes no sense, because it would +// mean "give access to the sender's locals for this app", which is implicit. +func (lr LocalsRef) Empty() bool { + return lr == LocalsRef{} +} + +// Resolve looks up the referenced address and app in the access list. 0 is +// returned if the App index is 0, meaning "current app". +func (lr LocalsRef) Resolve(access []ResourceRef, sender basics.Address) (basics.Address, basics.AppIndex, error) { + address := sender + if lr.Address != 0 { + if lr.Address > uint64(len(access)) { // recall that Access is 1-based + return basics.Address{}, 0, fmt.Errorf("locals Address reference %d outside tx.Access", lr.Address) + } + address = access[lr.Address-1].Address + if address.IsZero() { + return basics.Address{}, 0, fmt.Errorf("locals Address reference %d is not an Address", lr.Address) + } + } + if lr.App == 0 || lr.App > uint64(len(access)) { // 1-based + return basics.Address{}, 0, fmt.Errorf("locals App reference %d outside tx.Access", lr.App) + } + app := access[lr.App-1].App + if app == 0 { + return basics.Address{}, 0, fmt.Errorf("locals App reference %d is not an App", lr.App) + } + return address, app, nil +} + +// BoxRef names a box by the slot. In the Boxes field, `i` is an index into +// ForeignApps. As an entry in Access, `i` is a index into Access itself. type BoxRef struct { _struct struct{} `codec:",omitempty,omitemptyarray"` + Index uint64 `codec:"i"` + Name []byte `codec:"n,allocbound=config.MaxBytesKeyValueLen"` +} + +// Empty does the obvious. But the meaning is not obvious. An empty BoxRef just +// adds to the read/write quota of the transaction. In tx.Access, _any_ empty +// ResourceRef bumps the read/write quota. (We cannot distinguish the type when +// all are empty.) +func (br BoxRef) Empty() bool { + return br.Index == 0 && br.Name == nil +} - Index uint64 `codec:"i"` - Name []byte `codec:"n,allocbound=config.MaxBytesKeyValueLen"` +// Resolve looks up the referenced app and returns it with the name. 0 is +// returned if the App index is 0, meaning "current app". +func (br BoxRef) Resolve(access []ResourceRef) (basics.AppIndex, string, error) { + app := basics.AppIndex(0) + switch { + case br.Index == 0: + return 0, string(br.Name), nil + case br.Index <= uint64(len(access)): // 1-based + app = access[br.Index-1].App + if app == 0 { + return 0, "", fmt.Errorf("box Index reference %d is not an App", br.Index) + } + return app, string(br.Name), nil + default: + return app, "", fmt.Errorf("box Index %d outside tx.Access", br.Index) + } } // Empty indicates whether or not all the fields in the @@ -206,6 +390,9 @@ func (ac *ApplicationCallTxnFields) Empty() bool { if ac.Boxes != nil { return false } + if ac.Access != nil { + return false + } if ac.LocalStateSchema != (basics.StateSchema{}) { return false } @@ -235,7 +422,7 @@ func (ac ApplicationCallTxnFields) wellFormed(proto config.ConsensusParams) erro case NoOpOC, OptInOC, CloseOutOC, ClearStateOC, UpdateApplicationOC, DeleteApplicationOC: /* ok */ default: - return fmt.Errorf("invalid application OnCompletion") + return fmt.Errorf("invalid application OnCompletion (%d)", ac.OnCompletion) } if !proto.EnableAppVersioning && ac.RejectVersion > 0 { @@ -263,9 +450,11 @@ func (ac ApplicationCallTxnFields) wellFormed(proto config.ConsensusParams) erro effectiveEPP := ac.ExtraProgramPages // Schemas and ExtraProgramPages may only be set during application creation if ac.ApplicationID != 0 { - if ac.LocalStateSchema != (basics.StateSchema{}) || - ac.GlobalStateSchema != (basics.StateSchema{}) { - return fmt.Errorf("local and global state schemas are immutable") + if ac.GlobalStateSchema != (basics.StateSchema{}) { + return fmt.Errorf("tx.GlobalStateSchema is immutable") + } + if ac.LocalStateSchema != (basics.StateSchema{}) { + return fmt.Errorf("tx.LocalStateSchema is immutable") } if ac.ExtraProgramPages != 0 { return fmt.Errorf("tx.ExtraProgramPages is immutable") @@ -279,7 +468,8 @@ func (ac ApplicationCallTxnFields) wellFormed(proto config.ConsensusParams) erro // Limit total number of arguments if len(ac.ApplicationArgs) > proto.MaxAppArgs { - return fmt.Errorf("too many application args, max %d", proto.MaxAppArgs) + return fmt.Errorf("tx.ApplicationArgs has too many arguments. %d > %d", + len(ac.ApplicationArgs), proto.MaxAppArgs) } // Sum up argument lengths @@ -290,30 +480,51 @@ func (ac ApplicationCallTxnFields) wellFormed(proto config.ConsensusParams) erro // Limit total length of all arguments if argSum > uint64(proto.MaxAppTotalArgLen) { - return fmt.Errorf("application args total length too long, max len %d bytes", proto.MaxAppTotalArgLen) - } - - // Limit number of accounts referred to in a single ApplicationCall - if len(ac.Accounts) > proto.MaxAppTxnAccounts { - return fmt.Errorf("tx.Accounts too long, max number of accounts is %d", proto.MaxAppTxnAccounts) + return fmt.Errorf("tx.ApplicationArgs total length is too long. %d > %d", + argSum, proto.MaxAppTotalArgLen) } - // Limit number of other app global states referred to - if len(ac.ForeignApps) > proto.MaxAppTxnForeignApps { - return fmt.Errorf("tx.ForeignApps too long, max number of foreign apps is %d", proto.MaxAppTxnForeignApps) - } - - if len(ac.ForeignAssets) > proto.MaxAppTxnForeignAssets { - return fmt.Errorf("tx.ForeignAssets too long, max number of foreign assets is %d", proto.MaxAppTxnForeignAssets) - } + if len(ac.Access) > 0 { + if len(ac.Access) > proto.MaxAppAccess { + return fmt.Errorf("tx.Access too long, max number of references is %d", proto.MaxAppAccess) + } + // When ac.Access is used, no other references are allowed + if len(ac.Accounts) > 0 { + return errors.New("tx.Accounts can't be used when tx.Access is used") + } + if len(ac.ForeignApps) > 0 { + return errors.New("tx.ForeignApps can't be used when tx.Access is used") + } + if len(ac.ForeignAssets) > 0 { + return errors.New("tx.ForeignAssets can't be used when tx.Access is used") + } + if len(ac.Boxes) > 0 { + return errors.New("tx.Boxes can't be used when tx.Access is used") + } - if len(ac.Boxes) > proto.MaxAppBoxReferences { - return fmt.Errorf("tx.Boxes too long, max number of box references is %d", proto.MaxAppBoxReferences) - } + for _, rr := range ac.Access { + if err := rr.wellFormed(ac.Access, proto); err != nil { + return err + } + } + } else { + if len(ac.Accounts) > proto.MaxAppTxnAccounts { + return fmt.Errorf("tx.Accounts too long, max number of accounts is %d", proto.MaxAppTxnAccounts) + } + if len(ac.ForeignApps) > proto.MaxAppTxnForeignApps { + return fmt.Errorf("tx.ForeignApps too long, max number of foreign apps is %d", proto.MaxAppTxnForeignApps) + } + if len(ac.ForeignAssets) > proto.MaxAppTxnForeignAssets { + return fmt.Errorf("tx.ForeignAssets too long, max number of foreign assets is %d", proto.MaxAppTxnForeignAssets) + } + if len(ac.Boxes) > proto.MaxAppBoxReferences { + return fmt.Errorf("tx.Boxes too long, max number of box references is %d", proto.MaxAppBoxReferences) + } - // Limit the sum of all types of references that bring in account records - if len(ac.Accounts)+len(ac.ForeignApps)+len(ac.ForeignAssets)+len(ac.Boxes) > proto.MaxAppTotalTxnReferences { - return fmt.Errorf("tx references exceed MaxAppTotalTxnReferences = %d", proto.MaxAppTotalTxnReferences) + // Limit the sum of all types of references that bring in resource records + if len(ac.Accounts)+len(ac.ForeignApps)+len(ac.ForeignAssets)+len(ac.Boxes) > proto.MaxAppTotalTxnReferences { + return fmt.Errorf("tx references exceed MaxAppTotalTxnReferences = %d", proto.MaxAppTotalTxnReferences) + } } if ac.ExtraProgramPages > uint32(proto.MaxExtraAppProgramPages) { @@ -344,11 +555,13 @@ func (ac ApplicationCallTxnFields) wellFormed(proto config.ConsensusParams) erro } if ac.LocalStateSchema.NumEntries() > proto.MaxLocalSchemaEntries { - return fmt.Errorf("tx.LocalStateSchema too large, max number of keys is %d", proto.MaxLocalSchemaEntries) + return fmt.Errorf("tx.LocalStateSchema is too large. %d > %d", + ac.LocalStateSchema.NumEntries(), proto.MaxLocalSchemaEntries) } if ac.GlobalStateSchema.NumEntries() > proto.MaxGlobalSchemaEntries { - return fmt.Errorf("tx.GlobalStateSchema too large, max number of keys is %d", proto.MaxGlobalSchemaEntries) + return fmt.Errorf("tx.GlobalStateSchema is too large. %d > %d", + ac.GlobalStateSchema.NumEntries(), proto.MaxGlobalSchemaEntries) } return nil @@ -356,7 +569,7 @@ func (ac ApplicationCallTxnFields) wellFormed(proto config.ConsensusParams) erro // AddressByIndex converts an integer index into an address associated with the // transaction. Index 0 corresponds to the transaction sender, and an index > 0 -// corresponds to an offset into txn.Accounts. Returns an error if the index is +// corresponds to an offset into txn.Accounts or txn.Access. Returns an error if the index is // not valid. func (ac *ApplicationCallTxnFields) AddressByIndex(accountIdx uint64, sender basics.Address) (basics.Address, error) { // Index 0 always corresponds to the sender @@ -364,10 +577,24 @@ func (ac *ApplicationCallTxnFields) AddressByIndex(accountIdx uint64, sender bas return sender, nil } + if ac.Access != nil { + // An index > 0 corresponds to an offset into txn.Access. Check to + // make sure the index is valid. + if accountIdx > uint64(len(ac.Access)) { + return basics.Address{}, fmt.Errorf("invalid Account reference %d exceeds length of tx.Access %d", accountIdx, len(ac.Access)) + } + // And now check that the index refers to an Address + rr := ac.Access[accountIdx-1] + if rr.Address.IsZero() { + return basics.Address{}, fmt.Errorf("address reference %d is not an Address in tx.Access", accountIdx) + } + return rr.Address, nil + } + // An index > 0 corresponds to an offset into txn.Accounts. Check to // make sure the index is valid. if accountIdx > uint64(len(ac.Accounts)) { - return basics.Address{}, fmt.Errorf("invalid Account reference %d", accountIdx) + return basics.Address{}, fmt.Errorf("invalid Account reference %d exceeds length of tx.Accounts %d", accountIdx, len(ac.Accounts)) } // accountIdx must be in [1, len(ac.Accounts)] @@ -375,18 +602,23 @@ func (ac *ApplicationCallTxnFields) AddressByIndex(accountIdx uint64, sender bas } // IndexByAddress converts an address into an integer offset into [txn.Sender, -// txn.Accounts[0], ...], returning the index at the first match. It returns -// an error if there is no such match. +// XXX[0], ...], returning the index at the first match. XXX is tx.Access or +// tx.Accounts. It returns an error if there is no such match. func (ac *ApplicationCallTxnFields) IndexByAddress(target basics.Address, sender basics.Address) (uint64, error) { // Index 0 always corresponds to the sender if target == sender { return 0, nil } + // Try ac.Access first. Remember only one of Access or Accounts can be set. + if idx := slices.IndexFunc(ac.Access, func(rr ResourceRef) bool { return rr.Address == target }); idx != -1 { + return uint64(idx) + 1, nil + } + // Otherwise we index into ac.Accounts if idx := slices.Index(ac.Accounts, target); idx != -1 { return uint64(idx) + 1, nil } - return 0, fmt.Errorf("invalid Account reference %s", target) + return 0, fmt.Errorf("invalid Account reference %s does not appear in resource array", target) } diff --git a/data/transactions/application_test.go b/data/transactions/application_test.go index 3f7f7e8d17..427c3fa620 100644 --- a/data/transactions/application_test.go +++ b/data/transactions/application_test.go @@ -19,6 +19,7 @@ package transactions import ( "fmt" "reflect" + "slices" "strings" "testing" @@ -27,31 +28,52 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/data/basics" + basics_testing "github.com/algorand/go-algorand/data/basics/testing" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" ) func TestApplicationCallFieldsNotChanged(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() af := ApplicationCallTxnFields{} s := reflect.ValueOf(&af).Elem() if s.NumField() != 14 { t.Errorf("You added or removed a field from transactions.ApplicationCallTxnFields. " + - "Please ensure you have updated the Empty() method and then " + + "Please ensure you have updated ApplicationCallTxnFields.Empty() and then " + "fix this test") } } +func TestResourceRefEmpty(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + assert.True(t, ResourceRef{}.Empty()) + for _, nz := range basics_testing.NearZeros(t, ResourceRef{}) { + rr := nz.(ResourceRef) + assert.False(t, rr.Empty(), rr) + } +} + func TestApplicationCallFieldsEmpty(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() - a := require.New(t) + a := assert.New(t) ac := ApplicationCallTxnFields{} a.True(ac.Empty()) + for _, nz := range basics_testing.NearZeros(t, ac) { + fields := nz.(ApplicationCallTxnFields) + a.False(fields.Empty(), fields) + } + + // Everything below here ought to be redundant after the NearZeros loop. + ac.ApplicationID = 1 a.False(ac.Empty()) @@ -111,6 +133,7 @@ func TestApplicationCallFieldsEmpty(t *testing.T) { func TestEncodedAppTxnAllocationBounds(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() // ensure that all the supported protocols have value limits less or // equal to their corresponding codec allocbounds @@ -130,6 +153,220 @@ func TestEncodedAppTxnAllocationBounds(t *testing.T) { if proto.MaxAppBoxReferences > encodedMaxBoxes { require.Failf(t, "proto.MaxAppBoxReferences > encodedMaxBoxes", "protocol version = %s", protoVer) } + if proto.MaxAppAccess > encodedMaxAccess { + require.Failf(t, "proto.MaxAppAccess > encodedMaxAccess", "protocol version = %s", protoVer) + } + } +} + +func TestAppCallAccessWellFormed(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + preAccessCV := protocol.ConsensusV40 + addr1, err := basics.UnmarshalChecksumAddress("NDQCJNNY5WWWFLP4GFZ7MEF2QJSMZYK6OWIV2AQ7OMAVLEFCGGRHFPKJJA") + require.NoError(t, err) + + cases := []struct { + expectedError string + cv protocol.ConsensusVersion // defaults to future if not set + ac ApplicationCallTxnFields + }{ + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: slices.Repeat([]ResourceRef{{}}, 16), + }, + }, + { + expectedError: "tx.Access too long, max number of references is 0", + cv: preAccessCV, + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{}}, + }, + }, + { + expectedError: "tx.Access too long, max number of references is 16", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: slices.Repeat([]ResourceRef{{}}, 17), + }, + }, + { + expectedError: "tx.Accounts can't be used when tx.Access is used", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{}}, + Accounts: []basics.Address{addr1}, + }, + }, + { + expectedError: "tx.ForeignAssets can't be used when tx.Access is used", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{}}, + ForeignAssets: []basics.AssetIndex{2}, + }, + }, + { + expectedError: "tx.ForeignApps can't be used when tx.Access is used", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{}}, + ForeignApps: []basics.AppIndex{3}, + }, + }, + { + expectedError: "tx.Boxes can't be used when tx.Access is used", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{}}, + Boxes: []BoxRef{{Index: 0}}, + }, + }, + + // Exercise holdings + { + expectedError: "holding Asset reference 2 outside tx.Access", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{Holding: HoldingRef{Asset: 2}}}, + }, + }, + { + expectedError: "holding Asset reference 1 is not an Asset", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{Holding: HoldingRef{Asset: 1}}}, + }, + }, + { + expectedError: "holding Asset reference 0 outside tx.Access", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{ + {Address: basics.Address{0xaa}}, + {Holding: HoldingRef{Address: 1}}, + }, + }, + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{ + {Address: basics.Address{0xaa}}, + {Asset: 99}, + {Holding: HoldingRef{Address: 1, Asset: 2}}, + }, + }, + }, + + // Exercise locals + { + expectedError: "locals App reference 2 outside tx.Access", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{Locals: LocalsRef{App: 2}}}, + }, + }, + { + expectedError: "locals App reference 1 is not an App", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{Locals: LocalsRef{App: 1}}}, + }, + }, + { + expectedError: "locals App reference 0 outside tx.Access", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{ + {Address: basics.Address{0xaa}}, + {Locals: LocalsRef{Address: 1}}, + }, + }, + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{ + {Address: basics.Address{0xaa}}, + {App: 99}, + {Locals: LocalsRef{Address: 1, App: 2}}, + }, + }, + }, + + // Exercise boxes + { + expectedError: "box Index 2 outside tx.Access", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{Box: BoxRef{Index: 2}}}, + }, + }, + { + expectedError: "box Index reference 1 is not an App", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{Box: BoxRef{Index: 1}}}, + }, + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{App: 20}, {Box: BoxRef{Index: 1}}}, + }, + }, + { + expectedError: "tx.Access box Name too long, max len 64 bytes", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{Box: BoxRef{Name: make([]byte, 65)}}}, + }, + }, + + // Multiple uses in ResourceRef + { + expectedError: "tx.Access element has fields from multiple types", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{Address: basics.Address{0x01}, Box: BoxRef{Name: []byte("a")}}}, + }, + }, + { + expectedError: "tx.Access element has fields from multiple types", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{App: 10, Locals: LocalsRef{App: 1}}}, + }, + }, + { + expectedError: "tx.Access element has fields from multiple types", + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Access: []ResourceRef{{Asset: 10, Holding: HoldingRef{Asset: 1}}}, + }, + }, + } + for i, tc := range cases { + name := fmt.Sprintf("i=%d", i) + if tc.expectedError != "" { + name = tc.expectedError + } + t.Run(name, func(t *testing.T) { + cv := tc.cv + if cv == "" { + cv = protocol.ConsensusFuture + } + err := tc.ac.wellFormed(config.Consensus[cv]) + if tc.expectedError != "" { + require.ErrorContains(t, err, tc.expectedError) + } else { + require.NoError(t, err) + } + }) } } @@ -210,126 +447,86 @@ func TestAppCallVersioningWellFormed(t *testing.T) { func TestAppCallCreateWellFormed(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() - curProto := config.Consensus[protocol.ConsensusCurrentVersion] - futureProto := config.Consensus[protocol.ConsensusFuture] - addr1, err := basics.UnmarshalChecksumAddress("NDQCJNNY5WWWFLP4GFZ7MEF2QJSMZYK6OWIV2AQ7OMAVLEFCGGRHFPKJJA") - require.NoError(t, err) v5 := []byte{0x05} v6 := []byte{0x06} - - usecases := []struct { - tx Transaction - proto config.ConsensusParams + cases := []struct { expectedError string + cv protocol.ConsensusVersion // defaults to future if not set + ac ApplicationCallTxnFields }{ { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: Header{ - Sender: addr1, - Fee: basics.MicroAlgos{Raw: 1000}, - LastValid: 105, - FirstValid: 100, - }, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, - ApprovalProgram: v5, - ClearStateProgram: v5, - ApplicationArgs: [][]byte{ - []byte("write"), - }, + ac: ApplicationCallTxnFields{ + ApprovalProgram: v5, + ClearStateProgram: v5, + ApplicationArgs: [][]byte{ + []byte("write"), }, }, - proto: curProto, }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: Header{ - Sender: addr1, - Fee: basics.MicroAlgos{Raw: 1000}, - LastValid: 105, - FirstValid: 100, - }, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, - ApprovalProgram: v5, - ClearStateProgram: v5, - ApplicationArgs: [][]byte{ - []byte("write"), - }, - ExtraProgramPages: 0, + ac: ApplicationCallTxnFields{ + ApprovalProgram: v5, + ClearStateProgram: v5, + ApplicationArgs: [][]byte{ + []byte("write"), }, + ExtraProgramPages: 0, }, - proto: curProto, }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: Header{ - Sender: addr1, - Fee: basics.MicroAlgos{Raw: 1000}, - LastValid: 105, - FirstValid: 100, - }, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, - ApprovalProgram: v5, - ClearStateProgram: v5, - ApplicationArgs: [][]byte{ - []byte("write"), - }, - ExtraProgramPages: 3, + ac: ApplicationCallTxnFields{ + ApprovalProgram: v5, + ClearStateProgram: v5, + ApplicationArgs: [][]byte{ + []byte("write"), }, + ExtraProgramPages: 3, }, - proto: futureProto, }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: Header{ - Sender: addr1, - Fee: basics.MicroAlgos{Raw: 1000}, - LastValid: 105, - FirstValid: 100, - }, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, - ApprovalProgram: v5, - ClearStateProgram: v5, - ApplicationArgs: [][]byte{ - []byte("write"), - }, - ExtraProgramPages: 0, + ac: ApplicationCallTxnFields{ + ApprovalProgram: v5, + ClearStateProgram: v5, + ApplicationArgs: [][]byte{ + []byte("write"), }, }, - proto: futureProto, }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: Header{ - Sender: addr1, - Fee: basics.MicroAlgos{Raw: 1000}, - LastValid: 105, - FirstValid: 100, - }, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApprovalProgram: v5, - ClearStateProgram: v6, - }, + expectedError: "program version mismatch", + ac: ApplicationCallTxnFields{ + ApprovalProgram: v5, + ClearStateProgram: v6, }, - proto: futureProto, - expectedError: "mismatch", }, } - for i, usecase := range usecases { - t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) { - err := usecase.tx.WellFormed(SpecialAddresses{}, usecase.proto) - if usecase.expectedError != "" { - require.Error(t, err) - require.Contains(t, err.Error(), usecase.expectedError) + for i, tc := range cases { + name := fmt.Sprintf("i=%d", i) + if tc.expectedError != "" { + name = tc.expectedError + } + t.Run(name, func(t *testing.T) { + cv := tc.cv + if cv == "" { + cv = protocol.ConsensusFuture + } + err := tc.ac.wellFormed(config.Consensus[cv]) + if tc.expectedError != "" { + require.ErrorContains(t, err, tc.expectedError) + } else { + require.NoError(t, err) + } + // test the same thing for update, unless test has epp, which is illegal in update + if tc.ac.ExtraProgramPages != 0 { + return + } + tc.ac.OnCompletion = UpdateApplicationOC + tc.ac.ApplicationID = 1 + err = tc.ac.wellFormed(config.Consensus[cv]) + if tc.expectedError != "" { + require.ErrorContains(t, err, tc.expectedError) } else { require.NoError(t, err) } @@ -339,300 +536,329 @@ func TestAppCallCreateWellFormed(t *testing.T) { func TestWellFormedErrors(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() + + cv27 := protocol.ConsensusV27 + cv28 := protocol.ConsensusV28 + cv32 := protocol.ConsensusV32 + cv36 := protocol.ConsensusV36 - curProto := config.Consensus[protocol.ConsensusCurrentVersion] - futureProto := config.Consensus[protocol.ConsensusFuture] - protoV27 := config.Consensus[protocol.ConsensusV27] - protoV28 := config.Consensus[protocol.ConsensusV28] - protoV32 := config.Consensus[protocol.ConsensusV32] - protoV36 := config.Consensus[protocol.ConsensusV36] - addr1, err := basics.UnmarshalChecksumAddress("NDQCJNNY5WWWFLP4GFZ7MEF2QJSMZYK6OWIV2AQ7OMAVLEFCGGRHFPKJJA") - require.NoError(t, err) v5 := []byte{0x05} - okHeader := Header{ - Sender: addr1, - Fee: basics.MicroAlgos{Raw: 1000}, - LastValid: 105, - FirstValid: 100, - } - usecases := []struct { - tx Transaction - proto config.ConsensusParams - expectedError error + cases := []struct { + ac ApplicationCallTxnFields + cv protocol.ConsensusVersion + expectedError string }{ { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, // creation - ApprovalProgram: v5, - ClearStateProgram: v5, - ApplicationArgs: [][]byte{ - []byte("write"), - }, - ExtraProgramPages: 1, - }, + expectedError: "invalid application OnCompletion (6)", + ac: ApplicationCallTxnFields{ + ApplicationID: 99, + OnCompletion: DeleteApplicationOC + 1, }, - proto: protoV27, - expectedError: fmt.Errorf("tx.ExtraProgramPages exceeds MaxExtraAppProgramPages = %d", protoV27.MaxExtraAppProgramPages), }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, // creation - ApprovalProgram: []byte(strings.Repeat("X", 1025)), - ClearStateProgram: []byte("Xjunk"), - }, + expectedError: "programs may only be specified during application creation or update", + ac: ApplicationCallTxnFields{ + ApplicationID: 99, + ApprovalProgram: v5, + ClearStateProgram: v5, + OnCompletion: NoOpOC, }, - proto: protoV27, - expectedError: fmt.Errorf("approval program too long. max len 1024 bytes"), }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, // creation - ApprovalProgram: []byte(strings.Repeat("X", 1025)), - ClearStateProgram: []byte("Xjunk"), - }, + ac: ApplicationCallTxnFields{ + ApplicationID: 99, + ApprovalProgram: v5, + ClearStateProgram: v5, + OnCompletion: UpdateApplicationOC, }, - proto: futureProto, }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, // creation - ApprovalProgram: []byte(strings.Repeat("X", 1025)), - ClearStateProgram: []byte(strings.Repeat("X", 1025)), + ac: ApplicationCallTxnFields{ + ApplicationID: 0, // creation + ApprovalProgram: v5, + ClearStateProgram: v5, + ApplicationArgs: [][]byte{ + []byte("write"), }, + ExtraProgramPages: 1, }, - proto: futureProto, - expectedError: fmt.Errorf("app programs too long. max total len 2048 bytes"), + cv: cv27, + expectedError: "tx.ExtraProgramPages exceeds MaxExtraAppProgramPages = 0", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, // creation - ApprovalProgram: []byte(strings.Repeat("X", 1025)), - ClearStateProgram: []byte(strings.Repeat("X", 1025)), - ExtraProgramPages: 1, - }, + ac: ApplicationCallTxnFields{ + ApplicationID: 0, // creation + ApprovalProgram: []byte(strings.Repeat("X", 1025)), + ClearStateProgram: []byte("Xjunk"), }, - proto: futureProto, + cv: cv27, + expectedError: "approval program too long. max len 1024 bytes", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - ApplicationArgs: [][]byte{ - []byte("write"), - }, - ExtraProgramPages: 1, - }, + ac: ApplicationCallTxnFields{ + ApplicationID: 0, // creation + ApprovalProgram: []byte(strings.Repeat("X", 1025)), + ClearStateProgram: []byte("Xjunk"), }, - proto: futureProto, - expectedError: fmt.Errorf("tx.ExtraProgramPages is immutable"), - }, - { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 0, - ApprovalProgram: v5, - ClearStateProgram: v5, - ApplicationArgs: [][]byte{ - []byte("write"), - }, - ExtraProgramPages: 4, - }, + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 0, // creation + ApprovalProgram: []byte(strings.Repeat("X", 1025)), + ClearStateProgram: []byte(strings.Repeat("X", 1025)), }, - proto: futureProto, - expectedError: fmt.Errorf("tx.ExtraProgramPages exceeds MaxExtraAppProgramPages = %d", futureProto.MaxExtraAppProgramPages), + expectedError: "app programs too long. max total len 2048 bytes", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - ForeignApps: []basics.AppIndex{10, 11}, - }, + ac: ApplicationCallTxnFields{ + ApplicationID: 0, // creation + ApprovalProgram: []byte(strings.Repeat("X", 1025)), + ClearStateProgram: []byte(strings.Repeat("X", 1025)), + ExtraProgramPages: 1, }, - proto: protoV27, }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - ForeignApps: []basics.AppIndex{10, 11, 12}, + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApplicationArgs: [][]byte{ + []byte("write"), }, + GlobalStateSchema: basics.StateSchema{NumByteSlice: 1}, }, - proto: protoV27, - expectedError: fmt.Errorf("tx.ForeignApps too long, max number of foreign apps is 2"), + expectedError: "tx.GlobalStateSchema is immutable", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - ForeignApps: []basics.AppIndex{10, 11, 12, 13, 14, 15, 16, 17}, + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApplicationArgs: [][]byte{ + []byte("write"), }, + LocalStateSchema: basics.StateSchema{NumUint: 1}, }, - proto: futureProto, + expectedError: "tx.LocalStateSchema is immutable", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - ForeignAssets: []basics.AssetIndex{14, 15, 16, 17, 18, 19, 20, 21, 22}, + ac: ApplicationCallTxnFields{ + ApplicationID: 0, + ApprovalProgram: v5, + ClearStateProgram: v5, + ApplicationArgs: [][]byte{ + []byte("write"), }, + GlobalStateSchema: basics.StateSchema{NumByteSlice: 30, NumUint: 35}, }, - proto: futureProto, - expectedError: fmt.Errorf("tx.ForeignAssets too long, max number of foreign assets is 8"), + expectedError: "tx.GlobalStateSchema is too large. 65 > 64", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - Accounts: []basics.Address{{}, {}, {}}, - ForeignApps: []basics.AppIndex{14, 15, 16, 17}, - ForeignAssets: []basics.AssetIndex{14, 15, 16, 17}, + ac: ApplicationCallTxnFields{ + ApplicationID: 0, + ApprovalProgram: v5, + ClearStateProgram: v5, + ApplicationArgs: [][]byte{ + []byte("write"), }, + LocalStateSchema: basics.StateSchema{NumUint: 17}, }, - proto: futureProto, - expectedError: fmt.Errorf("tx references exceed MaxAppTotalTxnReferences = 8"), + expectedError: "tx.LocalStateSchema is too large. 17 > 16", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - ApprovalProgram: []byte(strings.Repeat("X", 1025)), - ClearStateProgram: []byte(strings.Repeat("X", 1025)), - ExtraProgramPages: 0, - OnCompletion: UpdateApplicationOC, + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApplicationArgs: [][]byte{ + []byte("write"), }, + ExtraProgramPages: 1, }, - proto: protoV28, - expectedError: fmt.Errorf("app programs too long. max total len %d bytes", curProto.MaxAppProgramLen), + expectedError: "tx.ExtraProgramPages is immutable", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - ApprovalProgram: []byte(strings.Repeat("X", 1025)), - ClearStateProgram: []byte(strings.Repeat("X", 1025)), - ExtraProgramPages: 0, - OnCompletion: UpdateApplicationOC, + ac: ApplicationCallTxnFields{ + ApplicationID: 0, + ApprovalProgram: v5, + ClearStateProgram: v5, + ApplicationArgs: [][]byte{ + []byte("write"), }, + ExtraProgramPages: 4, }, - proto: futureProto, - }, - { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - ApprovalProgram: v5, - ClearStateProgram: v5, - ApplicationArgs: [][]byte{ - []byte("write"), - }, - ExtraProgramPages: 1, - OnCompletion: UpdateApplicationOC, - }, + expectedError: "tx.ExtraProgramPages exceeds MaxExtraAppProgramPages = 3", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApplicationArgs: slices.Repeat([][]byte{[]byte("arg")}, 16), }, - proto: protoV28, - expectedError: fmt.Errorf("tx.ExtraProgramPages is immutable"), }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - Boxes: []BoxRef{{Index: 1, Name: []byte("junk")}}, - }, + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApplicationArgs: slices.Repeat([][]byte{[]byte("arg")}, 17), }, - proto: futureProto, - expectedError: fmt.Errorf("tx.Boxes[0].Index is 1. Exceeds len(tx.ForeignApps)"), + expectedError: "tx.ApplicationArgs has too many arguments. 17 > 16", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - Boxes: []BoxRef{{Index: 1, Name: []byte("junk")}}, - ForeignApps: []basics.AppIndex{1}, - }, + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApplicationArgs: [][]byte{make([]byte, 1500), make([]byte, 548)}, }, - proto: futureProto, }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - Boxes: []BoxRef{{Index: 1, Name: []byte("junk")}}, - ForeignApps: []basics.AppIndex{1}, - }, + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApplicationArgs: [][]byte{make([]byte, 1501), make([]byte, 548)}, }, - proto: protoV32, - expectedError: fmt.Errorf("tx.Boxes too long, max number of box references is 0"), + expectedError: "tx.ApplicationArgs total length is too long. 2049 > 2048", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - Boxes: []BoxRef{{Index: 1, Name: make([]byte, 65)}}, - ForeignApps: []basics.AppIndex{1}, - }, + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ForeignApps: []basics.AppIndex{10, 11}, + }, + cv: cv27, + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ForeignApps: []basics.AppIndex{10, 11, 12}, }, - proto: futureProto, - expectedError: fmt.Errorf("tx.Boxes[0].Name too long, max len 64 bytes"), + cv: cv27, + expectedError: "tx.ForeignApps too long, max number of foreign apps is 2", }, { - tx: Transaction{ - Type: protocol.ApplicationCallTx, - Header: okHeader, - ApplicationCallTxnFields: ApplicationCallTxnFields{ - ApplicationID: 1, - Boxes: []BoxRef{{Index: 1, Name: make([]byte, 65)}}, - ForeignApps: []basics.AppIndex{1}, + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ForeignApps: []basics.AppIndex{10, 11, 12, 13, 14, 15, 16, 17}, + }, + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Accounts: slices.Repeat([]basics.Address{{}}, 4), + }, + cv: protocol.ConsensusV40, + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Accounts: slices.Repeat([]basics.Address{{}}, 5), + }, + cv: protocol.ConsensusV40, + expectedError: "tx.Accounts too long, max number of accounts is 4", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Accounts: slices.Repeat([]basics.Address{{}}, 9), + }, + expectedError: "tx.Accounts too long, max number of accounts is 8", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ForeignAssets: []basics.AssetIndex{14, 15, 16, 17, 18, 19, 20, 21, 22}, + }, + expectedError: "tx.ForeignAssets too long, max number of foreign assets is 8", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Accounts: []basics.Address{{}, {}, {}}, + ForeignApps: []basics.AppIndex{14, 15, 16, 17}, + ForeignAssets: []basics.AssetIndex{14, 15, 16, 17}, + }, + expectedError: "tx references exceed MaxAppTotalTxnReferences = 8", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApprovalProgram: []byte(strings.Repeat("X", 1025)), + ClearStateProgram: []byte(strings.Repeat("X", 1025)), + ExtraProgramPages: 0, + OnCompletion: UpdateApplicationOC, + }, + cv: cv28, + expectedError: "app programs too long. max total len 2048 bytes", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApprovalProgram: []byte(strings.Repeat("X", 1025)), + ClearStateProgram: []byte(strings.Repeat("X", 1025)), + ExtraProgramPages: 0, + OnCompletion: UpdateApplicationOC, + }, + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + ApprovalProgram: v5, + ClearStateProgram: v5, + ApplicationArgs: [][]byte{ + []byte("write"), }, + ExtraProgramPages: 1, + OnCompletion: UpdateApplicationOC, + }, + cv: cv28, + expectedError: "tx.ExtraProgramPages is immutable", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Boxes: []BoxRef{{Index: 1, Name: []byte("junk")}}, + }, + expectedError: "tx.Boxes[0].Index is 1. Exceeds len(tx.ForeignApps)", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Boxes: []BoxRef{{Index: 1, Name: []byte("junk")}}, + ForeignApps: []basics.AppIndex{1}, + }, + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Boxes: []BoxRef{{Index: 1, Name: []byte("junk")}}, + ForeignApps: []basics.AppIndex{1}, + }, + cv: cv32, + expectedError: "tx.Boxes too long, max number of box references is 0", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Boxes: []BoxRef{{Index: 1, Name: make([]byte, 65)}}, + ForeignApps: []basics.AppIndex{1}, }, - proto: protoV36, - expectedError: nil, + expectedError: "tx.Boxes[0].Name too long, max len 64 bytes", + }, + { + ac: ApplicationCallTxnFields{ + ApplicationID: 1, + Boxes: []BoxRef{{Index: 1, Name: make([]byte, 65)}}, + ForeignApps: []basics.AppIndex{1}, + }, + cv: cv36, }, } - for _, usecase := range usecases { - err := usecase.tx.WellFormed(SpecialAddresses{}, usecase.proto) - assert.Equal(t, usecase.expectedError, err) + for i, tc := range cases { + name := fmt.Sprintf("i=%d", i) + if tc.expectedError != "" { + name = tc.expectedError + } + t.Run(name, func(t *testing.T) { + cv := tc.cv + if cv == "" { + cv = protocol.ConsensusFuture + } + err := tc.ac.wellFormed(config.Consensus[cv]) + if tc.expectedError != "" { + require.ErrorContains(t, err, tc.expectedError) + } else { + require.NoError(t, err) + } + }) } } diff --git a/data/transactions/logic/box.go b/data/transactions/logic/box.go index fb9ee6aa09..3a85951292 100644 --- a/data/transactions/logic/box.go +++ b/data/transactions/logic/box.go @@ -44,7 +44,7 @@ func (cx *EvalContext) availableBox(name string, operation BoxOperation, createS return nil, false, fmt.Errorf("boxes may not be accessed from ClearState program") } - dirty, ok := cx.available.boxes[BoxRef{cx.appID, name}] + dirty, ok := cx.available.boxes[basics.BoxRef{App: cx.appID, Name: name}] if !ok && cx.UnnamedResources != nil { ok = cx.UnnamedResources.AvailableBox(cx.appID, name, operation, createSize) } @@ -98,7 +98,7 @@ func (cx *EvalContext) availableBox(name string, operation BoxOperation, createS case BoxReadOperation: /* nothing to do */ } - cx.available.boxes[BoxRef{cx.appID, name}] = dirty + cx.available.boxes[basics.BoxRef{App: cx.appID, Name: name}] = dirty if cx.available.dirtyBytes > cx.ioBudget { return nil, false, fmt.Errorf("write budget (%d) exceeded %d", cx.ioBudget, cx.available.dirtyBytes) diff --git a/data/transactions/logic/box_test.go b/data/transactions/logic/box_test.go index 0b6c604b8e..bfe23b9c54 100644 --- a/data/transactions/logic/box_test.go +++ b/data/transactions/logic/box_test.go @@ -73,45 +73,102 @@ func TestBoxNewDel(t *testing.T) { func TestBoxNewBad(t *testing.T) { partitiontest.PartitionTest(t) - t.Parallel() - - ep, txn, ledger := MakeSampleEnv() - - ledger.NewApp(txn.Sender, 888, basics.AppParams{}) - TestApp(t, `byte "self"; int 999; box_create`, ep, "write budget") - - // In test proto, you get 100 I/O budget per boxref, and 1000 is the - // absolute biggest box. - ten := [10]transactions.BoxRef{} - txn.Boxes = append(txn.Boxes, ten[:]...) // write budget is now 11*100 = 1100 - TestApp(t, `byte "self"; int 999; box_create`, ep) - TestApp(t, `byte "self"; int 1000; box_resize; int 1`, ep) - TestApp(t, `byte "self"; int 1001; box_resize; int 1`, ep, "box size too large") - ledger.DelBoxes(888, "self") - TestApp(t, `byte "self"; int 1000; box_create`, ep) - ledger.DelBoxes(888, "self") - TestApp(t, `byte "self"; int 1001; box_create`, ep, "box size too large") - TestApp(t, `byte "unknown"; int 1000; box_create`, ep, "invalid Box reference") - - long := strings.Repeat("x", 65) - txn.Boxes = []transactions.BoxRef{{Name: []byte(long)}} - TestApp(t, 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 - TestApp(t, NoTrack(`byte ""; int 1000; box_create`), ep, "zero length") + for _, access := range []bool{false, true} { + t.Run(fmt.Sprintf("access=%t", access), func(t *testing.T) { + t.Parallel() + ep, txn, ledger := MakeSampleEnv() + // txn.Txn.Boxes contains (0,"self"), (0,"other") + if access { + ConvertEPToAccess(ep, false) + // now .Boxes is nil, .Access contains what was in .Boxes + } + ledger.NewApp(txn.Sender, 888, basics.AppParams{}) + TestApp(t, `byte "self"; int 200; box_create`, ep) + ledger.DelBoxes(888, "self") + TestApp(t, `byte "self"; int 201; box_create`, ep, "write budget") // 2 boxes gives 200 budget + + // In test proto, you get 100 I/O budget per boxref, and 1000 is the + // absolute biggest box. So that you get budget for boxes named but + // unused, and for empties. + if access { + // expand write budget + two := [2]transactions.ResourceRef{} + txn.Access = append(txn.Access, two[:]...) // write budget is now 11*100 = 1100 + x := transactions.ResourceRef{Box: transactions.BoxRef{Index: 0, Name: []byte{0x01}}} + for range 2 { + txn.Access = append(txn.Access, x) + } + } else { + two := [2]transactions.BoxRef{} + txn.Boxes = append(txn.Boxes, two[:]...) // write budget is now 11*100 = 1100 + x := transactions.BoxRef{Index: 0, Name: []byte{0x01}} + for range 2 { + txn.Boxes = append(txn.Boxes, x) + } + } + // write budget is now (2 original + 2 empties + 2 x's)*100 = 600 + TestApp(t, `byte "self"; int 600; box_create`, ep) + ledger.DelBoxes(888, "self") + TestApp(t, `byte "self"; int 601; box_create`, ep, "write budget") // 2 boxes gives 200 budget + + // add more budget to test max box length + if access { + // expand write budget + five := [5]transactions.ResourceRef{} + txn.Access = append(txn.Access, five[:]...) // write budget is now 11*100 = 1100 + } else { + five := [5]transactions.BoxRef{} + txn.Boxes = append(txn.Boxes, five[:]...) // write budget is now 11*100 = 1100 + } + // write budget is up to 1100, but we can't make more that 1000 + + TestApp(t, `byte "self"; int 999; box_create`, ep) + TestApp(t, `byte "self"; int 1000; box_resize; int 1`, ep) + TestApp(t, `byte "self"; int 1001; box_resize; int 1`, ep, "box size too large") + ledger.DelBoxes(888, "self") + TestApp(t, `byte "self"; int 1000; box_create`, ep) + ledger.DelBoxes(888, "self") + TestApp(t, `byte "self"; int 1001; box_create`, ep, "box size too large") + + TestApp(t, `byte "unknown"; int 1000; box_create`, ep, "invalid Box reference") + + long := strings.Repeat("x", 65) + if access { + txn.Access = []transactions.ResourceRef{{Box: transactions.BoxRef{Name: []byte(long)}}} + } else { + txn.Boxes = []transactions.BoxRef{{Name: []byte(long)}} + } + TestApp(t, NoTrack(fmt.Sprintf(`byte "%s"; int 1000; box_create`, long)), ep, "name too long") + + // irrelevant, zero check comes first anyway + if access { + txn.Access = []transactions.ResourceRef{{Box: transactions.BoxRef{Name: []byte("")}}} + } else { + txn.Boxes = []transactions.BoxRef{{Name: []byte("")}} + } + TestApp(t, NoTrack(`byte ""; int 1000; box_create`), ep, "zero length") + }) + } } func TestBoxReadWrite(t *testing.T) { partitiontest.PartitionTest(t) - t.Parallel() - ep, txn, ledger := MakeSampleEnv() + for _, access := range []bool{false, true} { + t.Run(fmt.Sprintf("access=%t", access), func(t *testing.T) { + t.Parallel() + ep, txn, ledger := MakeSampleEnv() + // txn.Txn.Boxes contains (0,"self"), (0,"other") + if access { + ConvertEPToAccess(ep, false) + // now .Boxes is nil, .Access contains what was in .Boxes + } - ledger.NewApp(txn.Sender, 888, basics.AppParams{}) - // extract some bytes until past the end, confirm the begin as zeros, and - // when it fails. - TestApp(t, `byte "self"; int 4; box_create; assert + ledger.NewApp(txn.Sender, 888, basics.AppParams{}) + // extract some bytes until past the end, confirm the begin as zeros, and + // when it fails. + TestApp(t, `byte "self"; int 4; box_create; assert byte "self"; int 1; int 2; box_extract; byte 0x0000; ==; assert; byte "self"; int 1; int 3; box_extract; @@ -120,36 +177,38 @@ func TestBoxReadWrite(t *testing.T) { byte 0x00000000; ==; assert; int 1`, ep) - TestApp(t, `byte "self"; int 1; int 4; box_extract; + TestApp(t, `byte "self"; int 1; int 4; box_extract; byte 0x00000000; ==`, ep, "extraction end 5") - // Replace some bytes until past the end, confirm when it fails. - TestApp(t, `byte "self"; int 1; byte 0x3031; box_replace; + // Replace some bytes until past the end, confirm when it fails. + TestApp(t, `byte "self"; int 1; byte 0x3031; box_replace; byte "self"; int 0; int 4; box_extract; byte 0x00303100; ==`, ep) - TestApp(t, `byte "self"; int 1; byte 0x303132; box_replace; + TestApp(t, `byte "self"; int 1; byte 0x303132; box_replace; byte "self"; int 0; int 4; box_extract; byte 0x00303132; ==`, ep) - TestApp(t, `byte "self"; int 1; byte 0x30313233; box_replace; + TestApp(t, `byte "self"; int 1; byte 0x30313233; box_replace; byte "self"; int 0; int 4; box_extract; byte 0x0030313233; ==`, ep, "replacement end 5") - // Replace with different byte in different place. - TestApp(t, `byte "self"; int 0; byte 0x4444; box_replace; + // Replace with different byte in different place. + TestApp(t, `byte "self"; int 0; byte 0x4444; box_replace; byte "self"; int 0; int 4; box_extract; byte 0x44443132; ==`, ep) - // All bow down to the God of code coverage! - ledger.DelBoxes(888, "self") - TestApp(t, `byte "self"; int 1; byte 0x3031; box_replace`, ep, - "no such box") - TestApp(t, `byte "junk"; int 1; byte 0x3031; box_replace`, ep, - "invalid Box reference") - - TestApp(t, `byte "self"; int 1; int 2; byte 0x3031; box_splice`, ep, - "no such box") - TestApp(t, `byte "junk"; int 1; int 2; byte 0x3031; box_splice`, ep, - "invalid Box reference") + // All bow down to the God of code coverage! + ledger.DelBoxes(888, "self") + TestApp(t, `byte "self"; int 1; byte 0x3031; box_replace`, ep, + "no such box") + TestApp(t, `byte "junk"; int 1; byte 0x3031; box_replace`, ep, + "invalid Box reference") + + TestApp(t, `byte "self"; int 1; int 2; byte 0x3031; box_splice`, ep, + "no such box") + TestApp(t, `byte "junk"; int 1; int 2; byte 0x3031; box_splice`, ep, + "invalid Box reference") + }) + } } func TestBoxSplice(t *testing.T) { @@ -360,43 +419,68 @@ func TestBoxAvailability(t *testing.T) { func TestBoxReadBudget(t *testing.T) { partitiontest.PartitionTest(t) - t.Parallel() appID := basics.AppIndex(888) appAddr := appID.Address() - ep, txn, ledger := MakeSampleEnv() - ledger.NewApp(basics.Address{}, appID, basics.AppParams{}) - - // Sample txn has two box refs, so read budget is 2*100 - - ledger.NewBox(appID, "self", make([]byte, 100), appAddr) - ledger.NewBox(appID, "other", make([]byte, 100), appAddr) - ledger.NewBox(appID, "third", make([]byte, 100), appAddr) - - // Right at budget - TestApp(t, `byte "self"; box_len; assert; byte "other"; box_len; assert; ==`, ep) - - // With three box refs, read budget is now 3*100 - txn.Boxes = append(txn.Boxes, transactions.BoxRef{Name: []byte("third")}) - TestApp(t, `byte "self"; box_len; assert; byte "third"; box_len; assert; ==`, ep) - - // Increase "third" box size to 101 - ledger.DelBox(appID, "third", appAddr) - ledger.NewBox(appID, "third", make([]byte, 101), appAddr) - - // Budget exceeded - TestApp(t, `byte "self"; box_len; assert; byte "third"; box_len; assert; ==`, ep, "box read budget (300) exceeded") - // Still exceeded if we don't touch the boxes - TestApp(t, `int 1`, ep, "box read budget (300) exceeded") - - // Still exceeded with one box ref - txn.Boxes = txn.Boxes[2:] - TestApp(t, `byte "third"; box_len; assert; int 101; ==`, ep, "box read budget (100) exceeded") - - // But not with two - txn.Boxes = append(txn.Boxes, transactions.BoxRef{}) - TestApp(t, `byte "third"; box_len; assert; int 101; ==`, ep) + for _, access := range []bool{false, true} { + t.Run(fmt.Sprintf("access=%t", access), func(t *testing.T) { + t.Parallel() + ep, txn, ledger := MakeSampleEnv() + // txn.Txn.Boxes contains (0,"self"), (0,"other") + if access { + ConvertEPToAccess(ep, false) + // now .Boxes is nil, .Access contains what was in .Boxes + } + + ledger.NewApp(basics.Address{}, appID, basics.AppParams{}) + + // Sample txn has two box refs, so read budget is 2*100 + + ledger.NewBox(appID, "self", make([]byte, 100), appAddr) + ledger.NewBox(appID, "other", make([]byte, 100), appAddr) + ledger.NewBox(appID, "third", make([]byte, 100), appAddr) + + // Right at budget + TestApp(t, `byte "self"; box_len; assert; byte "other"; box_len; assert; ==`, ep) + + // With three box refs, read budget is now 3*100 + if access { + txn.Access = append(txn.Access, transactions.ResourceRef{ + Box: transactions.BoxRef{Name: []byte("third")}, + }) + } else { + txn.Boxes = append(txn.Boxes, transactions.BoxRef{Name: []byte("third")}) + } + TestApp(t, `byte "self"; box_len; assert; byte "third"; box_len; assert; ==`, ep) + + // Increase "third" box size to 101 + ledger.DelBox(appID, "third", appAddr) + ledger.NewBox(appID, "third", make([]byte, 101), appAddr) + + // Budget exceeded + TestApp(t, `byte "self"; box_len; assert; byte "third"; box_len; assert; ==`, ep, "box read budget (300) exceeded") + // Still exceeded if we don't touch the boxes + TestApp(t, `int 1`, ep, "box read budget (300) exceeded") + + // Still exceeded with one box ref (remove the original box refs) + if access { + txn.Access[len(txn.Access)-3] = txn.Access[len(txn.Access)-1] + txn.Access = txn.Access[:len(txn.Access)-2] + } else { + txn.Boxes = txn.Boxes[2:] + } + TestApp(t, `byte "third"; box_len; assert; int 101; ==`, ep, "box read budget (100) exceeded") + + // But not with a second empty ref + if access { + txn.Access = append(txn.Access, transactions.ResourceRef{}) + } else { + txn.Boxes = append(txn.Boxes, transactions.BoxRef{}) + } + TestApp(t, `byte "third"; box_len; assert; int 101; ==`, ep) + }) + } } func TestBoxWriteBudget(t *testing.T) { @@ -529,33 +613,52 @@ func TestBoxRepeatedCreate(t *testing.T) { func TestIOBudgetGrow(t *testing.T) { partitiontest.PartitionTest(t) - t.Parallel() - - ep, txn, ledger := MakeSampleEnv() - ledger.NewApp(basics.Address{}, 888, basics.AppParams{}) - ledger.CreateBox(888, "self", 101) - ledger.CreateBox(888, "other", 101) - TestApp(t, `byte "self"; int 1; byte 0x3333; box_replace; + for _, access := range []bool{false, true} { + t.Run(fmt.Sprintf("access=%t", access), func(t *testing.T) { + t.Parallel() + ep, txn, ledger := MakeSampleEnv() + // txn.Txn.Boxes contains (0,"self"), (0,"other") + if access { + ConvertEPToAccess(ep, false) + // now .Boxes is nil, .Access contains what was in .Boxes + } + ledger.NewApp(basics.Address{}, 888, basics.AppParams{}) + ledger.CreateBox(888, "self", 101) + ledger.CreateBox(888, "other", 101) + + TestApp(t, `byte "self"; int 1; byte 0x3333; box_replace; byte "other"; int 1; byte 0x3333; box_replace; int 1`, ep, "read budget (200) exceeded") - txn.Boxes = append(txn.Boxes, transactions.BoxRef{}) - // Since we added an empty BoxRef, we can read > 200. - TestApp(t, `byte "self"; int 1; int 7; box_extract; pop; + if access { + txn.Access = append(txn.Access, transactions.ResourceRef{}) + } else { + txn.Boxes = append(txn.Boxes, transactions.BoxRef{}) + } + // Since we added an empty BoxRef, we can read > 200. + TestApp(t, `byte "self"; int 1; int 7; box_extract; pop; byte "other"; int 1; int 7; box_extract; pop; int 1`, ep) - // Add write, for that matter - TestApp(t, `byte "self"; int 1; byte 0x3333; box_replace; + // Add write, for that matter + TestApp(t, `byte "self"; int 1; byte 0x3333; box_replace; byte "other"; int 1; byte 0x3333; box_replace; int 1`, ep) - txn.Boxes = append(txn.Boxes, transactions.BoxRef{Name: []byte("another")}) + if access { + txn.Access = append(txn.Access, transactions.ResourceRef{ + Box: transactions.BoxRef{Name: []byte("another")}, + }) + } else { + txn.Boxes = append(txn.Boxes, transactions.BoxRef{Name: []byte("another")}) + } - // Here we read 202, and write a very different 350 (since we now have 4 brs) - TestApp(t, `byte "self"; int 1; int 7; box_extract; pop; + // Here we read 202, and write a very different 350 (since we now have 4 brs) + TestApp(t, `byte "self"; int 1; int 7; box_extract; pop; byte "other"; int 1; int 7; box_extract; pop; byte "another"; int 350; box_create`, ep) + }) + } } func TestConveniences(t *testing.T) { diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 96b00d8092..93fbc9b3b1 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -40,6 +40,7 @@ import ( "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/util" ) // The constants below control opcode evaluation and MAY NOT be changed without @@ -274,12 +275,6 @@ type LedgerForLogic interface { Counter() uint64 } -// BoxRef is the "hydrated" form of a transactions.BoxRef - it has the actual app id, not an index -type BoxRef struct { - App basics.AppIndex - Name string -} - // UnnamedResourcePolicy is an interface that defines the policy for allowing unnamed resources. // This should only be used during simulation or debugging. type UnnamedResourcePolicy interface { @@ -495,7 +490,7 @@ func (ep *EvalParams) computeAvailability() *resources { sharedApps: make(map[basics.AppIndex]struct{}), sharedHoldings: make(map[ledgercore.AccountAsset]struct{}), sharedLocals: make(map[ledgercore.AccountApp]struct{}), - boxes: make(map[BoxRef]bool), + boxes: make(map[basics.BoxRef]bool), } for i := range ep.TxnGroup { available.fill(&ep.TxnGroup[i].Txn, ep) @@ -1081,31 +1076,18 @@ func (cx *EvalContext) evalStates() []evalState { // should be located outside of the evalState, with the PC. var stack []any if cx.groupIndex == i { - stack = convertSlice(cx.Stack, func(sv stackValue) any { - return sv.asAny() - }) + stack = util.Map(cx.Stack, stackValue.asAny) } states[i] = evalState{ Scratch: scratchAsAny, Stack: stack, - Logs: convertSlice(cx.TxnGroup[i].EvalDelta.Logs, func(s string) []byte { return []byte(s) }), + Logs: util.Map(cx.TxnGroup[i].EvalDelta.Logs, func(s string) []byte { return []byte(s) }), } } return states } -func convertSlice[X any, Y any](input []X, fn func(X) Y) []Y { - if input == nil { - return nil - } - output := make([]Y, len(input)) - for i := range input { - output[i] = fn(input[i]) - } - return output -} - // EvalContract executes stateful program as the gi'th transaction in params func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParams) (bool, *EvalContext, error) { if params.Ledger == nil { @@ -1147,7 +1129,7 @@ func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParam // make any "0 index" box refs available now that we have an appID. for _, br := range cx.txn.Txn.Boxes { if br.Index == 0 { - cx.EvalParams.available.boxes[BoxRef{cx.appID, string(br.Name)}] = false + cx.EvalParams.available.boxes[basics.BoxRef{App: cx.appID, Name: string(br.Name)}] = false } } // and add the appID to `createdApps` @@ -1161,11 +1143,17 @@ func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParam // Check the I/O budget for reading if this is the first top-level app call if cx.caller == nil && !cx.readBudgetChecked { - boxRefCount := uint64(0) // Intentionally counts duplicates + bumps := uint64(0) // Intentionally counts duplicates for _, tx := range cx.TxnGroup { - boxRefCount += uint64(len(tx.Txn.Boxes)) + bumps += uint64(len(tx.Txn.Boxes)) + for _, rr := range tx.Txn.Access { + // A box or an empty ref is an io quota bump + if !rr.Box.Empty() || rr.Empty() { + bumps++ + } + } } - cx.ioBudget = boxRefCount * cx.Proto.BytesPerBoxReference + cx.ioBudget = bumps * cx.Proto.BytesPerBoxReference used := uint64(0) for br := range cx.available.boxes { @@ -4250,7 +4238,7 @@ func (cx *EvalContext) accountReference(account stackValue) (basics.Address, uin // created earlier in the group, or because of group sharing) ok := cx.availableAccount(addr) if !ok { - return addr, 0, fmt.Errorf("invalid Account reference %s", addr) + return addr, 0, fmt.Errorf("unavailable Account %s", addr) } // available, but not in txn.Accounts. Return 1 higher to signal. return addr, uint64(len(cx.txn.Txn.Accounts) + 1), nil @@ -4718,8 +4706,13 @@ func (cx *EvalContext) appReference(ref uint64, foreign bool) (aid basics.AppInd if foreign { // In old versions, a foreign reference must be an index in ForeignApps or 0 if ref <= uint64(len(cx.txn.Txn.ForeignApps)) { - return basics.AppIndex(cx.txn.Txn.ForeignApps[ref-1]), nil + return cx.txn.Txn.ForeignApps[ref-1], nil } + // it seems most consistent to allow slot access into tx.Access if it is used with an old app. + if ref > 0 && ref-1 < uint64(len(cx.txn.Txn.Access)) && cx.txn.Txn.Access[ref-1].App != 0 { + return cx.txn.Txn.Access[ref-1].App, nil + } + return 0, fmt.Errorf("App index %d beyond txn.ForeignApps", ref) } // Otherwise it's direct @@ -4750,8 +4743,12 @@ func (cx *EvalContext) resolveApp(ref uint64) (aid basics.AppIndex, err error) { // given to anyone who cares about semantics in the first few rounds of // a new network - don't use indexes for references, use the App ID if ref <= uint64(len(cx.txn.Txn.ForeignApps)) { - return basics.AppIndex(cx.txn.Txn.ForeignApps[ref-1]), nil + return cx.txn.Txn.ForeignApps[ref-1], nil } + if ref > 0 && ref-1 < uint64(len(cx.txn.Txn.Access)) && cx.txn.Txn.Access[ref-1].App != 0 { + return cx.txn.Txn.Access[ref-1].App, nil + } + return 0, fmt.Errorf("unavailable App %d", ref) } @@ -4773,21 +4770,22 @@ func (cx *EvalContext) localsReference(account stackValue, ref uint64) (basics.A } } - // Do an extra check to give a better error. The app is definitely - // available. If the addr is too, then the trouble is they must have - // come from different transactions, and the HOLDING is the problem. + // Do an extra check to give a better error, which also allows the + // UnnamedResources code to notice that the account must be available as + // well. acctOK := cx.availableAccount(addr) + localsErr := fmt.Errorf("unavailable Local State %s x %d", addr, aid) switch { case err != nil && acctOK: - // do nothing, err contains the an Asset specific problem + // do nothing, err contains the an App specific problem case err == nil && acctOK: // although both are available, the LOCALS are not - err = fmt.Errorf("unavailable Local State %s x %d", addr, aid) + err = localsErr case err != nil && !acctOK: - err = fmt.Errorf("unavailable Account %s, %w", addr, err) + err = fmt.Errorf("unavailable Account %s, %w, %w", addr, err, localsErr) case err == nil && !acctOK: - err = fmt.Errorf("unavailable Account %s", addr) + err = fmt.Errorf("unavailable Account %s, %w", addr, localsErr) } return basics.Address{}, 0, 0, err @@ -4803,7 +4801,32 @@ func (cx *EvalContext) localsReference(account stackValue, ref uint64) (basics.A if err != nil { return basics.Address{}, 0, 0, err } - return addr, app, addrIdx, nil + + // But if the transaction is using tx.Access, we must be more stringent, or + // else we open up to accessing a huge cross-product of locals. + if cx.txn.Txn.Access == nil { + return addr, app, addrIdx, nil + } + + if addr == cx.txn.Txn.Sender && app == cx.appID { + return addr, app, addrIdx, nil + } + + if slices.ContainsFunc(cx.txn.Txn.Access, + func(rr transactions.ResourceRef) bool { + // See if any holding in tx.Access matches what we need + if l := rr.Locals; !l.Empty() { + laddr, lapp, _ := l.Resolve(cx.txn.Txn.Access, cx.txn.Txn.Sender) + if laddr == addr && lapp == app { + return true + } + } + return false + }) { + return addr, app, addrIdx, nil + } + return basics.Address{}, 0, 0, fmt.Errorf("unavailable Local State %s x %d is not in tx.Access", addr, app) + } func (cx *EvalContext) assetReference(ref uint64, foreign bool) (aid basics.AssetIndex, err error) { @@ -4825,9 +4848,13 @@ func (cx *EvalContext) assetReference(ref uint64, foreign bool) (aid basics.Asse if foreign { // In old versions, a foreign reference must be an index in ForeignAssets if ref < uint64(len(cx.txn.Txn.ForeignAssets)) { - return basics.AssetIndex(cx.txn.Txn.ForeignAssets[ref]), nil + return cx.txn.Txn.ForeignAssets[ref], nil + } + // it seems most consistent to allow slot access into tx.Access if it is used with an old app. + if ref > 0 && ref-1 < uint64(len(cx.txn.Txn.Access)) && cx.txn.Txn.Access[ref-1].Asset != 0 { + return cx.txn.Txn.Access[ref-1].Asset, nil } - return 0, fmt.Errorf("Asset index %d beyond txn.ForeignAssets", ref) + return 0, fmt.Errorf("Asset index %d beyond access arrays", ref) } // Otherwise it's direct return basics.AssetIndex(ref), nil @@ -4835,9 +4862,8 @@ func (cx *EvalContext) assetReference(ref uint64, foreign bool) (aid basics.Asse const lastForbiddenResource = 255 -// resolveAsset figures out what Asset an integer is referring to, considering 0 as -// current app first, then uses the integer as is if it is an availableAsset, then -// tries to perform a slot lookup. +// resolveAsset figures out what Asset an integer is referring to, checking if +// the integer is an availableAsset, then tries to perform a slot lookup. func (cx *EvalContext) resolveAsset(ref uint64) (aid basics.AssetIndex, err error) { if cx.Proto.AppForbidLowResources { defer func() { @@ -4855,7 +4881,10 @@ func (cx *EvalContext) resolveAsset(ref uint64) (aid basics.AssetIndex, err erro // given to anyone who cares about semantics in the first few rounds of // a new network - don't use indexes for references, use the Asset ID if ref < uint64(len(cx.txn.Txn.ForeignAssets)) { - return basics.AssetIndex(cx.txn.Txn.ForeignAssets[ref]), nil + return cx.txn.Txn.ForeignAssets[ref], nil + } + if ref > 0 && ref-1 < uint64(len(cx.txn.Txn.Access)) && cx.txn.Txn.Access[ref-1].Asset != 0 { + return cx.txn.Txn.Access[ref-1].Asset, nil } return 0, fmt.Errorf("unavailable Asset %d", ref) } @@ -4902,7 +4931,27 @@ func (cx *EvalContext) holdingReference(account stackValue, ref uint64) (basics. if err != nil { return basics.Address{}, 0, err } - return addr, asset, nil + + // But if the transaction is using tx.Access, we must be more stringent, or + // else we open up to accessing a huge cross-product of holdings. + if cx.txn.Txn.Access == nil { + return addr, asset, nil + } + + if slices.ContainsFunc(cx.txn.Txn.Access, + func(rr transactions.ResourceRef) bool { + // See if any holding in tx.Access matches what we need + if h := rr.Holding; !h.Empty() { + haddr, hasset, _ := h.Resolve(cx.txn.Txn.Access, cx.txn.Txn.Sender) + if haddr == addr && hasset == asset { + return true + } + } + return false + }) { + return addr, asset, nil + } + return basics.Address{}, 0, fmt.Errorf("unavailable Holding %s x %d is not in tx.Access", addr, asset) } func opAssetHoldingGet(cx *EvalContext) error { @@ -5212,7 +5261,7 @@ func (cx *EvalContext) assignAsset(sv stackValue) (basics.AssetIndex, error) { return aid, nil } - return 0, fmt.Errorf("unavailable Asset %d", aid) + return 0, fmt.Errorf("unavailable Asset %d during assignment %v", aid, cx.available) } // availableAsset determines whether an asset is "available". Before @@ -5223,7 +5272,10 @@ func (cx *EvalContext) assignAsset(sv stackValue) (basics.AssetIndex, error) { // transaction (axfer,acfg,afrz), but not for holding lookups or assignments to // an inner static array. func (cx *EvalContext) availableAsset(aid basics.AssetIndex) bool { - // Ensure that aid is in Foreign Assets + // Check if aid is in an access array + if slices.ContainsFunc(cx.txn.Txn.Access, func(rr transactions.ResourceRef) bool { return rr.Asset == aid }) { + return true + } if slices.Contains(cx.txn.Txn.ForeignAssets, aid) { return true } @@ -5265,7 +5317,10 @@ func (cx *EvalContext) assignApp(sv stackValue) (basics.AppIndex, error) { } func (cx *EvalContext) availableApp(aid basics.AppIndex) bool { - // Ensure that aid is in Foreign Apps + // Check if aid is in an access array + if slices.ContainsFunc(cx.txn.Txn.Access, func(rr transactions.ResourceRef) bool { return rr.App == aid }) { + return true + } if slices.Contains(cx.txn.Txn.ForeignApps, aid) { return true } diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index f41b537692..921d7f5eec 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -19,6 +19,7 @@ package logic import ( "encoding/hex" "fmt" + "slices" "strconv" "strings" "testing" @@ -32,6 +33,7 @@ import ( "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" + "github.com/algorand/go-algorand/util" ) func makeApp(li uint64, lb uint64, gi uint64, gb uint64) basics.AppParams { @@ -72,6 +74,92 @@ func makeOldAndNewEnv(version uint64) (*EvalParams, *EvalParams, *Ledger) { return old, new, sharedLedger } +// Converts ep in place such that only tx.Access is used, not foreign arrays +func convertEPToAccess(ep *EvalParams, includeCrossProducts bool) { + for i := range ep.TxnGroup { + ep.TxnGroup[i].Txn = convertTxnToAccess(ep.TxnGroup[i].Txn, includeCrossProducts) + } +} + +// Returns a copy of `txn` with Foreign arrays nil'd out and replaced by equivalent Access list. +func convertTxnToAccess(txn transactions.Transaction, includeCrossProducts bool) transactions.Transaction { + for _, acct := range txn.Accounts { + txn.Access = append(txn.Access, transactions.ResourceRef{ + Address: acct, + }) + } + + for _, asset := range txn.ForeignAssets { + // Add each foreign asset + txn.Access = append(txn.Access, transactions.ResourceRef{ + Asset: asset, + }) + if includeCrossProducts { + index := len(txn.Access) + // Add the holding of the Sender + txn.Access = append(txn.Access, transactions.ResourceRef{ + Holding: transactions.HoldingRef{ + Address: 0, // Sender + Asset: uint64(index), + }, + }) + // Add holding for each added account + for i := range txn.Accounts { + txn.Access = append(txn.Access, transactions.ResourceRef{ + Holding: transactions.HoldingRef{ + Address: uint64(i + 1), + Asset: uint64(index), + }, + }) + } + } + } + + for _, app := range txn.ForeignApps { + txn.Access = append(txn.Access, transactions.ResourceRef{ + App: app, + }) + if includeCrossProducts { + index := len(txn.Access) + txn.Access = append(txn.Access, transactions.ResourceRef{ + Locals: transactions.LocalsRef{ + Address: 0, // Sender + App: uint64(index), + }, + }) + for i := range txn.Accounts { + txn.Access = append(txn.Access, transactions.ResourceRef{ + Locals: transactions.LocalsRef{ + Address: uint64(i + 1), + App: uint64(index), + }, + }) + } + } + } + + for _, br := range txn.Boxes { + index := br.Index + if index != 0 { + app := txn.ForeignApps[index-1] + index = uint64(slices.IndexFunc(txn.Access, func(rr transactions.ResourceRef) bool { + return rr.App == app + })) + } + txn.Access = append(txn.Access, transactions.ResourceRef{ + Box: transactions.BoxRef{ + Index: index, + Name: br.Name, + }, + }) + } + txn.Accounts = nil + txn.ForeignAssets = nil + txn.ForeignApps = nil + txn.Boxes = nil + return txn +} + func (r *resources) String() string { sb := strings.Builder{} if len(r.createdAsas) > 0 { @@ -784,7 +872,7 @@ byte "ALGO" testApp(t, text, now) testApp(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), now) testApp(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui02\"", -1), now, - "invalid Account reference") + "unavailable Account") // check reading state of other app ledger.NewApp(now.TxnGroup[0].Txn.Sender, 56, basics.AppParams{}) @@ -818,7 +906,7 @@ byte "ALGO" exp(3, "app_local_get arg 0 wanted type uint64...")) testApp(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01\"", -1), now) testApp(t, strings.Replace(text, "int 0 // account idx", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui02\"", -1), now, - "invalid Account reference") + "unavailable Account") // check app_local_get default value text = `int 0 // account idx @@ -847,7 +935,7 @@ int 100; byte 0x0201; == // types mismatch so this will fail _, err := testApp(t, badsource, nil, "cannot compare") attrs := basics.Attributes(err) zeros := [256]int{} - scratch := convertSlice(zeros[:], func(i int) any { return uint64(i) }) + scratch := util.Map(zeros[:], func(i int) any { return uint64(i) }) scratch[10] = uint64(5) scratch[15] = []byte{0x01, 0x02, 0x03, 0x00} require.Equal(t, map[string]any{ @@ -871,7 +959,7 @@ int 4; store 2 // store an int byte "jj"; store 3 // store a bytes int 1 ` - gscratch := convertSlice(zeros[:], func(i int) any { return uint64(i) }) + gscratch := util.Map(zeros[:], func(i int) any { return uint64(i) }) gscratch[2] = uint64(4) gscratch[3] = []byte("jj") @@ -1034,320 +1122,297 @@ int 4141 0, 100, now) } -const assetsTestTemplate = `int 0//account +const assetsTestTemplate = ` +int 0//account int 55 -asset_holding_get AssetBalance -! -bnz error -int 123 -== +asset_holding_get AssetBalance; assert +int 123; ==; assert + int 0//account int 55 -asset_holding_get AssetFrozen -! -bnz error -int 1 -== -&& -int 0//params -asset_params_get AssetTotal -! -bnz error -int 1000 -== -&& -int 0//params -asset_params_get AssetDecimals -! -bnz error -int 2 -== -&& -int 0//params -asset_params_get AssetDefaultFrozen -! -bnz error -int 0 -== -&& -int 0//params -asset_params_get AssetUnitName -! -bnz error -byte "ALGO" -== -&& -int 0//params -asset_params_get AssetName -! -bnz error -len -int 0 -== -&& -int 0//params -asset_params_get AssetURL -! -bnz error -txna ApplicationArgs 0 -== -&& -int 0//params -asset_params_get AssetMetadataHash -! -bnz error -byte 0x0000000000000000000000000000000000000000000000000000000000000000 -== -&& -int 0//params -asset_params_get AssetManager -! -bnz error -txna Accounts 0 -== -&& -int 0//params -asset_params_get AssetReserve -! -bnz error -txna Accounts 1 -== -&& -int 0//params -asset_params_get AssetFreeze -! -bnz error -txna Accounts 1 -== -&& -int 0//params -asset_params_get AssetClawback -! -bnz error -txna Accounts 1 -== -&& -bnz ok -error: -err -ok: +asset_holding_get AssetFrozen; assert +int 1; ==; assert + +int 0//asset +asset_params_get AssetTotal; assert +int 1000; ==; assert + +int 0//asset +asset_params_get AssetDecimals; assert +int 2; ==; assert + +int 0//asset +asset_params_get AssetDefaultFrozen; assert +int 0; ==; assert + +int 0//asset +asset_params_get AssetUnitName; assert +byte "ALGO"; ==; assert + +int 0//asset +asset_params_get AssetName; assert +len; int 0; ==; assert + +int 0//asset +asset_params_get AssetURL; assert +txna ApplicationArgs 0; ==; assert + +int 0//asset +asset_params_get AssetMetadataHash; assert +byte 0x0000000000000000000000000000000000000000000000000000000000000000; ==; assert + +int 0//asset +asset_params_get AssetManager; assert +txna Accounts 0; ==; assert // even with tx.Access, this means Sender + +int 0//asset +asset_params_get AssetReserve; assert +txna Accounts 1; ==; assert // this won't work when we use tx.Access, see substitute() call + +int 0//asset +asset_params_get AssetFreeze; assert +txna Accounts 1; ==; assert + +int 0//asset +asset_params_get AssetClawback; assert +txna Accounts 1; ==; assert + %s + int 1 ` +// v5extras adds test for AssetCreator, new in v5 const v5extras = ` -int 0//params -asset_params_get AssetCreator -pop -txn Sender -== -assert +int 0//asset +asset_params_get AssetCreator; assert +txn Sender; ==; assert ` +func substitute(s string, replacements map[string]string) string { + for old, new := range replacements { + s = strings.ReplaceAll(s, old, new) + } + return s +} + 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) + 4: fmt.Sprintf(assetsTestTemplate, ""), + 5: fmt.Sprintf(assetsTestTemplate, v5extras), + sharedResourcesVersion: fmt.Sprintf(assetsTestTemplate, v5extras), + LogicVersion: fmt.Sprintf(assetsTestTemplate, v5extras), } -} -func testAssetsByVersion(t *testing.T, assetsTestProgram string, version uint64) { - for _, field := range assetHoldingFieldNames { - fs := assetHoldingFieldSpecByName[field] - if fs.version <= version && !strings.Contains(assetsTestProgram, field) { - t.Errorf("TestAssets missing field %v", field) + testAssetsByVersion := func(t *testing.T, assetsTestProgram string, version uint64) { + for _, field := range assetHoldingFieldNames { + fs := assetHoldingFieldSpecByName[field] + if fs.version <= version && !strings.Contains(assetsTestProgram, field) { + t.Errorf("TestAssets missing field %v", field) + } } - } - for _, field := range assetParamsFieldNames { - fs := assetParamsFieldSpecByName[field] - if fs.version <= version && !strings.Contains(assetsTestProgram, field) { - t.Errorf("TestAssets missing field %v", field) + for _, field := range assetParamsFieldNames { + fs := assetParamsFieldSpecByName[field] + if fs.version <= version && !strings.Contains(assetsTestProgram, field) { + t.Errorf("TestAssets missing field %v", field) + } } - } - - txn := makeSampleAppl(888) - pre := defaultAppParamsWithVersion(directRefEnabledVersion-1, txn) - require.GreaterOrEqual(t, version, uint64(directRefEnabledVersion)) - now := defaultAppParamsWithVersion(version, txn) - ledger := NewLedger( - map[basics.Address]uint64{ - txn.Txn.Sender: 1, - }, - ) - pre.Ledger = ledger - now.Ledger = ledger - - // bear in mind: the sample transaction has ForeignAccounts{55,77} - testApp(t, "int 5; int 55; asset_holding_get AssetBalance", now, "invalid Account reference 5") - // was legal to get balance on a non-ForeignAsset - testApp(t, "int 0; int 54; asset_holding_get AssetBalance; ==", pre) - // but not since directRefEnabledVersion - testApp(t, "int 0; int 54; asset_holding_get AssetBalance", now, "unavailable Asset 54") - - // it wasn't legal to use a direct ref for account - testProg(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 54; asset_holding_get AssetBalance`, - directRefEnabledVersion-1, exp(1, "asset_holding_get AssetBalance arg 0 wanted type uint64...")) - // but it is now (empty asset yields 0,0 on stack) - testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 55; asset_holding_get AssetBalance; ==`, now) - // This is receiver, who is in Assets array - testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01"; int 55; asset_holding_get AssetBalance; ==`, now) - // But this is not in Assets, so illegal - testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui02"; int 55; asset_holding_get AssetBalance; ==`, now, "invalid") - - // for params get, presence in ForeignAssets has always be required - testApp(t, "int 5; asset_params_get AssetTotal", pre, "Asset index 5 beyond") - testApp(t, "int 5; asset_params_get AssetTotal", now, "unavailable Asset 5") - - params := basics.AssetParams{ - Total: 1000, - Decimals: 2, - DefaultFrozen: false, - UnitName: "ALGO", - AssetName: "", - URL: string(protocol.PaymentTx), - Manager: txn.Txn.Sender, - Reserve: txn.Txn.Receiver, - Freeze: txn.Txn.Receiver, - Clawback: txn.Txn.Receiver, - } - ledger.NewAsset(txn.Txn.Sender, 55, params) - ledger.NewHolding(txn.Txn.Sender, 55, 123, true) - // For consistency you can now use an indirect ref in holding_get - // (recall ForeignAssets[0] = 55, which has balance 123) - testApp(t, "int 0; int 0; asset_holding_get AssetBalance; int 1; ==; assert; int 123; ==", now) - // but previous code would still try to read ASA 0 - testApp(t, "int 0; int 0; asset_holding_get AssetBalance; int 0; ==; assert; int 0; ==", pre) - - testApp(t, assetsTestProgram, now) - - // In current versions, can swap out the account index for the account - testApp(t, strings.Replace(assetsTestProgram, "int 0//account", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), now) - // Or an asset index for the asset id - testApp(t, strings.Replace(assetsTestProgram, "int 0//params", "int 55", -1), now) - // Or an index for the asset id - testApp(t, strings.Replace(assetsTestProgram, "int 55", "int 0", -1), now) - - // but old code cannot - testProg(t, strings.Replace(assetsTestProgram, "int 0//account", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\"", -1), directRefEnabledVersion-1, exp(3, "asset_holding_get AssetBalance arg 0 wanted type uint64...")) + txn := makeSampleAppl(888) + pre := defaultAppParamsWithVersion(directRefEnabledVersion-1, txn) + require.GreaterOrEqual(t, version, uint64(directRefEnabledVersion)) + preCross := defaultAppParamsWithVersion(directRefEnabledVersion-1, txn) + convertEPToAccess(preCross, true) + preSimple := defaultAppParamsWithVersion(directRefEnabledVersion-1, txn) + convertEPToAccess(preSimple, false) + + now := defaultAppParamsWithVersion(version, txn) + // Make an ep that has the transactions with all the basic stuff AND implied cross products + nowCross := defaultAppParamsWithVersion(version, txn) + convertEPToAccess(nowCross, true) + // Make an ep that ONLY puts the base types into tx.Access, no implied cross products + nowSimple := defaultAppParamsWithVersion(version, txn) + convertEPToAccess(nowSimple, false) + ledger := NewLedger( + map[basics.Address]uint64{ + txn.Txn.Sender: 1, + }, + ) + pre.Ledger = ledger + now.Ledger = ledger + nowCross.Ledger = ledger + + // bear in mind: the sample transaction has ForeignAccounts{55,77} + testApp(t, "int 5; int 55; asset_holding_get AssetBalance", now, "invalid Account reference 5") + // the foreign arrays are converted into tx.Access for nowCross. 5 is not an Address in tx.Access + testApp(t, "int 5; int 55; asset_holding_get AssetBalance", nowCross, "address reference 5 is not an Address") + testApp(t, "int 5; int 55; asset_holding_get AssetBalance", nowSimple, "address reference 5 is not an Address") + testApp(t, "int 50; int 55; asset_holding_get AssetBalance", nowCross, "invalid Account reference 5") // too big + testApp(t, "int 50; int 55; asset_holding_get AssetBalance", nowSimple, "invalid Account reference 5") // too big + // was legal to get asset balance on a non-ForeignAsset + testApp(t, "int 0; int 54; asset_holding_get AssetBalance; ==", pre) + // but if using tx.Access we require explicitness, even back in v3 + testApp(t, "int 0; int 54; asset_holding_get AssetBalance; ==", preCross, + "is not in tx.Access") + testApp(t, "int 0; int 54; asset_holding_get AssetBalance; ==", preSimple, + "is not in tx.Access") + // Even if the ASSET is in tx.Access, you must have the HOLDING + testApp(t, "int 0; int 55; asset_holding_get AssetBalance; ==", preCross) + testApp(t, "int 0; int 55; asset_holding_get AssetBalance; ==", preSimple, + "is not in tx.Access") + // after directRefEnabledVersion, the asset must be included (whether using Foreign or Access) + testApp(t, "int 0; int 54; asset_holding_get AssetBalance", now, "unavailable Asset 54") + testApp(t, "int 0; int 54; asset_holding_get AssetBalance", nowCross, "unavailable Asset 54") + testApp(t, "int 0; int 54; asset_holding_get AssetBalance", nowSimple, "unavailable Asset 54") + + // it wasn't legal to use a direct ref for account + testProg(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 54; asset_holding_get AssetBalance`, + directRefEnabledVersion-1, exp(1, "asset_holding_get AssetBalance arg 0 wanted type uint64...")) + // but it is now (empty asset yields 0,0 on stack) + testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 55; asset_holding_get AssetBalance; ==`, now) + testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 55; asset_holding_get AssetBalance; ==`, nowCross) + testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"; int 55; asset_holding_get AssetBalance; ==`, nowSimple, + "unavailable Holding MFXWK5LJMFXWK5LJMFXWK5LJMFXWK5LJMFXWK5LJMFXWK5LJGAYG54XDH4 x 55") + // This is receiver, who is in Accounts array + testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01"; int 55; asset_holding_get AssetBalance; ==`, now) + testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01"; int 55; asset_holding_get AssetBalance; ==`, nowCross) + testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui01"; int 55; asset_holding_get AssetBalance; ==`, nowSimple, + "unavailable Holding MFXWK5LJMFXWK5LJMFXWK5LJMFXWK5LJMFXWK5LJMFXWK5LJGAY62VUCHY x 55") + // But this address is not in Accounts, so illegal + testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui02"; int 55; asset_holding_get AssetBalance; ==`, now, "unavailable Account") + testApp(t, `byte "aoeuiaoeuiaoeuiaoeuiaoeuiaoeui02"; int 55; asset_holding_get AssetBalance; ==`, nowCross, "unavailable Account") + + // for params get, presence in ForeignAssets has always be required + testApp(t, "int 6; asset_params_get AssetTotal", pre, "Asset index 6 beyond") + testApp(t, "int 6; asset_params_get AssetTotal", now, "unavailable Asset 6") + testApp(t, "int 6; asset_params_get AssetTotal", nowCross, "unavailable Asset 6") + testApp(t, "int 6; asset_params_get AssetTotal", nowSimple, "unavailable Asset 6") + + params := basics.AssetParams{ + Total: 1000, + Decimals: 2, + DefaultFrozen: false, + UnitName: "ALGO", + AssetName: "", + URL: string(protocol.PaymentTx), + Manager: txn.Txn.Sender, + Reserve: txn.Txn.Receiver, + Freeze: txn.Txn.Receiver, + Clawback: txn.Txn.Receiver, + } - if version < 5 { - // Can't run these with AppCreator anyway - testApp(t, strings.Replace(assetsTestProgram, "int 0//params", "int 55", -1), pre, "Asset index 55 beyond") - testApp(t, strings.Replace(assetsTestProgram, "int 55", "int 0", -1), pre, "err opcode") - } + ledger.NewAsset(txn.Txn.Sender, 55, params) + ledger.NewHolding(txn.Txn.Sender, 55, 123, true) + // For consistency you can now use an indirect ref in holding_get + // (recall ForeignAssets[0] = 55, which has balance 123) + testApp(t, "int 0; int 0; asset_holding_get AssetBalance; int 1; ==; assert; int 123; ==", now) + // (recall Access[1] = 55, which has balance 123, tx.Access slots are always 1 based, so use 2. + testApp(t, "int 0; int 2; asset_holding_get AssetBalance; int 1; ==; assert; int 123; ==", nowCross) + // but previous code would still try to read ASA 0 + testApp(t, "int 0; int 0; asset_holding_get AssetBalance; int 0; ==; assert; int 0; ==", pre) + + testApp(t, assetsTestProgram, now) + // To run it with tx.Access, use slot #2 for the asset, and directly use the Receiver + assetsTestProgramA := substitute(assetsTestProgram, map[string]string{ + "int 0//asset": "int 2//asset", + "txna Accounts 1": "txn Receiver", + }) + testApp(t, assetsTestProgramA, nowCross) + + // In current versions, can swap out the account index for the account + testApp(t, strings.ReplaceAll(assetsTestProgram, "int 0//account", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\""), now) + testApp(t, strings.ReplaceAll(assetsTestProgramA, "int 0//account", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\""), nowCross) + // Or an asset index for the asset id + testApp(t, strings.ReplaceAll(assetsTestProgram, "int 0//asset", "int 55"), now) + testApp(t, strings.ReplaceAll(assetsTestProgramA, "int 2//asset", "int 55"), nowCross) + // Or an index for the asset id + testApp(t, strings.ReplaceAll(assetsTestProgram, "int 55", "int 0"), now) + testApp(t, strings.ReplaceAll(assetsTestProgramA, "int 55", "int 2"), nowCross) + + // but old code cannot + testProg(t, strings.ReplaceAll(assetsTestProgram, "int 0//account", "byte \"aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00\""), directRefEnabledVersion-1, exp(4, "asset_holding_get AssetBalance arg 0 wanted type uint64...")) + + if version < 5 { + // Can't run these with AppCreator anyway + testApp(t, strings.ReplaceAll(assetsTestProgram, "int 0//asset", "int 55"), pre, "Asset index 55 beyond") + testApp(t, strings.ReplaceAll(assetsTestProgram, "int 55", "int 0"), pre, "assert failed pc=53") // AssetBalance => 0,0 + } - // check holdings bool value - source := `intcblock 0 55 1 + // check asset_holdings bool value + source := `intcblock 0 55 1 intc_0 // 0, account idx (txn.Sender) intc_1 // 55 -asset_holding_get AssetFrozen -! -bnz error +asset_holding_get AssetFrozen; assert intc_0 // 0 == -bnz ok -error: -err -ok: -intc_2 // 1 ` - ledger.NewHolding(txn.Txn.Sender, 55, 123, false) - testApp(t, source, now) - - // check holdings invalid offsets - ops := testProg(t, source, version) - require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_holding_get"].Opcode, ops.Program[8]) - ops.Program[9] = 0x02 - _, err := EvalApp(ops.Program, 0, 888, now) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid asset_holding_get field 2") - - // check holdings bool value - source = `intcblock 0 1 + ledger.NewHolding(txn.Txn.Sender, 55, 123, false) + testApp(t, source, now) + testApp(t, source, nowCross) + + // check asset_holding_get with invalid field number + ops := testProg(t, source, version) + require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_holding_get"].Opcode, ops.Program[8]) + ops.Program[9] = 0x02 + _, err := EvalApp(ops.Program, 0, 888, now) + require.ErrorContains(t, err, "invalid asset_holding_get field 2") + + // check asset_params bool value + source = `intcblock 0 1 intc_0 -asset_params_get AssetDefaultFrozen -! -bnz error +asset_params_get AssetDefaultFrozen; assert intc_1 == -bnz ok -error: -err -ok: -intc_1 ` - params.DefaultFrozen = true - ledger.NewAsset(txn.Txn.Sender, 55, params) - testApp(t, source, now) - // check holdings invalid offsets - ops = testProg(t, source, version) - require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_params_get"].Opcode, ops.Program[6]) - ops.Program[7] = 0x20 - _, err = EvalApp(ops.Program, 0, 888, now) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid asset_params_get field 32") - - // check empty string - source = `intcblock 0 1 -intc_0 // foreign asset idx (txn.ForeignAssets[0]) -asset_params_get AssetURL -! -bnz error -len -intc_0 -== -bnz ok -error: -err -ok: -intc_1 + params.DefaultFrozen = true + ledger.NewAsset(txn.Txn.Sender, 55, params) + testApp(t, source, now) + // check asset_params_get with invalid field number + ops = testProg(t, source, version) + require.Equal(t, OpsByName[now.Proto.LogicSigVersion]["asset_params_get"].Opcode, ops.Program[6]) + ops.Program[7] = 0x20 + _, err = EvalApp(ops.Program, 0, 888, now) + require.ErrorContains(t, err, "invalid asset_params_get field 32") + + // check empty string + source = ` +int 0 // foreign asset idx (txn.ForeignAssets[0]) +asset_params_get AssetURL; assert +len; ! ` - params.URL = "" - ledger.NewAsset(txn.Txn.Sender, 55, params) - testApp(t, source, now) + params.URL = "" + ledger.NewAsset(txn.Txn.Sender, 55, params) + testApp(t, source, now) - source = `intcblock 1 9 + source = `intcblock 1 9 intc_0 // foreign asset idx (txn.ForeignAssets[1]) -asset_params_get AssetURL -! -bnz error +asset_params_get AssetURL; assert len intc_1 == -bnz ok -error: -err -ok: -intc_0 ` - params.URL = "foobarbaz" - ledger.NewAsset(txn.Txn.Sender, 77, params) - testApp(t, source, now) + params.URL = "foobarbaz" + ledger.NewAsset(txn.Txn.Sender, 77, params) + testApp(t, source, now) - source = `intcblock 0 1 -intc_0 -asset_params_get AssetURL + source = ` +int 0 +asset_params_get AssetURL; assert ! -bnz error -intc_0 -== -bnz ok -error: -err -ok: -intc_1 ` - params.URL = "" - ledger.NewAsset(txn.Txn.Sender, 55, params) - testApp(t, notrack(source), now, "cannot compare ([]byte to uint64)") + params.URL = "" + ledger.NewAsset(txn.Txn.Sender, 55, params) + testApp(t, notrack(source), now, "! arg 0 wanted uint64") + } + + for v, source := range tests { + t.Run(fmt.Sprintf("v%d", v), func(t *testing.T) { + testAssetsByVersion(t, source, v) + }) + } } // TestAssetDisambiguation ensures we have a consistent interpretation of low @@ -1385,7 +1450,7 @@ func TestAssetDisambiguation(t *testing.T) { tx.ForeignAssets = []basics.AssetIndex{1, 256} if ep.Proto.LogicSigVersion < directRefEnabledVersion { - // There's no direct use of assets IDs, so 1 is still the 1th slot (256) + // direct use of assets IDs id disallowed, so 1 is still the 1th slot (256) testApp(t, `int 1; asset_params_get AssetName; assert; byte "thirty"; ==`, ep) } else { // Since 1 IS available, 1 means the assetid=1, not the 1th slot @@ -1405,7 +1470,7 @@ func TestAssetDisambiguation(t *testing.T) { testApp(t, `int 0; int 1; asset_holding_get AssetBalance; assert; int 256; ==`, ep) } - // but now if that resolution led to a number below 255, boom + // but now if that resolution led to a number below 256, boom tx.ForeignAssets = []basics.AssetIndex{256, 255} testApp(t, `int 1; asset_params_get AssetName; assert; byte "thirty"; ==`, ep, "low Asset lookup 255") @@ -2916,7 +2981,7 @@ func TestUnnamedResourceAccess(t *testing.T) { tc.policy.events = nil } } else { - testApp(t, source, ep, fmt.Sprintf("invalid Account reference %s", otherAccount)) + testApp(t, source, ep, fmt.Sprintf("unavailable Account %s", otherAccount)) } // Unaccessible app @@ -2988,12 +3053,7 @@ func TestUnnamedResourceAccess(t *testing.T) { tc.policy.events = nil } } else { - problem := "unavailable Account %s" - if ep.Proto.LogicSigVersion < 9 { - // Message is difference before sharedResourcesVersion - problem = "invalid Account reference %s" - } - testApp(t, source, ep, fmt.Sprintf(problem, otherAccount)) + testApp(t, source, ep, fmt.Sprintf("unavailable Account %s", otherAccount)) } // Unaccessible asset @@ -3051,12 +3111,7 @@ func TestUnnamedResourceAccess(t *testing.T) { tc.policy.events = nil } } else { - problem := "unavailable Account %s" - if ep.Proto.LogicSigVersion < 9 { - // Message is different before sharedResourcesVersion - problem = "invalid Account reference %s" - } - testApp(t, source, ep, fmt.Sprintf(problem, otherAccount)) + testApp(t, source, ep, fmt.Sprintf("unavailable Account %s", otherAccount)) } // Unaccessible box @@ -3972,15 +4027,21 @@ func TestTxnaLimits(t *testing.T) { t.Parallel() // txna came in v2, but Apps and Assets in v3. TestLogicRange(t, 3, 0, func(t *testing.T, ep *EvalParams, tx *transactions.Transaction, ledger *Ledger) { - testApp(t, "txna Accounts "+strconv.Itoa(len(tx.Accounts))+";len", ep) - testApp(t, "txna Accounts "+strconv.Itoa(len(tx.Accounts)+1)+";len", ep, "invalid Accounts index") + if len(tx.Accounts) > 0 { // With tx.Access, sample txn has no Accounts + testApp(t, "txna Accounts "+strconv.Itoa(len(tx.Accounts))+";len", ep) + testApp(t, "txna Accounts "+strconv.Itoa(len(tx.Accounts)+1)+";len", ep, "invalid Accounts index") + } - testApp(t, "txna Applications "+strconv.Itoa(len(tx.ForeignApps)), ep) - testApp(t, "txna Applications "+strconv.Itoa(len(tx.ForeignApps)+1), ep, "invalid Applications index") + if len(tx.ForeignApps) > 0 { // With tx.Access, sample txn has no ForeignApps + testApp(t, "txna Applications "+strconv.Itoa(len(tx.ForeignApps)), ep) + testApp(t, "txna Applications "+strconv.Itoa(len(tx.ForeignApps)+1), ep, "invalid Applications index") + } // Assets and AppArgs have no implicit 0 index, so everything shifts - testApp(t, "txna Assets "+strconv.Itoa(len(tx.ForeignAssets)-1), ep) - testApp(t, "txna Assets "+strconv.Itoa(len(tx.ForeignAssets)), ep, "invalid Assets index") + if len(tx.ForeignApps) > 0 { // With tx.Access, sample txn has no ForeignApps + testApp(t, "txna Assets "+strconv.Itoa(len(tx.ForeignAssets)-1), ep) + testApp(t, "txna Assets "+strconv.Itoa(len(tx.ForeignAssets)), ep, "invalid Assets index") + } testApp(t, "txna ApplicationArgs "+strconv.Itoa(len(tx.ApplicationArgs)-1)+";len", ep) testApp(t, "txna ApplicationArgs "+strconv.Itoa(len(tx.ApplicationArgs))+";len", ep, "invalid ApplicationArgs index") diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index f5bb656ef2..1251e69ca1 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -39,6 +39,7 @@ import ( "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" + "github.com/algorand/go-algorand/util" "pgregory.net/rapid" ) @@ -1087,7 +1088,7 @@ func TestTxnBadField(t *testing.T) { testLogicBytes(t, program, nil, "invalid txn field") // TODO: Check should know the type stack was wrong - // test txn does not accept ApplicationArgs and Accounts + // test that `txn` does not accept ApplicationArgs and Accounts, `txna` is necessary txnOpcode := OpsByName[LogicVersion]["txn"].Opcode txnaOpcode := OpsByName[LogicVersion]["txna"].Opcode @@ -1835,8 +1836,7 @@ func makeSampleTxn() transactions.SignedTxn { txn.Txn.AssetReceiver = txn.Txn.CloseRemainderTo txn.Txn.AssetCloseTo = txn.Txn.Sender txn.Txn.ApplicationID = basics.AppIndex(888) - txn.Txn.Accounts = make([]basics.Address, 1) - txn.Txn.Accounts[0] = txn.Txn.Receiver + txn.Txn.Accounts = []basics.Address{txn.Txn.Receiver} rekeyToAddr := []byte("aoeuiaoeuiaoeuiaoeuiaoeuiaoeui05") metadata := []byte("aoeuiaoeuiaoeuiaoeuiaoeuiaoeuiHH") managerAddr := []byte("aoeuiaoeuiaoeuiaoeuiaoeuiaoeui06") @@ -2811,7 +2811,7 @@ int 100; byte 0x0201; == // types mismatch so this will fail err := testPanics(t, badsource, 1, "cannot compare") attrs := basics.Attributes(err) zeros := [256]int{} - scratch := convertSlice(zeros[:], func(i int) any { return uint64(i) }) + scratch := util.Map(zeros[:], func(i int) any { return uint64(i) }) scratch[10] = uint64(5) scratch[15] = []byte{0x01, 0x02, 0x03, 0x00} require.Equal(t, map[string]any{ @@ -2830,7 +2830,7 @@ int 4; store 2 // store an int byte "jj"; store 3 // store a bytes int 1 ` - gscratch := convertSlice(zeros[:], func(i int) any { return uint64(i) }) + gscratch := util.Map(zeros[:], func(i int) any { return uint64(i) }) gscratch[2] = uint64(4) gscratch[3] = []byte("jj") diff --git a/data/transactions/logic/export_test.go b/data/transactions/logic/export_test.go index 82801007e4..a293e2342d 100644 --- a/data/transactions/logic/export_test.go +++ b/data/transactions/logic/export_test.go @@ -41,6 +41,7 @@ func (l *Ledger) DelBoxes(app basics.AppIndex, names ...string) { } } +var ConvertEPToAccess = convertEPToAccess var DefaultSigParams = defaultSigParams var DefaultAppParams = defaultAppParams var Exp = exp @@ -61,6 +62,7 @@ var WithPanicOpcode = withPanicOpcode // can't export call this "TestApps" because it looks like a Test function with // the wrong signature. But we can get that effect with the alias below. func TryApps(t *testing.T, programs []string, txgroup []transactions.SignedTxn, ver uint64, ledger *Ledger, expected ...expect) (*EvalParams, error) { + t.Helper() return testApps(t, programs, txgroup, protoVer(ver), ledger, expected...) } diff --git a/data/transactions/logic/resources.go b/data/transactions/logic/resources.go index 95c693c85a..88c1ed45bb 100644 --- a/data/transactions/logic/resources.go +++ b/data/transactions/logic/resources.go @@ -53,7 +53,7 @@ type resources struct { // of the box - has it been modified in this txngroup? If yes, the size of // the box counts against the group writeBudget. So delete is NOT a dirtying // operation. - boxes map[BoxRef]bool + boxes map[basics.BoxRef]bool // dirtyBytes maintains a running count of the number of dirty bytes in `boxes` dirtyBytes uint64 @@ -74,6 +74,18 @@ func (r *resources) shareLocal(addr basics.Address, id basics.AppIndex) { r.sharedLocals[ledgercore.AccountApp{Address: addr, App: id}] = struct{}{} } +func (r *resources) shareBox(br basics.BoxRef, current basics.AppIndex) { + if br.App == 0 { + // "current app": Ignore if this is a create, else use ApplicationID + if current == 0 { + // When the create actually happens, and we learn the appID, we'll add it. + return + } + br.App = current + } + r.boxes[br] = false +} + // In the fill* and allows* routines, we pass the header and the fields in // separately, even though they are pointers into the same structure. That // prevents dumb attempts to use other fields from the transaction. @@ -280,6 +292,47 @@ func (cx *EvalContext) allowsAssetFreeze(hdr *transactions.Header, tx *transacti } func (r *resources) fillApplicationCall(ep *EvalParams, hdr *transactions.Header, tx *transactions.ApplicationCallTxnFields) { + if tx.Access != nil { + r.fillApplicationCallAccess(ep, hdr, tx) + } else { + r.fillApplicationCallForeign(ep, hdr, tx) + } +} + +func (r *resources) fillApplicationCallAccess(ep *EvalParams, hdr *transactions.Header, tx *transactions.ApplicationCallTxnFields) { + // The only implicitly available things are the sender, the app, and the sender's locals + r.sharedAccounts[hdr.Sender] = struct{}{} + if tx.ApplicationID != 0 { + r.sharedApps[tx.ApplicationID] = struct{}{} + r.shareLocal(hdr.Sender, tx.ApplicationID) + } + + // Access is a explicit list of resources that should be made "available" + for _, rr := range tx.Access { + switch { + case !rr.Address.IsZero(): + r.sharedAccounts[rr.Address] = struct{}{} + case rr.Asset != 0: + r.sharedAsas[rr.Asset] = struct{}{} + case rr.App != 0: + r.sharedApps[rr.App] = struct{}{} + case !rr.Holding.Empty(): + // ApplicationCallTxnFields.wellFormed ensures no error here. + address, asset, _ := rr.Holding.Resolve(tx.Access, hdr.Sender) + r.shareHolding(address, asset) + case !rr.Locals.Empty(): + // ApplicationCallTxnFields.wellFormed ensures no error here. + address, app, _ := rr.Locals.Resolve(tx.Access, hdr.Sender) + r.shareLocal(address, app) + case !rr.Box.Empty(): + // ApplicationCallTxnFields.wellFormed ensures no error here. + app, name, _ := rr.Box.Resolve(tx.Access) + r.shareBox(basics.BoxRef{App: app, Name: name}, tx.ApplicationID) + } + } +} + +func (r *resources) fillApplicationCallForeign(ep *EvalParams, hdr *transactions.Header, tx *transactions.ApplicationCallTxnFields) { txAccounts := make([]basics.Address, 0, 2+len(tx.Accounts)+len(tx.ForeignApps)) txAccounts = append(txAccounts, hdr.Sender) txAccounts = append(txAccounts, tx.Accounts...) @@ -315,21 +368,14 @@ func (r *resources) fillApplicationCall(ep *EvalParams, hdr *transactions.Header } for _, br := range tx.Boxes { - var app basics.AppIndex - if br.Index == 0 { - // "current app": Ignore if this is a create, else use ApplicationID - if tx.ApplicationID == 0 { - // When the create actually happens, and we learn the appID, we'll add it. - continue - } - app = tx.ApplicationID - } else { - // Bounds check will already have been done by + app := basics.AppIndex(0) // 0 can be handled by shareBox as current + if br.Index != 0 { + // Upper bounds check will already have been done by // WellFormed. For testing purposes, it's better to panic // now than after returning a nil. - app = tx.ForeignApps[br.Index-1] // shift for the 0=this convention + app = tx.ForeignApps[br.Index-1] // shift for the 0=current convention } - r.boxes[BoxRef{app, string(br.Name)}] = false + r.shareBox(basics.BoxRef{App: app, Name: string(br.Name)}, tx.ApplicationID) } } diff --git a/data/transactions/logic/resources_test.go b/data/transactions/logic/resources_test.go index 8d0b2456f4..48325ef01d 100644 --- a/data/transactions/logic/resources_test.go +++ b/data/transactions/logic/resources_test.go @@ -109,8 +109,8 @@ func TestAppSharing(t *testing.T) { Exp(1, "unavailable Local State "+appl1.Sender.String())) // But it's ok in appl2, because appl2 uses the same Sender, even though the - // foreign-app is not repeated in appl2 because the holding being accessed - // is the one from tx0. + // localref is not repeated in appl2 because the locals being accessed is + // the one from tx0. TestApps(t, []string{optInCheck500, optInCheck500}, txntest.Group(&appl0, &appl2), 9, ledger) TestApps(t, []string{optInCheck500, optInCheck500}, txntest.Group(&appl0, &appl2), 8, ledger, // version 8 does not get sharing Exp(1, "unavailable App 500")) @@ -183,12 +183,215 @@ func TestAppSharing(t *testing.T) { sources = []string{`gtxn 1 Sender; byte "key"; byte "val"; app_local_put; int 1`} TestApps(t, sources, txntest.Group(&appl0, &appl1), 9, ledger) TestApps(t, sources, txntest.Group(&appl0, &appl1), 8, ledger, // 8 doesn't share the account - Exp(0, "invalid Account reference "+appl1.Sender.String())) + Exp(0, "unavailable Account "+appl1.Sender.String())) + // same for app_local_del + sources = []string{`gtxn 1 Sender; byte "key"; app_local_del; int 1`} + TestApps(t, sources, txntest.Group(&appl0, &appl1), 9, ledger) + TestApps(t, sources, txntest.Group(&appl0, &appl1), 8, ledger, // 8 doesn't share the account + Exp(0, "unavailable Account "+appl1.Sender.String())) +} + +// TestAppAccess confirms availablility of apps using tx.Access +func TestAppAccess(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + // Create some sample transactions. The main reason this a blackbox test + // (_test package) is to have access to txntest. + appl0 := txntest.Txn{ + Type: protocol.ApplicationCallTx, + ApplicationID: 900, + Sender: basics.Address{1, 2, 3, 4}, + Access: []transactions.ResourceRef{{ + App: 500, + }, { + Locals: transactions.LocalsRef{ + Address: 0, + App: 1, + }, + }}, + } + + appl0noLocals := txntest.Txn{ + Type: protocol.ApplicationCallTx, + ApplicationID: 900, + Sender: basics.Address{1, 2, 3, 4}, + Access: []transactions.ResourceRef{{ + App: 500, + }}, + } + + appl1 := txntest.Txn{ + Type: protocol.ApplicationCallTx, + ApplicationID: 901, + Sender: basics.Address{4, 3, 2, 1}, + } + + appl2 := txntest.Txn{ + Type: protocol.ApplicationCallTx, + ApplicationID: 902, + Sender: basics.Address{1, 2, 3, 4}, + } + + pay1 := txntest.Txn{ + Type: protocol.PaymentTx, + Sender: basics.Address{5, 5, 5, 5}, + Receiver: basics.Address{6, 6, 6, 6}, + } + + getSchema := "int 500; app_params_get AppGlobalNumByteSlice; !; assert; pop; int 1" + // In v8, the first tx can read app params of 500, because it's in its + // access array, but the second can't + TestApps(t, []string{getSchema, getSchema}, txntest.Group(&appl0, &appl1), 8, nil, + Exp(1, "unavailable App 500")) + // In v9, the second can, because the first can. + TestApps(t, []string{getSchema, getSchema}, txntest.Group(&appl0, &appl1), 9, nil) + + getLocalEx := `txn Sender; int 500; byte "some-key"; app_local_get_ex; pop; pop; int 1` + + // In contrast, here there's no help from v9, because the second tx is + // reading the locals for a different account. + + // app_local_get* requires the address and the app exist, else the program fails + TestApps(t, []string{getLocalEx, getLocalEx}, txntest.Group(&appl0, &appl1), 8, nil, + Exp(0, "no account")) + + _, _, ledger := MakeSampleEnv() + ledger.NewAccount(appl0.Sender, 100_000) + ledger.NewAccount(appl1.Sender, 100_000) + ledger.NewApp(appl0.Sender, 500, basics.AppParams{}) + ledger.NewLocals(appl0.Sender, 500) // opt in + // Now txn0 passes, but txn1 has an error because it can't see app 500 + TestApps(t, []string{getLocalEx, getLocalEx}, txntest.Group(&appl0, &appl1), 9, ledger, + Exp(1, "unavailable Local State")) + + // Locals won't be available if we only listed the app in tx.Access + TestApps(t, []string{getLocalEx, getLocalEx}, txntest.Group(&appl0noLocals, &appl1), 9, ledger, + Exp(0, "unavailable Local State")) + + // But it's ok in appl2, because appl2 uses the same Sender, even though the + // foreign-app is not repeated in appl2 because the holding being accessed + // is the one from tx0. + TestApps(t, []string{getLocalEx, getLocalEx}, txntest.Group(&appl0, &appl2), 9, ledger) + TestApps(t, []string{getLocalEx, getLocalEx}, txntest.Group(&appl0, &appl2), 8, ledger, // version 8 does not get sharing + Exp(1, "unavailable App 500")) + + // Checking if an account is opted in has pretty much the same rules + optInCheck500 := "txn Sender; int 500; app_opted_in" + + // app_opted_in requires the address and the app exist, else the program fails + TestApps(t, []string{optInCheck500, optInCheck500}, txntest.Group(&appl0, &appl1), 9, nil, // nil ledger, no account + Exp(0, "no account: "+appl0.Sender.String())) + + // Now txn0 passes, but txn1 has an error because it can't see app 500 locals for appl1.Sender + TestApps(t, []string{optInCheck500, optInCheck500}, txntest.Group(&appl0, &appl1), 9, ledger, + Exp(1, "unavailable Local State "+appl1.Sender.String())) + + // But it's ok in appl2, because appl2 uses the same Sender, even though the + // foreign-app is not repeated in appl2 because the holding being accessed + // is the one from tx0. + TestApps(t, []string{optInCheck500, optInCheck500}, txntest.Group(&appl0, &appl2), 9, ledger) + TestApps(t, []string{optInCheck500, optInCheck500}, txntest.Group(&appl0, &appl2), 8, ledger, // version 8 does not get sharing + Exp(1, "unavailable App 500")) + + // Confirm sharing applies to the app id called in tx0, not just access array + optInCheck900 := "txn Sender; int 900; app_opted_in; !" // we did not opt any senders into 900 + + // as above, appl1 can't see the local state, but appl2 can b/c sender is same as appl0 + TestApps(t, []string{optInCheck900, optInCheck900}, txntest.Group(&appl0, &appl1), 9, ledger, + Exp(1, "unavailable Local State "+appl1.Sender.String())) + TestApps(t, []string{optInCheck900, optInCheck900}, txntest.Group(&appl0, &appl2), 9, ledger) + fmt.Println("unavailable Local State " + appl2.Sender.String() + " x 900 is not in tx.Access") + TestApps(t, []string{optInCheck900, optInCheck900}, txntest.Group(&appl0, &appl2), 8, ledger, // v8=no sharing + Exp(1, "unavailable App 900")) // without sharing, even the app is unavailable, let along locals + + // Now, confirm that *setting* a local state in tx1 that was made available + // in tx0 works. The extra check here is that the change is recorded + // properly in EvalDelta. + putLocal := `txn ApplicationArgs 0; byte "X"; int 74; app_local_put; int 1` + + noop := `int 1` + sources := []string{noop, putLocal} + appl1.ApplicationArgs = [][]byte{appl0.Sender[:]} // tx1 will try to modify local state exposed in tx0 + // appl0.Sender is available, but 901's local state for it isn't (only 900's is, since 900 was called in tx0) + TestApps(t, sources, txntest.Group(&appl0, &appl1), 9, ledger, + Exp(1, "unavailable Local State "+appl0.Sender.String())) + // Add 901 to tx0's Access. Still won't work because we don't include the Locals yet + appl0.Access = append(appl0.Access, transactions.ResourceRef{App: 901}) + TestApps(t, sources, txntest.Group(&appl0, &appl1), 9, ledger, + Exp(1, "unavailable Local State "+appl0.Sender.String()+" x 901")) + // Now add the LocalsRef + appl0.Access = append(appl0.Access, transactions.ResourceRef{ + Locals: transactions.LocalsRef{ + Address: 0, + App: uint64(len(appl0.Access)), // reference 901 we added + }}) + // This error shows we have access, just not opted in. + TestApps(t, sources, txntest.Group(&appl0, &appl1), 9, ledger, + Exp(1, "account "+appl0.Sender.String()+" is not opted into 901")) + ledger.NewLocals(appl0.Sender, 901) // opt in + ep, _ := TestApps(t, sources, txntest.Group(&appl0, &appl1), 9, ledger) + require.Len(t, ep.TxnGroup, 2) + ed := ep.TxnGroup[1].ApplyData.EvalDelta + require.Equal(t, map[uint64]basics.StateDelta{ + 1: { // no tx.Accounts, 1 indicates first in SharedAccts + "X": { + Action: basics.SetUintAction, + Uint: 74, + }, + }, + }, ed.LocalDeltas) + require.Len(t, ed.SharedAccts, 1) + require.Equal(t, ep.TxnGroup[0].Txn.Sender, ed.SharedAccts[0]) + + // when running all three, appl2 can't read the locals of app in tx0 and addr in tx1 + sources = []string{"", "", "gtxn 1 Sender; gtxn 0 Applications 0; byte 0xAA; app_local_get_ex"} + TestApps(t, sources, txntest.Group(&appl0, &appl1, &appl2), 9, nil, + Exp(2, "unavailable Local State")) // note that the error message is for Locals, not specialized + // same test of an account in Access of tx1 rather than Sender, but no Locals in tx1 + junk := "J5YDZLPOHWB5O6MVRHNFGY4JXIQAYYM6NUJWPBSYBBIXH5ENQ4Z5LTJELU" + j5y, err := basics.UnmarshalChecksumAddress(junk) + require.NoError(t, err) + appl1.Access = append(appl1.Access, transactions.ResourceRef{ + Address: j5y, + }) + sources = []string{"", "", ` +addr J5YDZLPOHWB5O6MVRHNFGY4JXIQAYYM6NUJWPBSYBBIXH5ENQ4Z5LTJELU +gtxn 0 Applications 0; byte 0xAA; app_local_get_ex`} + TestApps(t, sources, txntest.Group(&appl0, &appl1, &appl2), 9, nil, + Exp(2, "unavailable Local State")) // note that the error message is for Locals, not specialized + + // try to do a put on local state of the account in tx1, but tx0 ought not have access to that local state + ledger.NewAccount(pay1.Receiver, 200_000) + ledger.NewLocals(pay1.Receiver, 900) // opt in + sources = []string{`gtxn 1 Receiver; byte "key"; byte "val"; app_local_put; int 1`} + TestApps(t, sources, txntest.Group(&appl0, &pay1), 9, ledger, + Exp(0, "unavailable Local State "+pay1.Receiver.String())) + + // same for app_local_del + sources = []string{`gtxn 1 Receiver; byte "key"; app_local_del; int 1`} + TestApps(t, sources, txntest.Group(&appl0, &pay1), 9, ledger, + Exp(0, "unavailable Local State "+pay1.Receiver.String())) + + // now, use an app call in tx1, with 900 in Access + appl1.Access = append(appl1.Access, transactions.ResourceRef{App: 900}) + ledger.NewLocals(appl1.Sender, 900) // opt in + sources = []string{`gtxn 1 Sender; byte "key"; byte "val"; app_local_put; int 1`} + // not enough: app 900 is in Access, but not the locals + TestApps(t, sources, txntest.Group(&appl0, &appl1), 9, ledger, + Exp(0, "unavailable Local State "+appl1.Sender.String())) + appl1.Access = append(appl1.Access, transactions.ResourceRef{ + Locals: transactions.LocalsRef{ + App: uint64(len(appl1.Access)), + Address: 0, + }}) + TestApps(t, sources, txntest.Group(&appl0, &appl1), 8, ledger, // 8 doesn't share the account + Exp(0, "unavailable Account "+appl1.Sender.String())) // same for app_local_del sources = []string{`gtxn 1 Sender; byte "key"; app_local_del; int 1`} TestApps(t, sources, txntest.Group(&appl0, &appl1), 9, ledger) TestApps(t, sources, txntest.Group(&appl0, &appl1), 8, ledger, // 8 doesn't share the account - Exp(0, "invalid Account reference "+appl1.Sender.String())) + Exp(0, "unavailable Account "+appl1.Sender.String())) } // TestBetterLocalErrors confirms that we get specific errors about the missing @@ -422,9 +625,9 @@ func TestOtherTxSharing(t *testing.T) { TestApps(t, []string{senderBalance, ""}, txntest.Group(&appl, &send), 9, ledger) TestApps(t, []string{"", senderBalance}, txntest.Group(&send, &appl), 8, ledger, - Exp(1, "invalid Account reference")) + Exp(1, "unavailable Account")) TestApps(t, []string{senderBalance, ""}, txntest.Group(&appl, &send), 8, ledger, - Exp(0, "invalid Account reference")) + Exp(0, "unavailable Account")) } holdingAccess := ` @@ -451,7 +654,7 @@ func TestOtherTxSharing(t *testing.T) { // The other account is not (it's not even in the pay txn) appl.ApplicationArgs = [][]byte{otherAcct[:]} TestApps(t, []string{"", otherBalance}, txntest.Group(&pay, &appl), 9, ledger, - Exp(1, "invalid Account reference "+otherAcct.String())) + Exp(1, "unavailable Account "+otherAcct.String())) // The other account becomes accessible because used in CloseRemainderTo withClose := pay @@ -463,7 +666,7 @@ func TestOtherTxSharing(t *testing.T) { // The other account is not available even though it's all the extra addresses appl.ApplicationArgs = [][]byte{otherAcct[:]} TestApps(t, []string{"", otherBalance}, txntest.Group(&acfg, &appl), 9, ledger, - Exp(1, "invalid Account reference "+otherAcct.String())) + Exp(1, "unavailable Account "+otherAcct.String())) }) t.Run("axfer", func(t *testing.T) { // nolint:paralleltest // shares `ledger` @@ -490,7 +693,7 @@ func TestOtherTxSharing(t *testing.T) { // AssetCloseTo holding becomes available when set appl.ApplicationArgs = [][]byte{other2Acct[:], {byte(axfer.XferAsset)}} TestApps(t, []string{"", other2Balance}, txntest.Group(&axfer, &appl), 9, ledger, - Exp(1, "invalid Account reference "+other2Acct.String())) + Exp(1, "unavailable Account "+other2Acct.String())) TestApps(t, []string{"", holdingAccess}, txntest.Group(&axfer, &appl), 9, ledger, Exp(1, "unavailable Account "+other2Acct.String())) diff --git a/data/transactions/msgp_gen.go b/data/transactions/msgp_gen.go index fe1d52680e..d70a57bf1f 100644 --- a/data/transactions/msgp_gen.go +++ b/data/transactions/msgp_gen.go @@ -108,6 +108,16 @@ import ( // |-----> (*) MsgIsZero // |-----> HeartbeatTxnFieldsMaxSize() // +// HoldingRef +// |-----> (*) MarshalMsg +// |-----> (*) CanMarshalMsg +// |-----> (*) UnmarshalMsg +// |-----> (*) UnmarshalMsgWithState +// |-----> (*) CanUnmarshalMsg +// |-----> (*) Msgsize +// |-----> (*) MsgIsZero +// |-----> HoldingRefMaxSize() +// // KeyregTxnFields // |-----> (*) MarshalMsg // |-----> (*) CanMarshalMsg @@ -118,6 +128,16 @@ import ( // |-----> (*) MsgIsZero // |-----> KeyregTxnFieldsMaxSize() // +// LocalsRef +// |-----> (*) MarshalMsg +// |-----> (*) CanMarshalMsg +// |-----> (*) UnmarshalMsg +// |-----> (*) UnmarshalMsgWithState +// |-----> (*) CanUnmarshalMsg +// |-----> (*) Msgsize +// |-----> (*) MsgIsZero +// |-----> LocalsRefMaxSize() +// // LogicSig // |-----> (*) MarshalMsg // |-----> (*) CanMarshalMsg @@ -158,6 +178,16 @@ import ( // |-----> MsgIsZero // |-----> PaysetMaxSize() // +// ResourceRef +// |-----> (*) MarshalMsg +// |-----> (*) CanMarshalMsg +// |-----> (*) UnmarshalMsg +// |-----> (*) UnmarshalMsgWithState +// |-----> (*) CanUnmarshalMsg +// |-----> (*) Msgsize +// |-----> (*) MsgIsZero +// |-----> ResourceRefMaxSize() +// // SignedTxn // |-----> (*) MarshalMsg // |-----> (*) CanMarshalMsg @@ -235,64 +265,80 @@ import ( func (z *ApplicationCallTxnFields) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0006Len := uint32(13) - var zb0006Mask uint16 /* 14 bits */ + zb0007Len := uint32(14) + var zb0007Mask uint16 /* 15 bits */ + if len((*z).Access) == 0 { + zb0007Len-- + zb0007Mask |= 0x2 + } if len((*z).ApplicationArgs) == 0 { - zb0006Len-- - zb0006Mask |= 0x2 + zb0007Len-- + zb0007Mask |= 0x4 } if (*z).OnCompletion == 0 { - zb0006Len-- - zb0006Mask |= 0x4 + zb0007Len-- + zb0007Mask |= 0x8 } if len((*z).ApprovalProgram) == 0 { - zb0006Len-- - zb0006Mask |= 0x8 + zb0007Len-- + zb0007Mask |= 0x10 } if len((*z).ForeignAssets) == 0 { - zb0006Len-- - zb0006Mask |= 0x10 + zb0007Len-- + zb0007Mask |= 0x20 } if len((*z).Accounts) == 0 { - zb0006Len-- - zb0006Mask |= 0x20 + zb0007Len-- + zb0007Mask |= 0x40 } if len((*z).Boxes) == 0 { - zb0006Len-- - zb0006Mask |= 0x40 + zb0007Len-- + zb0007Mask |= 0x80 } if (*z).ExtraProgramPages == 0 { - zb0006Len-- - zb0006Mask |= 0x80 + zb0007Len-- + zb0007Mask |= 0x100 } if len((*z).ForeignApps) == 0 { - zb0006Len-- - zb0006Mask |= 0x100 + zb0007Len-- + zb0007Mask |= 0x200 } if (*z).GlobalStateSchema.MsgIsZero() { - zb0006Len-- - zb0006Mask |= 0x200 + zb0007Len-- + zb0007Mask |= 0x400 } if (*z).ApplicationID.MsgIsZero() { - zb0006Len-- - zb0006Mask |= 0x400 + zb0007Len-- + zb0007Mask |= 0x800 } if (*z).LocalStateSchema.MsgIsZero() { - zb0006Len-- - zb0006Mask |= 0x800 + zb0007Len-- + zb0007Mask |= 0x1000 } if (*z).RejectVersion == 0 { - zb0006Len-- - zb0006Mask |= 0x1000 + zb0007Len-- + zb0007Mask |= 0x2000 } if len((*z).ClearStateProgram) == 0 { - zb0006Len-- - zb0006Mask |= 0x2000 + zb0007Len-- + zb0007Mask |= 0x4000 } - // variable map header, size zb0006Len - o = append(o, 0x80|uint8(zb0006Len)) - if zb0006Len != 0 { - if (zb0006Mask & 0x2) == 0 { // if not empty + // variable map header, size zb0007Len + o = append(o, 0x80|uint8(zb0007Len)) + if zb0007Len != 0 { + if (zb0007Mask & 0x2) == 0 { // if not empty + // string "al" + o = append(o, 0xa2, 0x61, 0x6c) + if (*z).Access == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len((*z).Access))) + } + for zb0005 := range (*z).Access { + o = (*z).Access[zb0005].MarshalMsg(o) + } + } + if (zb0007Mask & 0x4) == 0 { // if not empty // string "apaa" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x61) if (*z).ApplicationArgs == nil { @@ -304,17 +350,17 @@ func (z *ApplicationCallTxnFields) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendBytes(o, (*z).ApplicationArgs[zb0001]) } } - if (zb0006Mask & 0x4) == 0 { // if not empty + if (zb0007Mask & 0x8) == 0 { // if not empty // string "apan" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x6e) o = msgp.AppendUint64(o, uint64((*z).OnCompletion)) } - if (zb0006Mask & 0x8) == 0 { // if not empty + if (zb0007Mask & 0x10) == 0 { // if not empty // string "apap" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x70) o = msgp.AppendBytes(o, (*z).ApprovalProgram) } - if (zb0006Mask & 0x10) == 0 { // if not empty + if (zb0007Mask & 0x20) == 0 { // if not empty // string "apas" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x73) if (*z).ForeignAssets == nil { @@ -322,11 +368,11 @@ func (z *ApplicationCallTxnFields) MarshalMsg(b []byte) (o []byte) { } else { o = msgp.AppendArrayHeader(o, uint32(len((*z).ForeignAssets))) } - for zb0005 := range (*z).ForeignAssets { - o = (*z).ForeignAssets[zb0005].MarshalMsg(o) + for zb0003 := range (*z).ForeignAssets { + o = (*z).ForeignAssets[zb0003].MarshalMsg(o) } } - if (zb0006Mask & 0x20) == 0 { // if not empty + if (zb0007Mask & 0x40) == 0 { // if not empty // string "apat" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x74) if (*z).Accounts == nil { @@ -338,7 +384,7 @@ func (z *ApplicationCallTxnFields) MarshalMsg(b []byte) (o []byte) { o = (*z).Accounts[zb0002].MarshalMsg(o) } } - if (zb0006Mask & 0x40) == 0 { // if not empty + if (zb0007Mask & 0x80) == 0 { // if not empty // string "apbx" o = append(o, 0xa4, 0x61, 0x70, 0x62, 0x78) if (*z).Boxes == nil { @@ -346,38 +392,38 @@ func (z *ApplicationCallTxnFields) MarshalMsg(b []byte) (o []byte) { } else { o = msgp.AppendArrayHeader(o, uint32(len((*z).Boxes))) } - for zb0004 := range (*z).Boxes { + for zb0006 := range (*z).Boxes { // omitempty: check for empty values - zb0007Len := uint32(2) - var zb0007Mask uint8 /* 3 bits */ - if (*z).Boxes[zb0004].Index == 0 { - zb0007Len-- - zb0007Mask |= 0x2 - } - if len((*z).Boxes[zb0004].Name) == 0 { - zb0007Len-- - zb0007Mask |= 0x4 - } - // variable map header, size zb0007Len - o = append(o, 0x80|uint8(zb0007Len)) - if (zb0007Mask & 0x2) == 0 { // if not empty + zb0008Len := uint32(2) + var zb0008Mask uint8 /* 3 bits */ + if (*z).Boxes[zb0006].Index == 0 { + zb0008Len-- + zb0008Mask |= 0x2 + } + if len((*z).Boxes[zb0006].Name) == 0 { + zb0008Len-- + zb0008Mask |= 0x4 + } + // variable map header, size zb0008Len + o = append(o, 0x80|uint8(zb0008Len)) + if (zb0008Mask & 0x2) == 0 { // if not empty // string "i" o = append(o, 0xa1, 0x69) - o = msgp.AppendUint64(o, (*z).Boxes[zb0004].Index) + o = msgp.AppendUint64(o, (*z).Boxes[zb0006].Index) } - if (zb0007Mask & 0x4) == 0 { // if not empty + if (zb0008Mask & 0x4) == 0 { // if not empty // string "n" o = append(o, 0xa1, 0x6e) - o = msgp.AppendBytes(o, (*z).Boxes[zb0004].Name) + o = msgp.AppendBytes(o, (*z).Boxes[zb0006].Name) } } } - if (zb0006Mask & 0x80) == 0 { // if not empty + if (zb0007Mask & 0x100) == 0 { // if not empty // string "apep" o = append(o, 0xa4, 0x61, 0x70, 0x65, 0x70) o = msgp.AppendUint32(o, (*z).ExtraProgramPages) } - if (zb0006Mask & 0x100) == 0 { // if not empty + if (zb0007Mask & 0x200) == 0 { // if not empty // string "apfa" o = append(o, 0xa4, 0x61, 0x70, 0x66, 0x61) if (*z).ForeignApps == nil { @@ -385,31 +431,31 @@ func (z *ApplicationCallTxnFields) MarshalMsg(b []byte) (o []byte) { } else { o = msgp.AppendArrayHeader(o, uint32(len((*z).ForeignApps))) } - for zb0003 := range (*z).ForeignApps { - o = (*z).ForeignApps[zb0003].MarshalMsg(o) + for zb0004 := range (*z).ForeignApps { + o = (*z).ForeignApps[zb0004].MarshalMsg(o) } } - if (zb0006Mask & 0x200) == 0 { // if not empty + if (zb0007Mask & 0x400) == 0 { // if not empty // string "apgs" o = append(o, 0xa4, 0x61, 0x70, 0x67, 0x73) o = (*z).GlobalStateSchema.MarshalMsg(o) } - if (zb0006Mask & 0x400) == 0 { // if not empty + if (zb0007Mask & 0x800) == 0 { // if not empty // string "apid" o = append(o, 0xa4, 0x61, 0x70, 0x69, 0x64) o = (*z).ApplicationID.MarshalMsg(o) } - if (zb0006Mask & 0x800) == 0 { // if not empty + if (zb0007Mask & 0x1000) == 0 { // if not empty // string "apls" o = append(o, 0xa4, 0x61, 0x70, 0x6c, 0x73) o = (*z).LocalStateSchema.MarshalMsg(o) } - if (zb0006Mask & 0x1000) == 0 { // if not empty + if (zb0007Mask & 0x2000) == 0 { // if not empty // string "aprv" o = append(o, 0xa4, 0x61, 0x70, 0x72, 0x76) o = msgp.AppendUint64(o, (*z).RejectVersion) } - if (zb0006Mask & 0x2000) == 0 { // if not empty + if (zb0007Mask & 0x4000) == 0 { // if not empty // string "apsu" o = append(o, 0xa4, 0x61, 0x70, 0x73, 0x75) o = msgp.AppendBytes(o, (*z).ClearStateProgram) @@ -432,55 +478,55 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm st.AllowableDepth-- var field []byte _ = field - var zb0006 int - var zb0007 bool - zb0006, zb0007, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0007 int + var zb0008 bool + zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0006, zb0007, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0007, zb0008, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err) return } - if zb0006 > 0 { - zb0006-- + if zb0007 > 0 { + zb0007-- bts, err = (*z).ApplicationID.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ApplicationID") return } } - if zb0006 > 0 { - zb0006-- + if zb0007 > 0 { + zb0007-- { - var zb0008 uint64 - zb0008, bts, err = msgp.ReadUint64Bytes(bts) + var zb0009 uint64 + zb0009, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "OnCompletion") return } - (*z).OnCompletion = OnCompletion(zb0008) + (*z).OnCompletion = OnCompletion(zb0009) } } - if zb0006 > 0 { - zb0006-- - var zb0009 int - var zb0010 bool - zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0007 > 0 { + zb0007-- + var zb0010 int + var zb0011 bool + zb0010, zb0011, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ApplicationArgs") return } - if zb0009 > encodedMaxApplicationArgs { - err = msgp.ErrOverflow(uint64(zb0009), uint64(encodedMaxApplicationArgs)) + if zb0010 > encodedMaxApplicationArgs { + err = msgp.ErrOverflow(uint64(zb0010), uint64(encodedMaxApplicationArgs)) err = msgp.WrapError(err, "struct-from-array", "ApplicationArgs") return } - if zb0010 { + if zb0011 { (*z).ApplicationArgs = nil - } else if (*z).ApplicationArgs != nil && cap((*z).ApplicationArgs) >= zb0009 { - (*z).ApplicationArgs = ((*z).ApplicationArgs)[:zb0009] + } else if (*z).ApplicationArgs != nil && cap((*z).ApplicationArgs) >= zb0010 { + (*z).ApplicationArgs = ((*z).ApplicationArgs)[:zb0010] } else { - (*z).ApplicationArgs = make([][]byte, zb0009) + (*z).ApplicationArgs = make([][]byte, zb0010) } for zb0001 := range (*z).ApplicationArgs { (*z).ApplicationArgs[zb0001], bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationArgs[zb0001]) @@ -490,26 +536,26 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm } } } - if zb0006 > 0 { - zb0006-- - var zb0011 int - var zb0012 bool - zb0011, zb0012, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0007 > 0 { + zb0007-- + var zb0012 int + var zb0013 bool + zb0012, zb0013, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Accounts") return } - if zb0011 > encodedMaxAccounts { - err = msgp.ErrOverflow(uint64(zb0011), uint64(encodedMaxAccounts)) + if zb0012 > encodedMaxAccounts { + err = msgp.ErrOverflow(uint64(zb0012), uint64(encodedMaxAccounts)) err = msgp.WrapError(err, "struct-from-array", "Accounts") return } - if zb0012 { + if zb0013 { (*z).Accounts = nil - } else if (*z).Accounts != nil && cap((*z).Accounts) >= zb0011 { - (*z).Accounts = ((*z).Accounts)[:zb0011] + } else if (*z).Accounts != nil && cap((*z).Accounts) >= zb0012 { + (*z).Accounts = ((*z).Accounts)[:zb0012] } else { - (*z).Accounts = make([]basics.Address, zb0011) + (*z).Accounts = make([]basics.Address, zb0012) } for zb0002 := range (*z).Accounts { bts, err = (*z).Accounts[zb0002].UnmarshalMsgWithState(bts, st) @@ -519,141 +565,199 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm } } } - if zb0006 > 0 { - zb0006-- - var zb0013 int - var zb0014 bool - zb0013, zb0014, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0007 > 0 { + zb0007-- + var zb0014 int + var zb0015 bool + zb0014, zb0015, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "ForeignAssets") + return + } + if zb0014 > encodedMaxForeignAssets { + err = msgp.ErrOverflow(uint64(zb0014), uint64(encodedMaxForeignAssets)) + err = msgp.WrapError(err, "struct-from-array", "ForeignAssets") + return + } + if zb0015 { + (*z).ForeignAssets = nil + } else if (*z).ForeignAssets != nil && cap((*z).ForeignAssets) >= zb0014 { + (*z).ForeignAssets = ((*z).ForeignAssets)[:zb0014] + } else { + (*z).ForeignAssets = make([]basics.AssetIndex, zb0014) + } + for zb0003 := range (*z).ForeignAssets { + bts, err = (*z).ForeignAssets[zb0003].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "ForeignAssets", zb0003) + return + } + } + } + if zb0007 > 0 { + zb0007-- + var zb0016 int + var zb0017 bool + zb0016, zb0017, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ForeignApps") return } - if zb0013 > encodedMaxForeignApps { - err = msgp.ErrOverflow(uint64(zb0013), uint64(encodedMaxForeignApps)) + if zb0016 > encodedMaxForeignApps { + err = msgp.ErrOverflow(uint64(zb0016), uint64(encodedMaxForeignApps)) err = msgp.WrapError(err, "struct-from-array", "ForeignApps") return } - if zb0014 { + if zb0017 { (*z).ForeignApps = nil - } else if (*z).ForeignApps != nil && cap((*z).ForeignApps) >= zb0013 { - (*z).ForeignApps = ((*z).ForeignApps)[:zb0013] + } else if (*z).ForeignApps != nil && cap((*z).ForeignApps) >= zb0016 { + (*z).ForeignApps = ((*z).ForeignApps)[:zb0016] } else { - (*z).ForeignApps = make([]basics.AppIndex, zb0013) + (*z).ForeignApps = make([]basics.AppIndex, zb0016) } - for zb0003 := range (*z).ForeignApps { - bts, err = (*z).ForeignApps[zb0003].UnmarshalMsgWithState(bts, st) + for zb0004 := range (*z).ForeignApps { + bts, err = (*z).ForeignApps[zb0004].UnmarshalMsgWithState(bts, st) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "ForeignApps", zb0003) + err = msgp.WrapError(err, "struct-from-array", "ForeignApps", zb0004) return } } } - if zb0006 > 0 { - zb0006-- - var zb0015 int - var zb0016 bool - zb0015, zb0016, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0007 > 0 { + zb0007-- + var zb0018 int + var zb0019 bool + zb0018, zb0019, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Access") + return + } + if zb0018 > encodedMaxAccess { + err = msgp.ErrOverflow(uint64(zb0018), uint64(encodedMaxAccess)) + err = msgp.WrapError(err, "struct-from-array", "Access") + return + } + if zb0019 { + (*z).Access = nil + } else if (*z).Access != nil && cap((*z).Access) >= zb0018 { + (*z).Access = ((*z).Access)[:zb0018] + } else { + (*z).Access = make([]ResourceRef, zb0018) + } + for zb0005 := range (*z).Access { + bts, err = (*z).Access[zb0005].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Access", zb0005) + return + } + } + } + if zb0007 > 0 { + zb0007-- + var zb0020 int + var zb0021 bool + zb0020, zb0021, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Boxes") return } - if zb0015 > encodedMaxBoxes { - err = msgp.ErrOverflow(uint64(zb0015), uint64(encodedMaxBoxes)) + if zb0020 > encodedMaxBoxes { + err = msgp.ErrOverflow(uint64(zb0020), uint64(encodedMaxBoxes)) err = msgp.WrapError(err, "struct-from-array", "Boxes") return } - if zb0016 { + if zb0021 { (*z).Boxes = nil - } else if (*z).Boxes != nil && cap((*z).Boxes) >= zb0015 { - (*z).Boxes = ((*z).Boxes)[:zb0015] + } else if (*z).Boxes != nil && cap((*z).Boxes) >= zb0020 { + (*z).Boxes = ((*z).Boxes)[:zb0020] } else { - (*z).Boxes = make([]BoxRef, zb0015) + (*z).Boxes = make([]BoxRef, zb0020) } - for zb0004 := range (*z).Boxes { - var zb0017 int - var zb0018 bool - zb0017, zb0018, bts, err = msgp.ReadMapHeaderBytes(bts) + for zb0006 := range (*z).Boxes { + var zb0022 int + var zb0023 bool + zb0022, zb0023, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0017, zb0018, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0022, zb0023, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004) + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006) return } - if zb0017 > 0 { - zb0017-- - (*z).Boxes[zb0004].Index, bts, err = msgp.ReadUint64Bytes(bts) + if zb0022 > 0 { + zb0022-- + (*z).Boxes[zb0006].Index, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004, "struct-from-array", "Index") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006, "struct-from-array", "Index") return } } - if zb0017 > 0 { - zb0017-- - var zb0019 int - zb0019, err = msgp.ReadBytesBytesHeader(bts) + if zb0022 > 0 { + zb0022-- + var zb0024 int + zb0024, err = msgp.ReadBytesBytesHeader(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004, "struct-from-array", "Name") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006, "struct-from-array", "Name") return } - if zb0019 > config.MaxBytesKeyValueLen { - err = msgp.ErrOverflow(uint64(zb0019), uint64(config.MaxBytesKeyValueLen)) + if zb0024 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0024), uint64(config.MaxBytesKeyValueLen)) return } - (*z).Boxes[zb0004].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Boxes[zb0004].Name) + (*z).Boxes[zb0006].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Boxes[zb0006].Name) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004, "struct-from-array", "Name") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006, "struct-from-array", "Name") return } } - if zb0017 > 0 { - err = msgp.ErrTooManyArrayFields(zb0017) + if zb0022 > 0 { + err = msgp.ErrTooManyArrayFields(zb0022) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004, "struct-from-array") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006, "struct-from-array") return } } } else { if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004) + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006) return } - if zb0018 { - (*z).Boxes[zb0004] = BoxRef{} + if zb0023 { + (*z).Boxes[zb0006] = BoxRef{} } - for zb0017 > 0 { - zb0017-- + for zb0022 > 0 { + zb0022-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004) + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006) return } switch string(field) { case "i": - (*z).Boxes[zb0004].Index, bts, err = msgp.ReadUint64Bytes(bts) + (*z).Boxes[zb0006].Index, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004, "Index") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006, "Index") return } case "n": - var zb0020 int - zb0020, err = msgp.ReadBytesBytesHeader(bts) + var zb0025 int + zb0025, err = msgp.ReadBytesBytesHeader(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004, "Name") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006, "Name") return } - if zb0020 > config.MaxBytesKeyValueLen { - err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxBytesKeyValueLen)) + if zb0025 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0025), uint64(config.MaxBytesKeyValueLen)) return } - (*z).Boxes[zb0004].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Boxes[zb0004].Name) + (*z).Boxes[zb0006].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Boxes[zb0006].Name) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004, "Name") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006, "Name") return } default: err = msgp.ErrNoField(string(field)) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0004) + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0006) return } } @@ -661,61 +765,32 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm } } } - if zb0006 > 0 { - zb0006-- - var zb0021 int - var zb0022 bool - zb0021, zb0022, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "struct-from-array", "ForeignAssets") - return - } - if zb0021 > encodedMaxForeignAssets { - err = msgp.ErrOverflow(uint64(zb0021), uint64(encodedMaxForeignAssets)) - err = msgp.WrapError(err, "struct-from-array", "ForeignAssets") - return - } - if zb0022 { - (*z).ForeignAssets = nil - } else if (*z).ForeignAssets != nil && cap((*z).ForeignAssets) >= zb0021 { - (*z).ForeignAssets = ((*z).ForeignAssets)[:zb0021] - } else { - (*z).ForeignAssets = make([]basics.AssetIndex, zb0021) - } - for zb0005 := range (*z).ForeignAssets { - bts, err = (*z).ForeignAssets[zb0005].UnmarshalMsgWithState(bts, st) - if err != nil { - err = msgp.WrapError(err, "struct-from-array", "ForeignAssets", zb0005) - return - } - } - } - if zb0006 > 0 { - zb0006-- + if zb0007 > 0 { + zb0007-- bts, err = (*z).LocalStateSchema.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "LocalStateSchema") return } } - if zb0006 > 0 { - zb0006-- + if zb0007 > 0 { + zb0007-- bts, err = (*z).GlobalStateSchema.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GlobalStateSchema") return } } - if zb0006 > 0 { - zb0006-- - var zb0023 int - zb0023, err = msgp.ReadBytesBytesHeader(bts) + if zb0007 > 0 { + zb0007-- + var zb0026 int + zb0026, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ApprovalProgram") return } - if zb0023 > config.MaxAvailableAppProgramLen { - err = msgp.ErrOverflow(uint64(zb0023), uint64(config.MaxAvailableAppProgramLen)) + if zb0026 > config.MaxAvailableAppProgramLen { + err = msgp.ErrOverflow(uint64(zb0026), uint64(config.MaxAvailableAppProgramLen)) return } (*z).ApprovalProgram, bts, err = msgp.ReadBytesBytes(bts, (*z).ApprovalProgram) @@ -724,16 +799,16 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm return } } - if zb0006 > 0 { - zb0006-- - var zb0024 int - zb0024, err = msgp.ReadBytesBytesHeader(bts) + if zb0007 > 0 { + zb0007-- + var zb0027 int + zb0027, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ClearStateProgram") return } - if zb0024 > config.MaxAvailableAppProgramLen { - err = msgp.ErrOverflow(uint64(zb0024), uint64(config.MaxAvailableAppProgramLen)) + if zb0027 > config.MaxAvailableAppProgramLen { + err = msgp.ErrOverflow(uint64(zb0027), uint64(config.MaxAvailableAppProgramLen)) return } (*z).ClearStateProgram, bts, err = msgp.ReadBytesBytes(bts, (*z).ClearStateProgram) @@ -742,24 +817,24 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm return } } - if zb0006 > 0 { - zb0006-- + if zb0007 > 0 { + zb0007-- (*z).ExtraProgramPages, bts, err = msgp.ReadUint32Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ExtraProgramPages") return } } - if zb0006 > 0 { - zb0006-- + if zb0007 > 0 { + zb0007-- (*z).RejectVersion, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RejectVersion") return } } - if zb0006 > 0 { - err = msgp.ErrTooManyArrayFields(zb0006) + if zb0007 > 0 { + err = msgp.ErrTooManyArrayFields(zb0007) if err != nil { err = msgp.WrapError(err, "struct-from-array") return @@ -770,12 +845,12 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm err = msgp.WrapError(err) return } - if zb0007 { + if zb0008 { (*z) = ApplicationCallTxnFields{} } - for zb0006 > 0 { - zb0006-- - field, bts, err = msgp.ReadMapKeyZC(bts) + for zb0007 > 0 { + zb0007-- + field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err) return @@ -789,33 +864,33 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm } case "apan": { - var zb0025 uint64 - zb0025, bts, err = msgp.ReadUint64Bytes(bts) + var zb0028 uint64 + zb0028, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "OnCompletion") return } - (*z).OnCompletion = OnCompletion(zb0025) + (*z).OnCompletion = OnCompletion(zb0028) } case "apaa": - var zb0026 int - var zb0027 bool - zb0026, zb0027, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0029 int + var zb0030 bool + zb0029, zb0030, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ApplicationArgs") return } - if zb0026 > encodedMaxApplicationArgs { - err = msgp.ErrOverflow(uint64(zb0026), uint64(encodedMaxApplicationArgs)) + if zb0029 > encodedMaxApplicationArgs { + err = msgp.ErrOverflow(uint64(zb0029), uint64(encodedMaxApplicationArgs)) err = msgp.WrapError(err, "ApplicationArgs") return } - if zb0027 { + if zb0030 { (*z).ApplicationArgs = nil - } else if (*z).ApplicationArgs != nil && cap((*z).ApplicationArgs) >= zb0026 { - (*z).ApplicationArgs = ((*z).ApplicationArgs)[:zb0026] + } else if (*z).ApplicationArgs != nil && cap((*z).ApplicationArgs) >= zb0029 { + (*z).ApplicationArgs = ((*z).ApplicationArgs)[:zb0029] } else { - (*z).ApplicationArgs = make([][]byte, zb0026) + (*z).ApplicationArgs = make([][]byte, zb0029) } for zb0001 := range (*z).ApplicationArgs { (*z).ApplicationArgs[zb0001], bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationArgs[zb0001]) @@ -825,24 +900,24 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm } } case "apat": - var zb0028 int - var zb0029 bool - zb0028, zb0029, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0031 int + var zb0032 bool + zb0031, zb0032, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "Accounts") return } - if zb0028 > encodedMaxAccounts { - err = msgp.ErrOverflow(uint64(zb0028), uint64(encodedMaxAccounts)) + if zb0031 > encodedMaxAccounts { + err = msgp.ErrOverflow(uint64(zb0031), uint64(encodedMaxAccounts)) err = msgp.WrapError(err, "Accounts") return } - if zb0029 { + if zb0032 { (*z).Accounts = nil - } else if (*z).Accounts != nil && cap((*z).Accounts) >= zb0028 { - (*z).Accounts = ((*z).Accounts)[:zb0028] + } else if (*z).Accounts != nil && cap((*z).Accounts) >= zb0031 { + (*z).Accounts = ((*z).Accounts)[:zb0031] } else { - (*z).Accounts = make([]basics.Address, zb0028) + (*z).Accounts = make([]basics.Address, zb0031) } for zb0002 := range (*z).Accounts { bts, err = (*z).Accounts[zb0002].UnmarshalMsgWithState(bts, st) @@ -851,171 +926,198 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm return } } + case "apas": + var zb0033 int + var zb0034 bool + zb0033, zb0034, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "ForeignAssets") + return + } + if zb0033 > encodedMaxForeignAssets { + err = msgp.ErrOverflow(uint64(zb0033), uint64(encodedMaxForeignAssets)) + err = msgp.WrapError(err, "ForeignAssets") + return + } + if zb0034 { + (*z).ForeignAssets = nil + } else if (*z).ForeignAssets != nil && cap((*z).ForeignAssets) >= zb0033 { + (*z).ForeignAssets = ((*z).ForeignAssets)[:zb0033] + } else { + (*z).ForeignAssets = make([]basics.AssetIndex, zb0033) + } + for zb0003 := range (*z).ForeignAssets { + bts, err = (*z).ForeignAssets[zb0003].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "ForeignAssets", zb0003) + return + } + } case "apfa": - var zb0030 int - var zb0031 bool - zb0030, zb0031, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0035 int + var zb0036 bool + zb0035, zb0036, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ForeignApps") return } - if zb0030 > encodedMaxForeignApps { - err = msgp.ErrOverflow(uint64(zb0030), uint64(encodedMaxForeignApps)) + if zb0035 > encodedMaxForeignApps { + err = msgp.ErrOverflow(uint64(zb0035), uint64(encodedMaxForeignApps)) err = msgp.WrapError(err, "ForeignApps") return } - if zb0031 { + if zb0036 { (*z).ForeignApps = nil - } else if (*z).ForeignApps != nil && cap((*z).ForeignApps) >= zb0030 { - (*z).ForeignApps = ((*z).ForeignApps)[:zb0030] + } else if (*z).ForeignApps != nil && cap((*z).ForeignApps) >= zb0035 { + (*z).ForeignApps = ((*z).ForeignApps)[:zb0035] + } else { + (*z).ForeignApps = make([]basics.AppIndex, zb0035) + } + for zb0004 := range (*z).ForeignApps { + bts, err = (*z).ForeignApps[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "ForeignApps", zb0004) + return + } + } + case "al": + var zb0037 int + var zb0038 bool + zb0037, zb0038, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Access") + return + } + if zb0037 > encodedMaxAccess { + err = msgp.ErrOverflow(uint64(zb0037), uint64(encodedMaxAccess)) + err = msgp.WrapError(err, "Access") + return + } + if zb0038 { + (*z).Access = nil + } else if (*z).Access != nil && cap((*z).Access) >= zb0037 { + (*z).Access = ((*z).Access)[:zb0037] } else { - (*z).ForeignApps = make([]basics.AppIndex, zb0030) + (*z).Access = make([]ResourceRef, zb0037) } - for zb0003 := range (*z).ForeignApps { - bts, err = (*z).ForeignApps[zb0003].UnmarshalMsgWithState(bts, st) + for zb0005 := range (*z).Access { + bts, err = (*z).Access[zb0005].UnmarshalMsgWithState(bts, st) if err != nil { - err = msgp.WrapError(err, "ForeignApps", zb0003) + err = msgp.WrapError(err, "Access", zb0005) return } } case "apbx": - var zb0032 int - var zb0033 bool - zb0032, zb0033, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0039 int + var zb0040 bool + zb0039, zb0040, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "Boxes") return } - if zb0032 > encodedMaxBoxes { - err = msgp.ErrOverflow(uint64(zb0032), uint64(encodedMaxBoxes)) + if zb0039 > encodedMaxBoxes { + err = msgp.ErrOverflow(uint64(zb0039), uint64(encodedMaxBoxes)) err = msgp.WrapError(err, "Boxes") return } - if zb0033 { + if zb0040 { (*z).Boxes = nil - } else if (*z).Boxes != nil && cap((*z).Boxes) >= zb0032 { - (*z).Boxes = ((*z).Boxes)[:zb0032] + } else if (*z).Boxes != nil && cap((*z).Boxes) >= zb0039 { + (*z).Boxes = ((*z).Boxes)[:zb0039] } else { - (*z).Boxes = make([]BoxRef, zb0032) + (*z).Boxes = make([]BoxRef, zb0039) } - for zb0004 := range (*z).Boxes { - var zb0034 int - var zb0035 bool - zb0034, zb0035, bts, err = msgp.ReadMapHeaderBytes(bts) + for zb0006 := range (*z).Boxes { + var zb0041 int + var zb0042 bool + zb0041, zb0042, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0034, zb0035, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0041, zb0042, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004) + err = msgp.WrapError(err, "Boxes", zb0006) return } - if zb0034 > 0 { - zb0034-- - (*z).Boxes[zb0004].Index, bts, err = msgp.ReadUint64Bytes(bts) + if zb0041 > 0 { + zb0041-- + (*z).Boxes[zb0006].Index, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004, "struct-from-array", "Index") + err = msgp.WrapError(err, "Boxes", zb0006, "struct-from-array", "Index") return } } - if zb0034 > 0 { - zb0034-- - var zb0036 int - zb0036, err = msgp.ReadBytesBytesHeader(bts) + if zb0041 > 0 { + zb0041-- + var zb0043 int + zb0043, err = msgp.ReadBytesBytesHeader(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004, "struct-from-array", "Name") + err = msgp.WrapError(err, "Boxes", zb0006, "struct-from-array", "Name") return } - if zb0036 > config.MaxBytesKeyValueLen { - err = msgp.ErrOverflow(uint64(zb0036), uint64(config.MaxBytesKeyValueLen)) + if zb0043 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0043), uint64(config.MaxBytesKeyValueLen)) return } - (*z).Boxes[zb0004].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Boxes[zb0004].Name) + (*z).Boxes[zb0006].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Boxes[zb0006].Name) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004, "struct-from-array", "Name") + err = msgp.WrapError(err, "Boxes", zb0006, "struct-from-array", "Name") return } } - if zb0034 > 0 { - err = msgp.ErrTooManyArrayFields(zb0034) + if zb0041 > 0 { + err = msgp.ErrTooManyArrayFields(zb0041) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004, "struct-from-array") + err = msgp.WrapError(err, "Boxes", zb0006, "struct-from-array") return } } } else { if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004) + err = msgp.WrapError(err, "Boxes", zb0006) return } - if zb0035 { - (*z).Boxes[zb0004] = BoxRef{} + if zb0042 { + (*z).Boxes[zb0006] = BoxRef{} } - for zb0034 > 0 { - zb0034-- + for zb0041 > 0 { + zb0041-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004) + err = msgp.WrapError(err, "Boxes", zb0006) return } switch string(field) { case "i": - (*z).Boxes[zb0004].Index, bts, err = msgp.ReadUint64Bytes(bts) + (*z).Boxes[zb0006].Index, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004, "Index") + err = msgp.WrapError(err, "Boxes", zb0006, "Index") return } case "n": - var zb0037 int - zb0037, err = msgp.ReadBytesBytesHeader(bts) + var zb0044 int + zb0044, err = msgp.ReadBytesBytesHeader(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004, "Name") + err = msgp.WrapError(err, "Boxes", zb0006, "Name") return } - if zb0037 > config.MaxBytesKeyValueLen { - err = msgp.ErrOverflow(uint64(zb0037), uint64(config.MaxBytesKeyValueLen)) + if zb0044 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0044), uint64(config.MaxBytesKeyValueLen)) return } - (*z).Boxes[zb0004].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Boxes[zb0004].Name) + (*z).Boxes[zb0006].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Boxes[zb0006].Name) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004, "Name") + err = msgp.WrapError(err, "Boxes", zb0006, "Name") return } default: err = msgp.ErrNoField(string(field)) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0004) + err = msgp.WrapError(err, "Boxes", zb0006) return } } } } } - case "apas": - var zb0038 int - var zb0039 bool - zb0038, zb0039, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "ForeignAssets") - return - } - if zb0038 > encodedMaxForeignAssets { - err = msgp.ErrOverflow(uint64(zb0038), uint64(encodedMaxForeignAssets)) - err = msgp.WrapError(err, "ForeignAssets") - return - } - if zb0039 { - (*z).ForeignAssets = nil - } else if (*z).ForeignAssets != nil && cap((*z).ForeignAssets) >= zb0038 { - (*z).ForeignAssets = ((*z).ForeignAssets)[:zb0038] - } else { - (*z).ForeignAssets = make([]basics.AssetIndex, zb0038) - } - for zb0005 := range (*z).ForeignAssets { - bts, err = (*z).ForeignAssets[zb0005].UnmarshalMsgWithState(bts, st) - if err != nil { - err = msgp.WrapError(err, "ForeignAssets", zb0005) - return - } - } case "apls": bts, err = (*z).LocalStateSchema.UnmarshalMsgWithState(bts, st) if err != nil { @@ -1029,14 +1131,14 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm return } case "apap": - var zb0040 int - zb0040, err = msgp.ReadBytesBytesHeader(bts) + var zb0045 int + zb0045, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "ApprovalProgram") return } - if zb0040 > config.MaxAvailableAppProgramLen { - err = msgp.ErrOverflow(uint64(zb0040), uint64(config.MaxAvailableAppProgramLen)) + if zb0045 > config.MaxAvailableAppProgramLen { + err = msgp.ErrOverflow(uint64(zb0045), uint64(config.MaxAvailableAppProgramLen)) return } (*z).ApprovalProgram, bts, err = msgp.ReadBytesBytes(bts, (*z).ApprovalProgram) @@ -1045,14 +1147,14 @@ func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.Unm return } case "apsu": - var zb0041 int - zb0041, err = msgp.ReadBytesBytesHeader(bts) + var zb0046 int + zb0046, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "ClearStateProgram") return } - if zb0041 > config.MaxAvailableAppProgramLen { - err = msgp.ErrOverflow(uint64(zb0041), uint64(config.MaxAvailableAppProgramLen)) + if zb0046 > config.MaxAvailableAppProgramLen { + err = msgp.ErrOverflow(uint64(zb0046), uint64(config.MaxAvailableAppProgramLen)) return } (*z).ClearStateProgram, bts, err = msgp.ReadBytesBytes(bts, (*z).ClearStateProgram) @@ -1104,16 +1206,20 @@ func (z *ApplicationCallTxnFields) Msgsize() (s int) { s += (*z).Accounts[zb0002].Msgsize() } s += 5 + msgp.ArrayHeaderSize - for zb0003 := range (*z).ForeignApps { - s += (*z).ForeignApps[zb0003].Msgsize() + for zb0003 := range (*z).ForeignAssets { + s += (*z).ForeignAssets[zb0003].Msgsize() } s += 5 + msgp.ArrayHeaderSize - for zb0004 := range (*z).Boxes { - s += 1 + 2 + msgp.Uint64Size + 2 + msgp.BytesPrefixSize + len((*z).Boxes[zb0004].Name) + for zb0004 := range (*z).ForeignApps { + s += (*z).ForeignApps[zb0004].Msgsize() + } + s += 3 + msgp.ArrayHeaderSize + for zb0005 := range (*z).Access { + s += (*z).Access[zb0005].Msgsize() } s += 5 + msgp.ArrayHeaderSize - for zb0005 := range (*z).ForeignAssets { - s += (*z).ForeignAssets[zb0005].Msgsize() + for zb0006 := range (*z).Boxes { + s += 1 + 2 + msgp.Uint64Size + 2 + msgp.BytesPrefixSize + len((*z).Boxes[zb0006].Name) } s += 5 + (*z).LocalStateSchema.Msgsize() + 5 + (*z).GlobalStateSchema.Msgsize() + 5 + msgp.BytesPrefixSize + len((*z).ApprovalProgram) + 5 + msgp.BytesPrefixSize + len((*z).ClearStateProgram) + 5 + msgp.Uint32Size + 5 + msgp.Uint64Size return @@ -1121,7 +1227,7 @@ func (z *ApplicationCallTxnFields) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *ApplicationCallTxnFields) MsgIsZero() bool { - return ((*z).ApplicationID.MsgIsZero()) && ((*z).OnCompletion == 0) && (len((*z).ApplicationArgs) == 0) && (len((*z).Accounts) == 0) && (len((*z).ForeignApps) == 0) && (len((*z).Boxes) == 0) && (len((*z).ForeignAssets) == 0) && ((*z).LocalStateSchema.MsgIsZero()) && ((*z).GlobalStateSchema.MsgIsZero()) && (len((*z).ApprovalProgram) == 0) && (len((*z).ClearStateProgram) == 0) && ((*z).ExtraProgramPages == 0) && ((*z).RejectVersion == 0) + return ((*z).ApplicationID.MsgIsZero()) && ((*z).OnCompletion == 0) && (len((*z).ApplicationArgs) == 0) && (len((*z).Accounts) == 0) && (len((*z).ForeignAssets) == 0) && (len((*z).ForeignApps) == 0) && (len((*z).Access) == 0) && (len((*z).Boxes) == 0) && ((*z).LocalStateSchema.MsgIsZero()) && ((*z).GlobalStateSchema.MsgIsZero()) && (len((*z).ApprovalProgram) == 0) && (len((*z).ClearStateProgram) == 0) && ((*z).ExtraProgramPages == 0) && ((*z).RejectVersion == 0) } // MaxSize returns a maximum valid message size for this message type @@ -1132,14 +1238,17 @@ func ApplicationCallTxnFieldsMaxSize() (s int) { // Calculating size of slice: z.Accounts s += msgp.ArrayHeaderSize + ((encodedMaxAccounts) * (basics.AddressMaxSize())) s += 5 + // Calculating size of slice: z.ForeignAssets + s += msgp.ArrayHeaderSize + ((encodedMaxForeignAssets) * (basics.AssetIndexMaxSize())) + s += 5 // Calculating size of slice: z.ForeignApps s += msgp.ArrayHeaderSize + ((encodedMaxForeignApps) * (basics.AppIndexMaxSize())) + s += 3 + // Calculating size of slice: z.Access + s += msgp.ArrayHeaderSize + ((encodedMaxAccess) * (ResourceRefMaxSize())) s += 5 // Calculating size of slice: z.Boxes s += msgp.ArrayHeaderSize + ((encodedMaxBoxes) * (BoxRefMaxSize())) - s += 5 - // Calculating size of slice: z.ForeignAssets - s += msgp.ArrayHeaderSize + ((encodedMaxForeignAssets) * (basics.AssetIndexMaxSize())) s += 5 + basics.StateSchemaMaxSize() + 5 + basics.StateSchemaMaxSize() + 5 + msgp.BytesPrefixSize + config.MaxAvailableAppProgramLen + 5 + msgp.BytesPrefixSize + config.MaxAvailableAppProgramLen + 5 + msgp.Uint32Size + 5 + msgp.Uint64Size return } @@ -3153,6 +3262,149 @@ func HeartbeatTxnFieldsMaxSize() (s int) { return } +// MarshalMsg implements msgp.Marshaler +func (z *HoldingRef) MarshalMsg(b []byte) (o []byte) { + o = msgp.Require(b, z.Msgsize()) + // omitempty: check for empty values + zb0001Len := uint32(2) + var zb0001Mask uint8 /* 3 bits */ + if (*z).Address == 0 { + zb0001Len-- + zb0001Mask |= 0x2 + } + if (*z).Asset == 0 { + zb0001Len-- + zb0001Mask |= 0x4 + } + // variable map header, size zb0001Len + o = append(o, 0x80|uint8(zb0001Len)) + if zb0001Len != 0 { + if (zb0001Mask & 0x2) == 0 { // if not empty + // string "d" + o = append(o, 0xa1, 0x64) + o = msgp.AppendUint64(o, (*z).Address) + } + if (zb0001Mask & 0x4) == 0 { // if not empty + // string "s" + o = append(o, 0xa1, 0x73) + o = msgp.AppendUint64(o, (*z).Asset) + } + } + return +} + +func (_ *HoldingRef) CanMarshalMsg(z interface{}) bool { + _, ok := (z).(*HoldingRef) + return ok +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *HoldingRef) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { + if st.AllowableDepth == 0 { + err = msgp.ErrMaxDepthExceeded{} + return + } + st.AllowableDepth-- + var field []byte + _ = field + var zb0001 int + var zb0002 bool + zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) + if _, ok := err.(msgp.TypeError); ok { + zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + if zb0001 > 0 { + zb0001-- + (*z).Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Address") + return + } + } + if zb0001 > 0 { + zb0001-- + (*z).Asset, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Asset") + return + } + } + if zb0001 > 0 { + err = msgp.ErrTooManyArrayFields(zb0001) + if err != nil { + err = msgp.WrapError(err, "struct-from-array") + return + } + } + } else { + if err != nil { + err = msgp.WrapError(err) + return + } + if zb0002 { + (*z) = HoldingRef{} + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch string(field) { + case "d": + (*z).Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Address") + return + } + case "s": + (*z).Asset, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Asset") + return + } + default: + err = msgp.ErrNoField(string(field)) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + } + o = bts + return +} + +func (z *HoldingRef) UnmarshalMsg(bts []byte) (o []byte, err error) { + return z.UnmarshalMsgWithState(bts, msgp.DefaultUnmarshalState) +} +func (_ *HoldingRef) CanUnmarshalMsg(z interface{}) bool { + _, ok := (z).(*HoldingRef) + return ok +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *HoldingRef) Msgsize() (s int) { + s = 1 + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + return +} + +// MsgIsZero returns whether this is a zero value +func (z *HoldingRef) MsgIsZero() bool { + return ((*z).Address == 0) && ((*z).Asset == 0) +} + +// MaxSize returns a maximum valid message size for this message type +func HoldingRefMaxSize() (s int) { + s = 1 + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + return +} + // MarshalMsg implements msgp.Marshaler func (z *KeyregTxnFields) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) @@ -3412,56 +3664,199 @@ func KeyregTxnFieldsMaxSize() (s int) { } // MarshalMsg implements msgp.Marshaler -func (z *LogicSig) MarshalMsg(b []byte) (o []byte) { +func (z *LocalsRef) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0002Len := uint32(4) - var zb0002Mask uint8 /* 5 bits */ - if len((*z).Args) == 0 { - zb0002Len-- - zb0002Mask |= 0x2 - } - if len((*z).Logic) == 0 { - zb0002Len-- - zb0002Mask |= 0x4 - } - if (*z).Msig.MsgIsZero() { - zb0002Len-- - zb0002Mask |= 0x8 + zb0001Len := uint32(2) + var zb0001Mask uint8 /* 3 bits */ + if (*z).Address == 0 { + zb0001Len-- + zb0001Mask |= 0x2 } - if (*z).Sig.MsgIsZero() { - zb0002Len-- - zb0002Mask |= 0x10 + if (*z).App == 0 { + zb0001Len-- + zb0001Mask |= 0x4 } - // variable map header, size zb0002Len - o = append(o, 0x80|uint8(zb0002Len)) - if zb0002Len != 0 { - if (zb0002Mask & 0x2) == 0 { // if not empty - // string "arg" - o = append(o, 0xa3, 0x61, 0x72, 0x67) - if (*z).Args == nil { - o = msgp.AppendNil(o) - } else { - o = msgp.AppendArrayHeader(o, uint32(len((*z).Args))) - } - for zb0001 := range (*z).Args { - o = msgp.AppendBytes(o, (*z).Args[zb0001]) - } - } - if (zb0002Mask & 0x4) == 0 { // if not empty - // string "l" - o = append(o, 0xa1, 0x6c) - o = msgp.AppendBytes(o, (*z).Logic) - } - if (zb0002Mask & 0x8) == 0 { // if not empty - // string "msig" - o = append(o, 0xa4, 0x6d, 0x73, 0x69, 0x67) - o = (*z).Msig.MarshalMsg(o) + // variable map header, size zb0001Len + o = append(o, 0x80|uint8(zb0001Len)) + if zb0001Len != 0 { + if (zb0001Mask & 0x2) == 0 { // if not empty + // string "d" + o = append(o, 0xa1, 0x64) + o = msgp.AppendUint64(o, (*z).Address) } - if (zb0002Mask & 0x10) == 0 { // if not empty - // string "sig" - o = append(o, 0xa3, 0x73, 0x69, 0x67) - o = (*z).Sig.MarshalMsg(o) + if (zb0001Mask & 0x4) == 0 { // if not empty + // string "p" + o = append(o, 0xa1, 0x70) + o = msgp.AppendUint64(o, (*z).App) + } + } + return +} + +func (_ *LocalsRef) CanMarshalMsg(z interface{}) bool { + _, ok := (z).(*LocalsRef) + return ok +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *LocalsRef) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { + if st.AllowableDepth == 0 { + err = msgp.ErrMaxDepthExceeded{} + return + } + st.AllowableDepth-- + var field []byte + _ = field + var zb0001 int + var zb0002 bool + zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) + if _, ok := err.(msgp.TypeError); ok { + zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + if zb0001 > 0 { + zb0001-- + (*z).Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Address") + return + } + } + if zb0001 > 0 { + zb0001-- + (*z).App, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "App") + return + } + } + if zb0001 > 0 { + err = msgp.ErrTooManyArrayFields(zb0001) + if err != nil { + err = msgp.WrapError(err, "struct-from-array") + return + } + } + } else { + if err != nil { + err = msgp.WrapError(err) + return + } + if zb0002 { + (*z) = LocalsRef{} + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch string(field) { + case "d": + (*z).Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Address") + return + } + case "p": + (*z).App, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "App") + return + } + default: + err = msgp.ErrNoField(string(field)) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + } + o = bts + return +} + +func (z *LocalsRef) UnmarshalMsg(bts []byte) (o []byte, err error) { + return z.UnmarshalMsgWithState(bts, msgp.DefaultUnmarshalState) +} +func (_ *LocalsRef) CanUnmarshalMsg(z interface{}) bool { + _, ok := (z).(*LocalsRef) + return ok +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *LocalsRef) Msgsize() (s int) { + s = 1 + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + return +} + +// MsgIsZero returns whether this is a zero value +func (z *LocalsRef) MsgIsZero() bool { + return ((*z).Address == 0) && ((*z).App == 0) +} + +// MaxSize returns a maximum valid message size for this message type +func LocalsRefMaxSize() (s int) { + s = 1 + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + return +} + +// MarshalMsg implements msgp.Marshaler +func (z *LogicSig) MarshalMsg(b []byte) (o []byte) { + o = msgp.Require(b, z.Msgsize()) + // omitempty: check for empty values + zb0002Len := uint32(4) + var zb0002Mask uint8 /* 5 bits */ + if len((*z).Args) == 0 { + zb0002Len-- + zb0002Mask |= 0x2 + } + if len((*z).Logic) == 0 { + zb0002Len-- + zb0002Mask |= 0x4 + } + if (*z).Msig.MsgIsZero() { + zb0002Len-- + zb0002Mask |= 0x8 + } + if (*z).Sig.MsgIsZero() { + zb0002Len-- + zb0002Mask |= 0x10 + } + // variable map header, size zb0002Len + o = append(o, 0x80|uint8(zb0002Len)) + if zb0002Len != 0 { + if (zb0002Mask & 0x2) == 0 { // if not empty + // string "arg" + o = append(o, 0xa3, 0x61, 0x72, 0x67) + if (*z).Args == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len((*z).Args))) + } + for zb0001 := range (*z).Args { + o = msgp.AppendBytes(o, (*z).Args[zb0001]) + } + } + if (zb0002Mask & 0x4) == 0 { // if not empty + // string "l" + o = append(o, 0xa1, 0x6c) + o = msgp.AppendBytes(o, (*z).Logic) + } + if (zb0002Mask & 0x8) == 0 { // if not empty + // string "msig" + o = append(o, 0xa4, 0x6d, 0x73, 0x69, 0x67) + o = (*z).Msig.MarshalMsg(o) + } + if (zb0002Mask & 0x10) == 0 { // if not empty + // string "sig" + o = append(o, 0xa3, 0x73, 0x69, 0x67) + o = (*z).Sig.MarshalMsg(o) } } return @@ -3895,116 +4290,841 @@ func (z *PaymentTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalSt return } -func (z *PaymentTxnFields) UnmarshalMsg(bts []byte) (o []byte, err error) { - return z.UnmarshalMsgWithState(bts, msgp.DefaultUnmarshalState) -} -func (_ *PaymentTxnFields) CanUnmarshalMsg(z interface{}) bool { - _, ok := (z).(*PaymentTxnFields) - return ok -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z *PaymentTxnFields) Msgsize() (s int) { - s = 1 + 4 + (*z).Receiver.Msgsize() + 4 + (*z).Amount.Msgsize() + 6 + (*z).CloseRemainderTo.Msgsize() - return -} - -// MsgIsZero returns whether this is a zero value -func (z *PaymentTxnFields) MsgIsZero() bool { - return ((*z).Receiver.MsgIsZero()) && ((*z).Amount.MsgIsZero()) && ((*z).CloseRemainderTo.MsgIsZero()) -} - -// MaxSize returns a maximum valid message size for this message type -func PaymentTxnFieldsMaxSize() (s int) { - s = 1 + 4 + basics.AddressMaxSize() + 4 + basics.MicroAlgosMaxSize() + 6 + basics.AddressMaxSize() - return -} - -// MarshalMsg implements msgp.Marshaler -func (z Payset) MarshalMsg(b []byte) (o []byte) { - o = msgp.Require(b, z.Msgsize()) - if z == nil { - o = msgp.AppendNil(o) - } else { - o = msgp.AppendArrayHeader(o, uint32(len(z))) - } - for za0001 := range z { - o = z[za0001].MarshalMsg(o) - } - return -} - -func (_ Payset) CanMarshalMsg(z interface{}) bool { - _, ok := (z).(Payset) - if !ok { - _, ok = (z).(*Payset) - } - return ok -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *Payset) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.AllowableDepth == 0 { - err = msgp.ErrMaxDepthExceeded{} - return - } - st.AllowableDepth-- - var zb0002 int - var zb0003 bool - zb0002, zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - if zb0002 > 100000 { - err = msgp.ErrOverflow(uint64(zb0002), uint64(100000)) - err = msgp.WrapError(err) - return - } - if zb0003 { - (*z) = nil - } else if (*z) != nil && cap((*z)) >= zb0002 { - (*z) = (*z)[:zb0002] - } else { - (*z) = make(Payset, zb0002) - } - for zb0001 := range *z { - bts, err = (*z)[zb0001].UnmarshalMsgWithState(bts, st) - if err != nil { - err = msgp.WrapError(err, zb0001) - return - } - } - o = bts - return -} - -func (z *Payset) UnmarshalMsg(bts []byte) (o []byte, err error) { +func (z *PaymentTxnFields) UnmarshalMsg(bts []byte) (o []byte, err error) { + return z.UnmarshalMsgWithState(bts, msgp.DefaultUnmarshalState) +} +func (_ *PaymentTxnFields) CanUnmarshalMsg(z interface{}) bool { + _, ok := (z).(*PaymentTxnFields) + return ok +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *PaymentTxnFields) Msgsize() (s int) { + s = 1 + 4 + (*z).Receiver.Msgsize() + 4 + (*z).Amount.Msgsize() + 6 + (*z).CloseRemainderTo.Msgsize() + return +} + +// MsgIsZero returns whether this is a zero value +func (z *PaymentTxnFields) MsgIsZero() bool { + return ((*z).Receiver.MsgIsZero()) && ((*z).Amount.MsgIsZero()) && ((*z).CloseRemainderTo.MsgIsZero()) +} + +// MaxSize returns a maximum valid message size for this message type +func PaymentTxnFieldsMaxSize() (s int) { + s = 1 + 4 + basics.AddressMaxSize() + 4 + basics.MicroAlgosMaxSize() + 6 + basics.AddressMaxSize() + return +} + +// MarshalMsg implements msgp.Marshaler +func (z Payset) MarshalMsg(b []byte) (o []byte) { + o = msgp.Require(b, z.Msgsize()) + if z == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len(z))) + } + for za0001 := range z { + o = z[za0001].MarshalMsg(o) + } + return +} + +func (_ Payset) CanMarshalMsg(z interface{}) bool { + _, ok := (z).(Payset) + if !ok { + _, ok = (z).(*Payset) + } + return ok +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *Payset) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { + if st.AllowableDepth == 0 { + err = msgp.ErrMaxDepthExceeded{} + return + } + st.AllowableDepth-- + var zb0002 int + var zb0003 bool + zb0002, zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + if zb0002 > 100000 { + err = msgp.ErrOverflow(uint64(zb0002), uint64(100000)) + err = msgp.WrapError(err) + return + } + if zb0003 { + (*z) = nil + } else if (*z) != nil && cap((*z)) >= zb0002 { + (*z) = (*z)[:zb0002] + } else { + (*z) = make(Payset, zb0002) + } + for zb0001 := range *z { + bts, err = (*z)[zb0001].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, zb0001) + return + } + } + o = bts + return +} + +func (z *Payset) UnmarshalMsg(bts []byte) (o []byte, err error) { + return z.UnmarshalMsgWithState(bts, msgp.DefaultUnmarshalState) +} +func (_ *Payset) CanUnmarshalMsg(z interface{}) bool { + _, ok := (z).(*Payset) + return ok +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z Payset) Msgsize() (s int) { + s = msgp.ArrayHeaderSize + for za0001 := range z { + s += z[za0001].Msgsize() + } + return +} + +// MsgIsZero returns whether this is a zero value +func (z Payset) MsgIsZero() bool { + return len(z) == 0 +} + +// MaxSize returns a maximum valid message size for this message type +func PaysetMaxSize() (s int) { + // Calculating size of slice: z + s += msgp.ArrayHeaderSize + ((100000) * (SignedTxnInBlockMaxSize())) + return +} + +// MarshalMsg implements msgp.Marshaler +func (z *ResourceRef) MarshalMsg(b []byte) (o []byte) { + o = msgp.Require(b, z.Msgsize()) + // omitempty: check for empty values + zb0001Len := uint32(6) + var zb0001Mask uint8 /* 7 bits */ + if ((*z).Box.Index == 0) && (len((*z).Box.Name) == 0) { + zb0001Len-- + zb0001Mask |= 0x2 + } + if (*z).Address.MsgIsZero() { + zb0001Len-- + zb0001Mask |= 0x4 + } + if ((*z).Holding.Address == 0) && ((*z).Holding.Asset == 0) { + zb0001Len-- + zb0001Mask |= 0x8 + } + if ((*z).Locals.Address == 0) && ((*z).Locals.App == 0) { + zb0001Len-- + zb0001Mask |= 0x10 + } + if (*z).App.MsgIsZero() { + zb0001Len-- + zb0001Mask |= 0x20 + } + if (*z).Asset.MsgIsZero() { + zb0001Len-- + zb0001Mask |= 0x40 + } + // variable map header, size zb0001Len + o = append(o, 0x80|uint8(zb0001Len)) + if zb0001Len != 0 { + if (zb0001Mask & 0x2) == 0 { // if not empty + // string "b" + o = append(o, 0xa1, 0x62) + // omitempty: check for empty values + zb0002Len := uint32(2) + var zb0002Mask uint8 /* 3 bits */ + if (*z).Box.Index == 0 { + zb0002Len-- + zb0002Mask |= 0x2 + } + if len((*z).Box.Name) == 0 { + zb0002Len-- + zb0002Mask |= 0x4 + } + // variable map header, size zb0002Len + o = append(o, 0x80|uint8(zb0002Len)) + if (zb0002Mask & 0x2) == 0 { // if not empty + // string "i" + o = append(o, 0xa1, 0x69) + o = msgp.AppendUint64(o, (*z).Box.Index) + } + if (zb0002Mask & 0x4) == 0 { // if not empty + // string "n" + o = append(o, 0xa1, 0x6e) + o = msgp.AppendBytes(o, (*z).Box.Name) + } + } + if (zb0001Mask & 0x4) == 0 { // if not empty + // string "d" + o = append(o, 0xa1, 0x64) + o = (*z).Address.MarshalMsg(o) + } + if (zb0001Mask & 0x8) == 0 { // if not empty + // string "h" + o = append(o, 0xa1, 0x68) + // omitempty: check for empty values + zb0003Len := uint32(2) + var zb0003Mask uint8 /* 3 bits */ + if (*z).Holding.Address == 0 { + zb0003Len-- + zb0003Mask |= 0x2 + } + if (*z).Holding.Asset == 0 { + zb0003Len-- + zb0003Mask |= 0x4 + } + // variable map header, size zb0003Len + o = append(o, 0x80|uint8(zb0003Len)) + if (zb0003Mask & 0x2) == 0 { // if not empty + // string "d" + o = append(o, 0xa1, 0x64) + o = msgp.AppendUint64(o, (*z).Holding.Address) + } + if (zb0003Mask & 0x4) == 0 { // if not empty + // string "s" + o = append(o, 0xa1, 0x73) + o = msgp.AppendUint64(o, (*z).Holding.Asset) + } + } + if (zb0001Mask & 0x10) == 0 { // if not empty + // string "l" + o = append(o, 0xa1, 0x6c) + // omitempty: check for empty values + zb0004Len := uint32(2) + var zb0004Mask uint8 /* 3 bits */ + if (*z).Locals.Address == 0 { + zb0004Len-- + zb0004Mask |= 0x2 + } + if (*z).Locals.App == 0 { + zb0004Len-- + zb0004Mask |= 0x4 + } + // variable map header, size zb0004Len + o = append(o, 0x80|uint8(zb0004Len)) + if (zb0004Mask & 0x2) == 0 { // if not empty + // string "d" + o = append(o, 0xa1, 0x64) + o = msgp.AppendUint64(o, (*z).Locals.Address) + } + if (zb0004Mask & 0x4) == 0 { // if not empty + // string "p" + o = append(o, 0xa1, 0x70) + o = msgp.AppendUint64(o, (*z).Locals.App) + } + } + if (zb0001Mask & 0x20) == 0 { // if not empty + // string "p" + o = append(o, 0xa1, 0x70) + o = (*z).App.MarshalMsg(o) + } + if (zb0001Mask & 0x40) == 0 { // if not empty + // string "s" + o = append(o, 0xa1, 0x73) + o = (*z).Asset.MarshalMsg(o) + } + } + return +} + +func (_ *ResourceRef) CanMarshalMsg(z interface{}) bool { + _, ok := (z).(*ResourceRef) + return ok +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *ResourceRef) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { + if st.AllowableDepth == 0 { + err = msgp.ErrMaxDepthExceeded{} + return + } + st.AllowableDepth-- + var field []byte + _ = field + var zb0001 int + var zb0002 bool + zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) + if _, ok := err.(msgp.TypeError); ok { + zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + if zb0001 > 0 { + zb0001-- + bts, err = (*z).Address.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Address") + return + } + } + if zb0001 > 0 { + zb0001-- + bts, err = (*z).Asset.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Asset") + return + } + } + if zb0001 > 0 { + zb0001-- + bts, err = (*z).App.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "App") + return + } + } + if zb0001 > 0 { + zb0001-- + var zb0003 int + var zb0004 bool + zb0003, zb0004, bts, err = msgp.ReadMapHeaderBytes(bts) + if _, ok := err.(msgp.TypeError); ok { + zb0003, zb0004, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Holding") + return + } + if zb0003 > 0 { + zb0003-- + (*z).Holding.Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Holding", "struct-from-array", "Address") + return + } + } + if zb0003 > 0 { + zb0003-- + (*z).Holding.Asset, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Holding", "struct-from-array", "Asset") + return + } + } + if zb0003 > 0 { + err = msgp.ErrTooManyArrayFields(zb0003) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Holding", "struct-from-array") + return + } + } + } else { + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Holding") + return + } + if zb0004 { + (*z).Holding = HoldingRef{} + } + for zb0003 > 0 { + zb0003-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Holding") + return + } + switch string(field) { + case "d": + (*z).Holding.Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Holding", "Address") + return + } + case "s": + (*z).Holding.Asset, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Holding", "Asset") + return + } + default: + err = msgp.ErrNoField(string(field)) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Holding") + return + } + } + } + } + } + if zb0001 > 0 { + zb0001-- + var zb0005 int + var zb0006 bool + zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts) + if _, ok := err.(msgp.TypeError); ok { + zb0005, zb0006, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Locals") + return + } + if zb0005 > 0 { + zb0005-- + (*z).Locals.Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Locals", "struct-from-array", "Address") + return + } + } + if zb0005 > 0 { + zb0005-- + (*z).Locals.App, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Locals", "struct-from-array", "App") + return + } + } + if zb0005 > 0 { + err = msgp.ErrTooManyArrayFields(zb0005) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Locals", "struct-from-array") + return + } + } + } else { + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Locals") + return + } + if zb0006 { + (*z).Locals = LocalsRef{} + } + for zb0005 > 0 { + zb0005-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Locals") + return + } + switch string(field) { + case "d": + (*z).Locals.Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Locals", "Address") + return + } + case "p": + (*z).Locals.App, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Locals", "App") + return + } + default: + err = msgp.ErrNoField(string(field)) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Locals") + return + } + } + } + } + } + if zb0001 > 0 { + zb0001-- + var zb0007 int + var zb0008 bool + zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts) + if _, ok := err.(msgp.TypeError); ok { + zb0007, zb0008, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box") + return + } + if zb0007 > 0 { + zb0007-- + (*z).Box.Index, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box", "struct-from-array", "Index") + return + } + } + if zb0007 > 0 { + zb0007-- + var zb0009 int + zb0009, err = msgp.ReadBytesBytesHeader(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box", "struct-from-array", "Name") + return + } + if zb0009 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0009), uint64(config.MaxBytesKeyValueLen)) + return + } + (*z).Box.Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Box.Name) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box", "struct-from-array", "Name") + return + } + } + if zb0007 > 0 { + err = msgp.ErrTooManyArrayFields(zb0007) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box", "struct-from-array") + return + } + } + } else { + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box") + return + } + if zb0008 { + (*z).Box = BoxRef{} + } + for zb0007 > 0 { + zb0007-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box") + return + } + switch string(field) { + case "i": + (*z).Box.Index, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box", "Index") + return + } + case "n": + var zb0010 int + zb0010, err = msgp.ReadBytesBytesHeader(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box", "Name") + return + } + if zb0010 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0010), uint64(config.MaxBytesKeyValueLen)) + return + } + (*z).Box.Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Box.Name) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box", "Name") + return + } + default: + err = msgp.ErrNoField(string(field)) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Box") + return + } + } + } + } + } + if zb0001 > 0 { + err = msgp.ErrTooManyArrayFields(zb0001) + if err != nil { + err = msgp.WrapError(err, "struct-from-array") + return + } + } + } else { + if err != nil { + err = msgp.WrapError(err) + return + } + if zb0002 { + (*z) = ResourceRef{} + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch string(field) { + case "d": + bts, err = (*z).Address.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Address") + return + } + case "s": + bts, err = (*z).Asset.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Asset") + return + } + case "p": + bts, err = (*z).App.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "App") + return + } + case "h": + var zb0011 int + var zb0012 bool + zb0011, zb0012, bts, err = msgp.ReadMapHeaderBytes(bts) + if _, ok := err.(msgp.TypeError); ok { + zb0011, zb0012, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Holding") + return + } + if zb0011 > 0 { + zb0011-- + (*z).Holding.Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Holding", "struct-from-array", "Address") + return + } + } + if zb0011 > 0 { + zb0011-- + (*z).Holding.Asset, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Holding", "struct-from-array", "Asset") + return + } + } + if zb0011 > 0 { + err = msgp.ErrTooManyArrayFields(zb0011) + if err != nil { + err = msgp.WrapError(err, "Holding", "struct-from-array") + return + } + } + } else { + if err != nil { + err = msgp.WrapError(err, "Holding") + return + } + if zb0012 { + (*z).Holding = HoldingRef{} + } + for zb0011 > 0 { + zb0011-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err, "Holding") + return + } + switch string(field) { + case "d": + (*z).Holding.Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Holding", "Address") + return + } + case "s": + (*z).Holding.Asset, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Holding", "Asset") + return + } + default: + err = msgp.ErrNoField(string(field)) + if err != nil { + err = msgp.WrapError(err, "Holding") + return + } + } + } + } + case "l": + var zb0013 int + var zb0014 bool + zb0013, zb0014, bts, err = msgp.ReadMapHeaderBytes(bts) + if _, ok := err.(msgp.TypeError); ok { + zb0013, zb0014, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Locals") + return + } + if zb0013 > 0 { + zb0013-- + (*z).Locals.Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Locals", "struct-from-array", "Address") + return + } + } + if zb0013 > 0 { + zb0013-- + (*z).Locals.App, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Locals", "struct-from-array", "App") + return + } + } + if zb0013 > 0 { + err = msgp.ErrTooManyArrayFields(zb0013) + if err != nil { + err = msgp.WrapError(err, "Locals", "struct-from-array") + return + } + } + } else { + if err != nil { + err = msgp.WrapError(err, "Locals") + return + } + if zb0014 { + (*z).Locals = LocalsRef{} + } + for zb0013 > 0 { + zb0013-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err, "Locals") + return + } + switch string(field) { + case "d": + (*z).Locals.Address, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Locals", "Address") + return + } + case "p": + (*z).Locals.App, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Locals", "App") + return + } + default: + err = msgp.ErrNoField(string(field)) + if err != nil { + err = msgp.WrapError(err, "Locals") + return + } + } + } + } + case "b": + var zb0015 int + var zb0016 bool + zb0015, zb0016, bts, err = msgp.ReadMapHeaderBytes(bts) + if _, ok := err.(msgp.TypeError); ok { + zb0015, zb0016, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Box") + return + } + if zb0015 > 0 { + zb0015-- + (*z).Box.Index, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Box", "struct-from-array", "Index") + return + } + } + if zb0015 > 0 { + zb0015-- + var zb0017 int + zb0017, err = msgp.ReadBytesBytesHeader(bts) + if err != nil { + err = msgp.WrapError(err, "Box", "struct-from-array", "Name") + return + } + if zb0017 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0017), uint64(config.MaxBytesKeyValueLen)) + return + } + (*z).Box.Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Box.Name) + if err != nil { + err = msgp.WrapError(err, "Box", "struct-from-array", "Name") + return + } + } + if zb0015 > 0 { + err = msgp.ErrTooManyArrayFields(zb0015) + if err != nil { + err = msgp.WrapError(err, "Box", "struct-from-array") + return + } + } + } else { + if err != nil { + err = msgp.WrapError(err, "Box") + return + } + if zb0016 { + (*z).Box = BoxRef{} + } + for zb0015 > 0 { + zb0015-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err, "Box") + return + } + switch string(field) { + case "i": + (*z).Box.Index, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Box", "Index") + return + } + case "n": + var zb0018 int + zb0018, err = msgp.ReadBytesBytesHeader(bts) + if err != nil { + err = msgp.WrapError(err, "Box", "Name") + return + } + if zb0018 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0018), uint64(config.MaxBytesKeyValueLen)) + return + } + (*z).Box.Name, bts, err = msgp.ReadBytesBytes(bts, (*z).Box.Name) + if err != nil { + err = msgp.WrapError(err, "Box", "Name") + return + } + default: + err = msgp.ErrNoField(string(field)) + if err != nil { + err = msgp.WrapError(err, "Box") + return + } + } + } + } + default: + err = msgp.ErrNoField(string(field)) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + } + o = bts + return +} + +func (z *ResourceRef) UnmarshalMsg(bts []byte) (o []byte, err error) { return z.UnmarshalMsgWithState(bts, msgp.DefaultUnmarshalState) } -func (_ *Payset) CanUnmarshalMsg(z interface{}) bool { - _, ok := (z).(*Payset) +func (_ *ResourceRef) CanUnmarshalMsg(z interface{}) bool { + _, ok := (z).(*ResourceRef) return ok } // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z Payset) Msgsize() (s int) { - s = msgp.ArrayHeaderSize - for za0001 := range z { - s += z[za0001].Msgsize() - } +func (z *ResourceRef) Msgsize() (s int) { + s = 1 + 2 + (*z).Address.Msgsize() + 2 + (*z).Asset.Msgsize() + 2 + (*z).App.Msgsize() + 2 + 1 + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + 1 + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + 1 + 2 + msgp.Uint64Size + 2 + msgp.BytesPrefixSize + len((*z).Box.Name) return } // MsgIsZero returns whether this is a zero value -func (z Payset) MsgIsZero() bool { - return len(z) == 0 +func (z *ResourceRef) MsgIsZero() bool { + return ((*z).Address.MsgIsZero()) && ((*z).Asset.MsgIsZero()) && ((*z).App.MsgIsZero()) && (((*z).Holding.Address == 0) && ((*z).Holding.Asset == 0)) && (((*z).Locals.Address == 0) && ((*z).Locals.App == 0)) && (((*z).Box.Index == 0) && (len((*z).Box.Name) == 0)) } // MaxSize returns a maximum valid message size for this message type -func PaysetMaxSize() (s int) { - // Calculating size of slice: z - s += msgp.ArrayHeaderSize + ((100000) * (SignedTxnInBlockMaxSize())) +func ResourceRefMaxSize() (s int) { + s = 1 + 2 + basics.AddressMaxSize() + 2 + basics.AssetIndexMaxSize() + 2 + basics.AppIndexMaxSize() + 2 + 1 + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + 1 + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + 1 + 2 + msgp.Uint64Size + 2 + msgp.BytesPrefixSize + config.MaxBytesKeyValueLen return } @@ -5228,224 +6348,240 @@ func StateProofTxnFieldsMaxSize() (s int) { func (z *Transaction) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0007Len := uint32(48) - var zb0007Mask uint64 /* 57 bits */ + zb0008Len := uint32(49) + var zb0008Mask uint64 /* 58 bits */ if (*z).AssetTransferTxnFields.AssetAmount == 0 { - zb0007Len-- - zb0007Mask |= 0x200 + zb0008Len-- + zb0008Mask |= 0x200 } if (*z).AssetTransferTxnFields.AssetCloseTo.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x400 + zb0008Len-- + zb0008Mask |= 0x400 } if (*z).AssetFreezeTxnFields.AssetFrozen == false { - zb0007Len-- - zb0007Mask |= 0x800 + zb0008Len-- + zb0008Mask |= 0x800 + } + if len((*z).ApplicationCallTxnFields.Access) == 0 { + zb0008Len-- + zb0008Mask |= 0x1000 } if (*z).PaymentTxnFields.Amount.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x1000 + zb0008Len-- + zb0008Mask |= 0x2000 } if len((*z).ApplicationCallTxnFields.ApplicationArgs) == 0 { - zb0007Len-- - zb0007Mask |= 0x2000 + zb0008Len-- + zb0008Mask |= 0x4000 } if (*z).ApplicationCallTxnFields.OnCompletion == 0 { - zb0007Len-- - zb0007Mask |= 0x4000 + zb0008Len-- + zb0008Mask |= 0x8000 } if len((*z).ApplicationCallTxnFields.ApprovalProgram) == 0 { - zb0007Len-- - zb0007Mask |= 0x8000 + zb0008Len-- + zb0008Mask |= 0x10000 } if (*z).AssetConfigTxnFields.AssetParams.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x10000 + zb0008Len-- + zb0008Mask |= 0x20000 } if len((*z).ApplicationCallTxnFields.ForeignAssets) == 0 { - zb0007Len-- - zb0007Mask |= 0x20000 + zb0008Len-- + zb0008Mask |= 0x40000 } if len((*z).ApplicationCallTxnFields.Accounts) == 0 { - zb0007Len-- - zb0007Mask |= 0x40000 + zb0008Len-- + zb0008Mask |= 0x80000 } if len((*z).ApplicationCallTxnFields.Boxes) == 0 { - zb0007Len-- - zb0007Mask |= 0x80000 + zb0008Len-- + zb0008Mask |= 0x100000 } if (*z).ApplicationCallTxnFields.ExtraProgramPages == 0 { - zb0007Len-- - zb0007Mask |= 0x100000 + zb0008Len-- + zb0008Mask |= 0x200000 } if len((*z).ApplicationCallTxnFields.ForeignApps) == 0 { - zb0007Len-- - zb0007Mask |= 0x200000 + zb0008Len-- + zb0008Mask |= 0x400000 } if (*z).ApplicationCallTxnFields.GlobalStateSchema.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x400000 + zb0008Len-- + zb0008Mask |= 0x800000 } if (*z).ApplicationCallTxnFields.ApplicationID.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x800000 + zb0008Len-- + zb0008Mask |= 0x1000000 } if (*z).ApplicationCallTxnFields.LocalStateSchema.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x1000000 + zb0008Len-- + zb0008Mask |= 0x2000000 } if (*z).ApplicationCallTxnFields.RejectVersion == 0 { - zb0007Len-- - zb0007Mask |= 0x2000000 + zb0008Len-- + zb0008Mask |= 0x4000000 } if len((*z).ApplicationCallTxnFields.ClearStateProgram) == 0 { - zb0007Len-- - zb0007Mask |= 0x4000000 + zb0008Len-- + zb0008Mask |= 0x8000000 } if (*z).AssetTransferTxnFields.AssetReceiver.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x8000000 + zb0008Len-- + zb0008Mask |= 0x10000000 } if (*z).AssetTransferTxnFields.AssetSender.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x10000000 + zb0008Len-- + zb0008Mask |= 0x20000000 } if (*z).AssetConfigTxnFields.ConfigAsset.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x20000000 + zb0008Len-- + zb0008Mask |= 0x40000000 } if (*z).PaymentTxnFields.CloseRemainderTo.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x40000000 + zb0008Len-- + zb0008Mask |= 0x80000000 } if (*z).AssetFreezeTxnFields.FreezeAccount.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x80000000 + zb0008Len-- + zb0008Mask |= 0x100000000 } if (*z).AssetFreezeTxnFields.FreezeAsset.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x100000000 + zb0008Len-- + zb0008Mask |= 0x200000000 } if (*z).Header.Fee.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x200000000 + zb0008Len-- + zb0008Mask |= 0x400000000 } if (*z).Header.FirstValid.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x400000000 + zb0008Len-- + zb0008Mask |= 0x800000000 } if (*z).Header.GenesisID == "" { - zb0007Len-- - zb0007Mask |= 0x800000000 + zb0008Len-- + zb0008Mask |= 0x1000000000 } if (*z).Header.GenesisHash.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x1000000000 + zb0008Len-- + zb0008Mask |= 0x2000000000 } if (*z).Header.Group.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x2000000000 + zb0008Len-- + zb0008Mask |= 0x4000000000 } if (*z).HeartbeatTxnFields == nil { - zb0007Len-- - zb0007Mask |= 0x4000000000 + zb0008Len-- + zb0008Mask |= 0x8000000000 } if (*z).Header.LastValid.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x8000000000 + zb0008Len-- + zb0008Mask |= 0x10000000000 } if (*z).Header.Lease == ([32]byte{}) { - zb0007Len-- - zb0007Mask |= 0x10000000000 + zb0008Len-- + zb0008Mask |= 0x20000000000 } if (*z).KeyregTxnFields.Nonparticipation == false { - zb0007Len-- - zb0007Mask |= 0x20000000000 + zb0008Len-- + zb0008Mask |= 0x40000000000 } if len((*z).Header.Note) == 0 { - zb0007Len-- - zb0007Mask |= 0x40000000000 + zb0008Len-- + zb0008Mask |= 0x80000000000 } if (*z).PaymentTxnFields.Receiver.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x80000000000 + zb0008Len-- + zb0008Mask |= 0x100000000000 } if (*z).Header.RekeyTo.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x100000000000 + zb0008Len-- + zb0008Mask |= 0x200000000000 } if (*z).KeyregTxnFields.SelectionPK.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x200000000000 + zb0008Len-- + zb0008Mask |= 0x400000000000 } if (*z).Header.Sender.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x400000000000 + zb0008Len-- + zb0008Mask |= 0x800000000000 } if (*z).StateProofTxnFields.StateProof.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x800000000000 + zb0008Len-- + zb0008Mask |= 0x1000000000000 } if (*z).StateProofTxnFields.Message.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x1000000000000 + zb0008Len-- + zb0008Mask |= 0x2000000000000 } if (*z).KeyregTxnFields.StateProofPK.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x2000000000000 + zb0008Len-- + zb0008Mask |= 0x4000000000000 } if (*z).StateProofTxnFields.StateProofType.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x4000000000000 + zb0008Len-- + zb0008Mask |= 0x8000000000000 } if (*z).Type.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x8000000000000 + zb0008Len-- + zb0008Mask |= 0x10000000000000 } if (*z).KeyregTxnFields.VoteFirst.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x10000000000000 + zb0008Len-- + zb0008Mask |= 0x20000000000000 } if (*z).KeyregTxnFields.VoteKeyDilution == 0 { - zb0007Len-- - zb0007Mask |= 0x20000000000000 + zb0008Len-- + zb0008Mask |= 0x40000000000000 } if (*z).KeyregTxnFields.VotePK.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x40000000000000 + zb0008Len-- + zb0008Mask |= 0x80000000000000 } if (*z).KeyregTxnFields.VoteLast.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x80000000000000 + zb0008Len-- + zb0008Mask |= 0x100000000000000 } if (*z).AssetTransferTxnFields.XferAsset.MsgIsZero() { - zb0007Len-- - zb0007Mask |= 0x100000000000000 + zb0008Len-- + zb0008Mask |= 0x200000000000000 } - // variable map header, size zb0007Len - o = msgp.AppendMapHeader(o, zb0007Len) - if zb0007Len != 0 { - if (zb0007Mask & 0x200) == 0 { // if not empty + // variable map header, size zb0008Len + o = msgp.AppendMapHeader(o, zb0008Len) + if zb0008Len != 0 { + if (zb0008Mask & 0x200) == 0 { // if not empty // string "aamt" o = append(o, 0xa4, 0x61, 0x61, 0x6d, 0x74) o = msgp.AppendUint64(o, (*z).AssetTransferTxnFields.AssetAmount) } - if (zb0007Mask & 0x400) == 0 { // if not empty + if (zb0008Mask & 0x400) == 0 { // if not empty // string "aclose" o = append(o, 0xa6, 0x61, 0x63, 0x6c, 0x6f, 0x73, 0x65) o = (*z).AssetTransferTxnFields.AssetCloseTo.MarshalMsg(o) } - if (zb0007Mask & 0x800) == 0 { // if not empty + if (zb0008Mask & 0x800) == 0 { // if not empty // string "afrz" o = append(o, 0xa4, 0x61, 0x66, 0x72, 0x7a) o = msgp.AppendBool(o, (*z).AssetFreezeTxnFields.AssetFrozen) } - if (zb0007Mask & 0x1000) == 0 { // if not empty + if (zb0008Mask & 0x1000) == 0 { // if not empty + // string "al" + o = append(o, 0xa2, 0x61, 0x6c) + if (*z).ApplicationCallTxnFields.Access == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len((*z).ApplicationCallTxnFields.Access))) + } + for zb0006 := range (*z).ApplicationCallTxnFields.Access { + o = (*z).ApplicationCallTxnFields.Access[zb0006].MarshalMsg(o) + } + } + if (zb0008Mask & 0x2000) == 0 { // if not empty // string "amt" o = append(o, 0xa3, 0x61, 0x6d, 0x74) o = (*z).PaymentTxnFields.Amount.MarshalMsg(o) } - if (zb0007Mask & 0x2000) == 0 { // if not empty + if (zb0008Mask & 0x4000) == 0 { // if not empty // string "apaa" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x61) if (*z).ApplicationCallTxnFields.ApplicationArgs == nil { @@ -5457,22 +6593,22 @@ func (z *Transaction) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendBytes(o, (*z).ApplicationCallTxnFields.ApplicationArgs[zb0002]) } } - if (zb0007Mask & 0x4000) == 0 { // if not empty + if (zb0008Mask & 0x8000) == 0 { // if not empty // string "apan" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x6e) o = msgp.AppendUint64(o, uint64((*z).ApplicationCallTxnFields.OnCompletion)) } - if (zb0007Mask & 0x8000) == 0 { // if not empty + if (zb0008Mask & 0x10000) == 0 { // if not empty // string "apap" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x70) o = msgp.AppendBytes(o, (*z).ApplicationCallTxnFields.ApprovalProgram) } - if (zb0007Mask & 0x10000) == 0 { // if not empty + if (zb0008Mask & 0x20000) == 0 { // if not empty // string "apar" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x72) o = (*z).AssetConfigTxnFields.AssetParams.MarshalMsg(o) } - if (zb0007Mask & 0x20000) == 0 { // if not empty + if (zb0008Mask & 0x40000) == 0 { // if not empty // string "apas" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x73) if (*z).ApplicationCallTxnFields.ForeignAssets == nil { @@ -5480,11 +6616,11 @@ func (z *Transaction) MarshalMsg(b []byte) (o []byte) { } else { o = msgp.AppendArrayHeader(o, uint32(len((*z).ApplicationCallTxnFields.ForeignAssets))) } - for zb0006 := range (*z).ApplicationCallTxnFields.ForeignAssets { - o = (*z).ApplicationCallTxnFields.ForeignAssets[zb0006].MarshalMsg(o) + for zb0004 := range (*z).ApplicationCallTxnFields.ForeignAssets { + o = (*z).ApplicationCallTxnFields.ForeignAssets[zb0004].MarshalMsg(o) } } - if (zb0007Mask & 0x40000) == 0 { // if not empty + if (zb0008Mask & 0x80000) == 0 { // if not empty // string "apat" o = append(o, 0xa4, 0x61, 0x70, 0x61, 0x74) if (*z).ApplicationCallTxnFields.Accounts == nil { @@ -5496,7 +6632,7 @@ func (z *Transaction) MarshalMsg(b []byte) (o []byte) { o = (*z).ApplicationCallTxnFields.Accounts[zb0003].MarshalMsg(o) } } - if (zb0007Mask & 0x80000) == 0 { // if not empty + if (zb0008Mask & 0x100000) == 0 { // if not empty // string "apbx" o = append(o, 0xa4, 0x61, 0x70, 0x62, 0x78) if (*z).ApplicationCallTxnFields.Boxes == nil { @@ -5504,38 +6640,38 @@ func (z *Transaction) MarshalMsg(b []byte) (o []byte) { } else { o = msgp.AppendArrayHeader(o, uint32(len((*z).ApplicationCallTxnFields.Boxes))) } - for zb0005 := range (*z).ApplicationCallTxnFields.Boxes { + for zb0007 := range (*z).ApplicationCallTxnFields.Boxes { // omitempty: check for empty values - zb0008Len := uint32(2) - var zb0008Mask uint8 /* 3 bits */ - if (*z).ApplicationCallTxnFields.Boxes[zb0005].Index == 0 { - zb0008Len-- - zb0008Mask |= 0x2 - } - if len((*z).ApplicationCallTxnFields.Boxes[zb0005].Name) == 0 { - zb0008Len-- - zb0008Mask |= 0x4 - } - // variable map header, size zb0008Len - o = append(o, 0x80|uint8(zb0008Len)) - if (zb0008Mask & 0x2) == 0 { // if not empty + zb0009Len := uint32(2) + var zb0009Mask uint8 /* 3 bits */ + if (*z).ApplicationCallTxnFields.Boxes[zb0007].Index == 0 { + zb0009Len-- + zb0009Mask |= 0x2 + } + if len((*z).ApplicationCallTxnFields.Boxes[zb0007].Name) == 0 { + zb0009Len-- + zb0009Mask |= 0x4 + } + // variable map header, size zb0009Len + o = append(o, 0x80|uint8(zb0009Len)) + if (zb0009Mask & 0x2) == 0 { // if not empty // string "i" o = append(o, 0xa1, 0x69) - o = msgp.AppendUint64(o, (*z).ApplicationCallTxnFields.Boxes[zb0005].Index) + o = msgp.AppendUint64(o, (*z).ApplicationCallTxnFields.Boxes[zb0007].Index) } - if (zb0008Mask & 0x4) == 0 { // if not empty + if (zb0009Mask & 0x4) == 0 { // if not empty // string "n" o = append(o, 0xa1, 0x6e) - o = msgp.AppendBytes(o, (*z).ApplicationCallTxnFields.Boxes[zb0005].Name) + o = msgp.AppendBytes(o, (*z).ApplicationCallTxnFields.Boxes[zb0007].Name) } } } - if (zb0007Mask & 0x100000) == 0 { // if not empty + if (zb0008Mask & 0x200000) == 0 { // if not empty // string "apep" o = append(o, 0xa4, 0x61, 0x70, 0x65, 0x70) o = msgp.AppendUint32(o, (*z).ApplicationCallTxnFields.ExtraProgramPages) } - if (zb0007Mask & 0x200000) == 0 { // if not empty + if (zb0008Mask & 0x400000) == 0 { // if not empty // string "apfa" o = append(o, 0xa4, 0x61, 0x70, 0x66, 0x61) if (*z).ApplicationCallTxnFields.ForeignApps == nil { @@ -5543,91 +6679,91 @@ func (z *Transaction) MarshalMsg(b []byte) (o []byte) { } else { o = msgp.AppendArrayHeader(o, uint32(len((*z).ApplicationCallTxnFields.ForeignApps))) } - for zb0004 := range (*z).ApplicationCallTxnFields.ForeignApps { - o = (*z).ApplicationCallTxnFields.ForeignApps[zb0004].MarshalMsg(o) + for zb0005 := range (*z).ApplicationCallTxnFields.ForeignApps { + o = (*z).ApplicationCallTxnFields.ForeignApps[zb0005].MarshalMsg(o) } } - if (zb0007Mask & 0x400000) == 0 { // if not empty + if (zb0008Mask & 0x800000) == 0 { // if not empty // string "apgs" o = append(o, 0xa4, 0x61, 0x70, 0x67, 0x73) o = (*z).ApplicationCallTxnFields.GlobalStateSchema.MarshalMsg(o) } - if (zb0007Mask & 0x800000) == 0 { // if not empty + if (zb0008Mask & 0x1000000) == 0 { // if not empty // string "apid" o = append(o, 0xa4, 0x61, 0x70, 0x69, 0x64) o = (*z).ApplicationCallTxnFields.ApplicationID.MarshalMsg(o) } - if (zb0007Mask & 0x1000000) == 0 { // if not empty + if (zb0008Mask & 0x2000000) == 0 { // if not empty // string "apls" o = append(o, 0xa4, 0x61, 0x70, 0x6c, 0x73) o = (*z).ApplicationCallTxnFields.LocalStateSchema.MarshalMsg(o) } - if (zb0007Mask & 0x2000000) == 0 { // if not empty + if (zb0008Mask & 0x4000000) == 0 { // if not empty // string "aprv" o = append(o, 0xa4, 0x61, 0x70, 0x72, 0x76) o = msgp.AppendUint64(o, (*z).ApplicationCallTxnFields.RejectVersion) } - if (zb0007Mask & 0x4000000) == 0 { // if not empty + if (zb0008Mask & 0x8000000) == 0 { // if not empty // string "apsu" o = append(o, 0xa4, 0x61, 0x70, 0x73, 0x75) o = msgp.AppendBytes(o, (*z).ApplicationCallTxnFields.ClearStateProgram) } - if (zb0007Mask & 0x8000000) == 0 { // if not empty + if (zb0008Mask & 0x10000000) == 0 { // if not empty // string "arcv" o = append(o, 0xa4, 0x61, 0x72, 0x63, 0x76) o = (*z).AssetTransferTxnFields.AssetReceiver.MarshalMsg(o) } - if (zb0007Mask & 0x10000000) == 0 { // if not empty + if (zb0008Mask & 0x20000000) == 0 { // if not empty // string "asnd" o = append(o, 0xa4, 0x61, 0x73, 0x6e, 0x64) o = (*z).AssetTransferTxnFields.AssetSender.MarshalMsg(o) } - if (zb0007Mask & 0x20000000) == 0 { // if not empty + if (zb0008Mask & 0x40000000) == 0 { // if not empty // string "caid" o = append(o, 0xa4, 0x63, 0x61, 0x69, 0x64) o = (*z).AssetConfigTxnFields.ConfigAsset.MarshalMsg(o) } - if (zb0007Mask & 0x40000000) == 0 { // if not empty + if (zb0008Mask & 0x80000000) == 0 { // if not empty // string "close" o = append(o, 0xa5, 0x63, 0x6c, 0x6f, 0x73, 0x65) o = (*z).PaymentTxnFields.CloseRemainderTo.MarshalMsg(o) } - if (zb0007Mask & 0x80000000) == 0 { // if not empty + if (zb0008Mask & 0x100000000) == 0 { // if not empty // string "fadd" o = append(o, 0xa4, 0x66, 0x61, 0x64, 0x64) o = (*z).AssetFreezeTxnFields.FreezeAccount.MarshalMsg(o) } - if (zb0007Mask & 0x100000000) == 0 { // if not empty + if (zb0008Mask & 0x200000000) == 0 { // if not empty // string "faid" o = append(o, 0xa4, 0x66, 0x61, 0x69, 0x64) o = (*z).AssetFreezeTxnFields.FreezeAsset.MarshalMsg(o) } - if (zb0007Mask & 0x200000000) == 0 { // if not empty + if (zb0008Mask & 0x400000000) == 0 { // if not empty // string "fee" o = append(o, 0xa3, 0x66, 0x65, 0x65) o = (*z).Header.Fee.MarshalMsg(o) } - if (zb0007Mask & 0x400000000) == 0 { // if not empty + if (zb0008Mask & 0x800000000) == 0 { // if not empty // string "fv" o = append(o, 0xa2, 0x66, 0x76) o = (*z).Header.FirstValid.MarshalMsg(o) } - if (zb0007Mask & 0x800000000) == 0 { // if not empty + if (zb0008Mask & 0x1000000000) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).Header.GenesisID) } - if (zb0007Mask & 0x1000000000) == 0 { // if not empty + if (zb0008Mask & 0x2000000000) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).Header.GenesisHash.MarshalMsg(o) } - if (zb0007Mask & 0x2000000000) == 0 { // if not empty + if (zb0008Mask & 0x4000000000) == 0 { // if not empty // string "grp" o = append(o, 0xa3, 0x67, 0x72, 0x70) o = (*z).Header.Group.MarshalMsg(o) } - if (zb0007Mask & 0x4000000000) == 0 { // if not empty + if (zb0008Mask & 0x8000000000) == 0 { // if not empty // string "hb" o = append(o, 0xa2, 0x68, 0x62) if (*z).HeartbeatTxnFields == nil { @@ -5636,92 +6772,92 @@ func (z *Transaction) MarshalMsg(b []byte) (o []byte) { o = (*z).HeartbeatTxnFields.MarshalMsg(o) } } - if (zb0007Mask & 0x8000000000) == 0 { // if not empty + if (zb0008Mask & 0x10000000000) == 0 { // if not empty // string "lv" o = append(o, 0xa2, 0x6c, 0x76) o = (*z).Header.LastValid.MarshalMsg(o) } - if (zb0007Mask & 0x10000000000) == 0 { // if not empty + if (zb0008Mask & 0x20000000000) == 0 { // if not empty // string "lx" o = append(o, 0xa2, 0x6c, 0x78) o = msgp.AppendBytes(o, ((*z).Header.Lease)[:]) } - if (zb0007Mask & 0x20000000000) == 0 { // if not empty + if (zb0008Mask & 0x40000000000) == 0 { // if not empty // string "nonpart" o = append(o, 0xa7, 0x6e, 0x6f, 0x6e, 0x70, 0x61, 0x72, 0x74) o = msgp.AppendBool(o, (*z).KeyregTxnFields.Nonparticipation) } - if (zb0007Mask & 0x40000000000) == 0 { // if not empty + if (zb0008Mask & 0x80000000000) == 0 { // if not empty // string "note" o = append(o, 0xa4, 0x6e, 0x6f, 0x74, 0x65) o = msgp.AppendBytes(o, (*z).Header.Note) } - if (zb0007Mask & 0x80000000000) == 0 { // if not empty + if (zb0008Mask & 0x100000000000) == 0 { // if not empty // string "rcv" o = append(o, 0xa3, 0x72, 0x63, 0x76) o = (*z).PaymentTxnFields.Receiver.MarshalMsg(o) } - if (zb0007Mask & 0x100000000000) == 0 { // if not empty + if (zb0008Mask & 0x200000000000) == 0 { // if not empty // string "rekey" o = append(o, 0xa5, 0x72, 0x65, 0x6b, 0x65, 0x79) o = (*z).Header.RekeyTo.MarshalMsg(o) } - if (zb0007Mask & 0x200000000000) == 0 { // if not empty + if (zb0008Mask & 0x400000000000) == 0 { // if not empty // string "selkey" o = append(o, 0xa6, 0x73, 0x65, 0x6c, 0x6b, 0x65, 0x79) o = (*z).KeyregTxnFields.SelectionPK.MarshalMsg(o) } - if (zb0007Mask & 0x400000000000) == 0 { // if not empty + if (zb0008Mask & 0x800000000000) == 0 { // if not empty // string "snd" o = append(o, 0xa3, 0x73, 0x6e, 0x64) o = (*z).Header.Sender.MarshalMsg(o) } - if (zb0007Mask & 0x800000000000) == 0 { // if not empty + if (zb0008Mask & 0x1000000000000) == 0 { // if not empty // string "sp" o = append(o, 0xa2, 0x73, 0x70) o = (*z).StateProofTxnFields.StateProof.MarshalMsg(o) } - if (zb0007Mask & 0x1000000000000) == 0 { // if not empty + if (zb0008Mask & 0x2000000000000) == 0 { // if not empty // string "spmsg" o = append(o, 0xa5, 0x73, 0x70, 0x6d, 0x73, 0x67) o = (*z).StateProofTxnFields.Message.MarshalMsg(o) } - if (zb0007Mask & 0x2000000000000) == 0 { // if not empty + if (zb0008Mask & 0x4000000000000) == 0 { // if not empty // string "sprfkey" o = append(o, 0xa7, 0x73, 0x70, 0x72, 0x66, 0x6b, 0x65, 0x79) o = (*z).KeyregTxnFields.StateProofPK.MarshalMsg(o) } - if (zb0007Mask & 0x4000000000000) == 0 { // if not empty + if (zb0008Mask & 0x8000000000000) == 0 { // if not empty // string "sptype" o = append(o, 0xa6, 0x73, 0x70, 0x74, 0x79, 0x70, 0x65) o = (*z).StateProofTxnFields.StateProofType.MarshalMsg(o) } - if (zb0007Mask & 0x8000000000000) == 0 { // if not empty + if (zb0008Mask & 0x10000000000000) == 0 { // if not empty // string "type" o = append(o, 0xa4, 0x74, 0x79, 0x70, 0x65) o = (*z).Type.MarshalMsg(o) } - if (zb0007Mask & 0x10000000000000) == 0 { // if not empty + if (zb0008Mask & 0x20000000000000) == 0 { // if not empty // string "votefst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x66, 0x73, 0x74) o = (*z).KeyregTxnFields.VoteFirst.MarshalMsg(o) } - if (zb0007Mask & 0x20000000000000) == 0 { // if not empty + if (zb0008Mask & 0x40000000000000) == 0 { // if not empty // string "votekd" o = append(o, 0xa6, 0x76, 0x6f, 0x74, 0x65, 0x6b, 0x64) o = msgp.AppendUint64(o, (*z).KeyregTxnFields.VoteKeyDilution) } - if (zb0007Mask & 0x40000000000000) == 0 { // if not empty + if (zb0008Mask & 0x80000000000000) == 0 { // if not empty // string "votekey" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x6b, 0x65, 0x79) o = (*z).KeyregTxnFields.VotePK.MarshalMsg(o) } - if (zb0007Mask & 0x80000000000000) == 0 { // if not empty + if (zb0008Mask & 0x100000000000000) == 0 { // if not empty // string "votelst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x6c, 0x73, 0x74) o = (*z).KeyregTxnFields.VoteLast.MarshalMsg(o) } - if (zb0007Mask & 0x100000000000000) == 0 { // if not empty + if (zb0008Mask & 0x200000000000000) == 0 { // if not empty // string "xaid" o = append(o, 0xa4, 0x78, 0x61, 0x69, 0x64) o = (*z).AssetTransferTxnFields.XferAsset.MarshalMsg(o) @@ -5744,65 +6880,65 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) st.AllowableDepth-- var field []byte _ = field - var zb0007 int - var zb0008 bool - zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0008 int + var zb0009 bool + zb0008, zb0009, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0007, zb0008, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0008, zb0009, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err) return } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).Type.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Type") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).Header.Sender.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Sender") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).Header.Fee.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Fee") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).Header.FirstValid.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FirstValid") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).Header.LastValid.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "LastValid") return } } - if zb0007 > 0 { - zb0007-- - var zb0009 int - zb0009, err = msgp.ReadBytesBytesHeader(bts) + if zb0008 > 0 { + zb0008-- + var zb0010 int + zb0010, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Note") return } - if zb0009 > config.MaxTxnNoteBytes { - err = msgp.ErrOverflow(uint64(zb0009), uint64(config.MaxTxnNoteBytes)) + if zb0010 > config.MaxTxnNoteBytes { + err = msgp.ErrOverflow(uint64(zb0010), uint64(config.MaxTxnNoteBytes)) return } (*z).Header.Note, bts, err = msgp.ReadBytesBytes(bts, (*z).Header.Note) @@ -5811,16 +6947,16 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } - if zb0007 > 0 { - zb0007-- - var zb0010 int - zb0010, err = msgp.ReadBytesBytesHeader(bts) + if zb0008 > 0 { + zb0008-- + var zb0011 int + zb0011, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisID") return } - if zb0010 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0010), uint64(config.MaxGenesisIDLen)) + if zb0011 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0011), uint64(config.MaxGenesisIDLen)) return } (*z).Header.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -5829,238 +6965,238 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).Header.GenesisHash.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisHash") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).Header.Group.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Group") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = msgp.ReadExactBytes(bts, ((*z).Header.Lease)[:]) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Lease") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).Header.RekeyTo.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RekeyTo") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).KeyregTxnFields.VotePK.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "VotePK") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).KeyregTxnFields.SelectionPK.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "SelectionPK") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).KeyregTxnFields.StateProofPK.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofPK") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).KeyregTxnFields.VoteFirst.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "VoteFirst") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).KeyregTxnFields.VoteLast.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "VoteLast") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- (*z).KeyregTxnFields.VoteKeyDilution, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "VoteKeyDilution") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- (*z).KeyregTxnFields.Nonparticipation, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Nonparticipation") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).PaymentTxnFields.Receiver.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Receiver") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).PaymentTxnFields.Amount.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Amount") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).PaymentTxnFields.CloseRemainderTo.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "CloseRemainderTo") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).AssetConfigTxnFields.ConfigAsset.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ConfigAsset") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).AssetConfigTxnFields.AssetParams.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetParams") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).AssetTransferTxnFields.XferAsset.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "XferAsset") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- (*z).AssetTransferTxnFields.AssetAmount, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetAmount") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).AssetTransferTxnFields.AssetSender.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetSender") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).AssetTransferTxnFields.AssetReceiver.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetReceiver") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).AssetTransferTxnFields.AssetCloseTo.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetCloseTo") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).AssetFreezeTxnFields.FreezeAccount.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FreezeAccount") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).AssetFreezeTxnFields.FreezeAsset.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FreezeAsset") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- (*z).AssetFreezeTxnFields.AssetFrozen, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetFrozen") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).ApplicationCallTxnFields.ApplicationID.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ApplicationID") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- { - var zb0011 uint64 - zb0011, bts, err = msgp.ReadUint64Bytes(bts) + var zb0012 uint64 + zb0012, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "OnCompletion") return } - (*z).ApplicationCallTxnFields.OnCompletion = OnCompletion(zb0011) + (*z).ApplicationCallTxnFields.OnCompletion = OnCompletion(zb0012) } } - if zb0007 > 0 { - zb0007-- - var zb0012 int - var zb0013 bool - zb0012, zb0013, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0008 > 0 { + zb0008-- + var zb0013 int + var zb0014 bool + zb0013, zb0014, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ApplicationArgs") return } - if zb0012 > encodedMaxApplicationArgs { - err = msgp.ErrOverflow(uint64(zb0012), uint64(encodedMaxApplicationArgs)) + if zb0013 > encodedMaxApplicationArgs { + err = msgp.ErrOverflow(uint64(zb0013), uint64(encodedMaxApplicationArgs)) err = msgp.WrapError(err, "struct-from-array", "ApplicationArgs") return } - if zb0013 { + if zb0014 { (*z).ApplicationCallTxnFields.ApplicationArgs = nil - } else if (*z).ApplicationCallTxnFields.ApplicationArgs != nil && cap((*z).ApplicationCallTxnFields.ApplicationArgs) >= zb0012 { - (*z).ApplicationCallTxnFields.ApplicationArgs = ((*z).ApplicationCallTxnFields.ApplicationArgs)[:zb0012] + } else if (*z).ApplicationCallTxnFields.ApplicationArgs != nil && cap((*z).ApplicationCallTxnFields.ApplicationArgs) >= zb0013 { + (*z).ApplicationCallTxnFields.ApplicationArgs = ((*z).ApplicationCallTxnFields.ApplicationArgs)[:zb0013] } else { - (*z).ApplicationCallTxnFields.ApplicationArgs = make([][]byte, zb0012) + (*z).ApplicationCallTxnFields.ApplicationArgs = make([][]byte, zb0013) } for zb0002 := range (*z).ApplicationCallTxnFields.ApplicationArgs { (*z).ApplicationCallTxnFields.ApplicationArgs[zb0002], bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.ApplicationArgs[zb0002]) @@ -6070,26 +7206,26 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } } } - if zb0007 > 0 { - zb0007-- - var zb0014 int - var zb0015 bool - zb0014, zb0015, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0008 > 0 { + zb0008-- + var zb0015 int + var zb0016 bool + zb0015, zb0016, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Accounts") return } - if zb0014 > encodedMaxAccounts { - err = msgp.ErrOverflow(uint64(zb0014), uint64(encodedMaxAccounts)) + if zb0015 > encodedMaxAccounts { + err = msgp.ErrOverflow(uint64(zb0015), uint64(encodedMaxAccounts)) err = msgp.WrapError(err, "struct-from-array", "Accounts") return } - if zb0015 { + if zb0016 { (*z).ApplicationCallTxnFields.Accounts = nil - } else if (*z).ApplicationCallTxnFields.Accounts != nil && cap((*z).ApplicationCallTxnFields.Accounts) >= zb0014 { - (*z).ApplicationCallTxnFields.Accounts = ((*z).ApplicationCallTxnFields.Accounts)[:zb0014] + } else if (*z).ApplicationCallTxnFields.Accounts != nil && cap((*z).ApplicationCallTxnFields.Accounts) >= zb0015 { + (*z).ApplicationCallTxnFields.Accounts = ((*z).ApplicationCallTxnFields.Accounts)[:zb0015] } else { - (*z).ApplicationCallTxnFields.Accounts = make([]basics.Address, zb0014) + (*z).ApplicationCallTxnFields.Accounts = make([]basics.Address, zb0015) } for zb0003 := range (*z).ApplicationCallTxnFields.Accounts { bts, err = (*z).ApplicationCallTxnFields.Accounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -6099,141 +7235,199 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } } } - if zb0007 > 0 { - zb0007-- - var zb0016 int - var zb0017 bool - zb0016, zb0017, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0008 > 0 { + zb0008-- + var zb0017 int + var zb0018 bool + zb0017, zb0018, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "ForeignAssets") + return + } + if zb0017 > encodedMaxForeignAssets { + err = msgp.ErrOverflow(uint64(zb0017), uint64(encodedMaxForeignAssets)) + err = msgp.WrapError(err, "struct-from-array", "ForeignAssets") + return + } + if zb0018 { + (*z).ApplicationCallTxnFields.ForeignAssets = nil + } else if (*z).ApplicationCallTxnFields.ForeignAssets != nil && cap((*z).ApplicationCallTxnFields.ForeignAssets) >= zb0017 { + (*z).ApplicationCallTxnFields.ForeignAssets = ((*z).ApplicationCallTxnFields.ForeignAssets)[:zb0017] + } else { + (*z).ApplicationCallTxnFields.ForeignAssets = make([]basics.AssetIndex, zb0017) + } + for zb0004 := range (*z).ApplicationCallTxnFields.ForeignAssets { + bts, err = (*z).ApplicationCallTxnFields.ForeignAssets[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "ForeignAssets", zb0004) + return + } + } + } + if zb0008 > 0 { + zb0008-- + var zb0019 int + var zb0020 bool + zb0019, zb0020, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ForeignApps") return } - if zb0016 > encodedMaxForeignApps { - err = msgp.ErrOverflow(uint64(zb0016), uint64(encodedMaxForeignApps)) + if zb0019 > encodedMaxForeignApps { + err = msgp.ErrOverflow(uint64(zb0019), uint64(encodedMaxForeignApps)) err = msgp.WrapError(err, "struct-from-array", "ForeignApps") return } - if zb0017 { + if zb0020 { (*z).ApplicationCallTxnFields.ForeignApps = nil - } else if (*z).ApplicationCallTxnFields.ForeignApps != nil && cap((*z).ApplicationCallTxnFields.ForeignApps) >= zb0016 { - (*z).ApplicationCallTxnFields.ForeignApps = ((*z).ApplicationCallTxnFields.ForeignApps)[:zb0016] + } else if (*z).ApplicationCallTxnFields.ForeignApps != nil && cap((*z).ApplicationCallTxnFields.ForeignApps) >= zb0019 { + (*z).ApplicationCallTxnFields.ForeignApps = ((*z).ApplicationCallTxnFields.ForeignApps)[:zb0019] } else { - (*z).ApplicationCallTxnFields.ForeignApps = make([]basics.AppIndex, zb0016) + (*z).ApplicationCallTxnFields.ForeignApps = make([]basics.AppIndex, zb0019) } - for zb0004 := range (*z).ApplicationCallTxnFields.ForeignApps { - bts, err = (*z).ApplicationCallTxnFields.ForeignApps[zb0004].UnmarshalMsgWithState(bts, st) + for zb0005 := range (*z).ApplicationCallTxnFields.ForeignApps { + bts, err = (*z).ApplicationCallTxnFields.ForeignApps[zb0005].UnmarshalMsgWithState(bts, st) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "ForeignApps", zb0004) + err = msgp.WrapError(err, "struct-from-array", "ForeignApps", zb0005) return } } } - if zb0007 > 0 { - zb0007-- - var zb0018 int - var zb0019 bool - zb0018, zb0019, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0008 > 0 { + zb0008-- + var zb0021 int + var zb0022 bool + zb0021, zb0022, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Access") + return + } + if zb0021 > encodedMaxAccess { + err = msgp.ErrOverflow(uint64(zb0021), uint64(encodedMaxAccess)) + err = msgp.WrapError(err, "struct-from-array", "Access") + return + } + if zb0022 { + (*z).ApplicationCallTxnFields.Access = nil + } else if (*z).ApplicationCallTxnFields.Access != nil && cap((*z).ApplicationCallTxnFields.Access) >= zb0021 { + (*z).ApplicationCallTxnFields.Access = ((*z).ApplicationCallTxnFields.Access)[:zb0021] + } else { + (*z).ApplicationCallTxnFields.Access = make([]ResourceRef, zb0021) + } + for zb0006 := range (*z).ApplicationCallTxnFields.Access { + bts, err = (*z).ApplicationCallTxnFields.Access[zb0006].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Access", zb0006) + return + } + } + } + if zb0008 > 0 { + zb0008-- + var zb0023 int + var zb0024 bool + zb0023, zb0024, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Boxes") return } - if zb0018 > encodedMaxBoxes { - err = msgp.ErrOverflow(uint64(zb0018), uint64(encodedMaxBoxes)) + if zb0023 > encodedMaxBoxes { + err = msgp.ErrOverflow(uint64(zb0023), uint64(encodedMaxBoxes)) err = msgp.WrapError(err, "struct-from-array", "Boxes") return } - if zb0019 { + if zb0024 { (*z).ApplicationCallTxnFields.Boxes = nil - } else if (*z).ApplicationCallTxnFields.Boxes != nil && cap((*z).ApplicationCallTxnFields.Boxes) >= zb0018 { - (*z).ApplicationCallTxnFields.Boxes = ((*z).ApplicationCallTxnFields.Boxes)[:zb0018] + } else if (*z).ApplicationCallTxnFields.Boxes != nil && cap((*z).ApplicationCallTxnFields.Boxes) >= zb0023 { + (*z).ApplicationCallTxnFields.Boxes = ((*z).ApplicationCallTxnFields.Boxes)[:zb0023] } else { - (*z).ApplicationCallTxnFields.Boxes = make([]BoxRef, zb0018) + (*z).ApplicationCallTxnFields.Boxes = make([]BoxRef, zb0023) } - for zb0005 := range (*z).ApplicationCallTxnFields.Boxes { - var zb0020 int - var zb0021 bool - zb0020, zb0021, bts, err = msgp.ReadMapHeaderBytes(bts) + for zb0007 := range (*z).ApplicationCallTxnFields.Boxes { + var zb0025 int + var zb0026 bool + zb0025, zb0026, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0020, zb0021, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0025, zb0026, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005) + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007) return } - if zb0020 > 0 { - zb0020-- - (*z).ApplicationCallTxnFields.Boxes[zb0005].Index, bts, err = msgp.ReadUint64Bytes(bts) + if zb0025 > 0 { + zb0025-- + (*z).ApplicationCallTxnFields.Boxes[zb0007].Index, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005, "struct-from-array", "Index") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007, "struct-from-array", "Index") return } } - if zb0020 > 0 { - zb0020-- - var zb0022 int - zb0022, err = msgp.ReadBytesBytesHeader(bts) + if zb0025 > 0 { + zb0025-- + var zb0027 int + zb0027, err = msgp.ReadBytesBytesHeader(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005, "struct-from-array", "Name") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007, "struct-from-array", "Name") return } - if zb0022 > config.MaxBytesKeyValueLen { - err = msgp.ErrOverflow(uint64(zb0022), uint64(config.MaxBytesKeyValueLen)) + if zb0027 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0027), uint64(config.MaxBytesKeyValueLen)) return } - (*z).ApplicationCallTxnFields.Boxes[zb0005].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.Boxes[zb0005].Name) + (*z).ApplicationCallTxnFields.Boxes[zb0007].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.Boxes[zb0007].Name) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005, "struct-from-array", "Name") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007, "struct-from-array", "Name") return } } - if zb0020 > 0 { - err = msgp.ErrTooManyArrayFields(zb0020) + if zb0025 > 0 { + err = msgp.ErrTooManyArrayFields(zb0025) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005, "struct-from-array") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007, "struct-from-array") return } } } else { if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005) + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007) return } - if zb0021 { - (*z).ApplicationCallTxnFields.Boxes[zb0005] = BoxRef{} + if zb0026 { + (*z).ApplicationCallTxnFields.Boxes[zb0007] = BoxRef{} } - for zb0020 > 0 { - zb0020-- + for zb0025 > 0 { + zb0025-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005) + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007) return } switch string(field) { case "i": - (*z).ApplicationCallTxnFields.Boxes[zb0005].Index, bts, err = msgp.ReadUint64Bytes(bts) + (*z).ApplicationCallTxnFields.Boxes[zb0007].Index, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005, "Index") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007, "Index") return } case "n": - var zb0023 int - zb0023, err = msgp.ReadBytesBytesHeader(bts) + var zb0028 int + zb0028, err = msgp.ReadBytesBytesHeader(bts) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005, "Name") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007, "Name") return } - if zb0023 > config.MaxBytesKeyValueLen { - err = msgp.ErrOverflow(uint64(zb0023), uint64(config.MaxBytesKeyValueLen)) + if zb0028 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0028), uint64(config.MaxBytesKeyValueLen)) return } - (*z).ApplicationCallTxnFields.Boxes[zb0005].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.Boxes[zb0005].Name) + (*z).ApplicationCallTxnFields.Boxes[zb0007].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.Boxes[zb0007].Name) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005, "Name") + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007, "Name") return } default: err = msgp.ErrNoField(string(field)) if err != nil { - err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0005) + err = msgp.WrapError(err, "struct-from-array", "Boxes", zb0007) return } } @@ -6241,61 +7435,32 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } } } - if zb0007 > 0 { - zb0007-- - var zb0024 int - var zb0025 bool - zb0024, zb0025, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "struct-from-array", "ForeignAssets") - return - } - if zb0024 > encodedMaxForeignAssets { - err = msgp.ErrOverflow(uint64(zb0024), uint64(encodedMaxForeignAssets)) - err = msgp.WrapError(err, "struct-from-array", "ForeignAssets") - return - } - if zb0025 { - (*z).ApplicationCallTxnFields.ForeignAssets = nil - } else if (*z).ApplicationCallTxnFields.ForeignAssets != nil && cap((*z).ApplicationCallTxnFields.ForeignAssets) >= zb0024 { - (*z).ApplicationCallTxnFields.ForeignAssets = ((*z).ApplicationCallTxnFields.ForeignAssets)[:zb0024] - } else { - (*z).ApplicationCallTxnFields.ForeignAssets = make([]basics.AssetIndex, zb0024) - } - for zb0006 := range (*z).ApplicationCallTxnFields.ForeignAssets { - bts, err = (*z).ApplicationCallTxnFields.ForeignAssets[zb0006].UnmarshalMsgWithState(bts, st) - if err != nil { - err = msgp.WrapError(err, "struct-from-array", "ForeignAssets", zb0006) - return - } - } - } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).ApplicationCallTxnFields.LocalStateSchema.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "LocalStateSchema") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).ApplicationCallTxnFields.GlobalStateSchema.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GlobalStateSchema") return } } - if zb0007 > 0 { - zb0007-- - var zb0026 int - zb0026, err = msgp.ReadBytesBytesHeader(bts) + if zb0008 > 0 { + zb0008-- + var zb0029 int + zb0029, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ApprovalProgram") return } - if zb0026 > config.MaxAvailableAppProgramLen { - err = msgp.ErrOverflow(uint64(zb0026), uint64(config.MaxAvailableAppProgramLen)) + if zb0029 > config.MaxAvailableAppProgramLen { + err = msgp.ErrOverflow(uint64(zb0029), uint64(config.MaxAvailableAppProgramLen)) return } (*z).ApplicationCallTxnFields.ApprovalProgram, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.ApprovalProgram) @@ -6304,16 +7469,16 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } - if zb0007 > 0 { - zb0007-- - var zb0027 int - zb0027, err = msgp.ReadBytesBytesHeader(bts) + if zb0008 > 0 { + zb0008-- + var zb0030 int + zb0030, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ClearStateProgram") return } - if zb0027 > config.MaxAvailableAppProgramLen { - err = msgp.ErrOverflow(uint64(zb0027), uint64(config.MaxAvailableAppProgramLen)) + if zb0030 > config.MaxAvailableAppProgramLen { + err = msgp.ErrOverflow(uint64(zb0030), uint64(config.MaxAvailableAppProgramLen)) return } (*z).ApplicationCallTxnFields.ClearStateProgram, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.ClearStateProgram) @@ -6322,48 +7487,48 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- (*z).ApplicationCallTxnFields.ExtraProgramPages, bts, err = msgp.ReadUint32Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ExtraProgramPages") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- (*z).ApplicationCallTxnFields.RejectVersion, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RejectVersion") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).StateProofTxnFields.StateProofType.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofType") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).StateProofTxnFields.StateProof.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProof") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- bts, err = (*z).StateProofTxnFields.Message.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Message") return } } - if zb0007 > 0 { - zb0007-- + if zb0008 > 0 { + zb0008-- if msgp.IsNil(bts) { bts, err = msgp.ReadNilBytes(bts) if err != nil { @@ -6381,8 +7546,8 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } } } - if zb0007 > 0 { - err = msgp.ErrTooManyArrayFields(zb0007) + if zb0008 > 0 { + err = msgp.ErrTooManyArrayFields(zb0008) if err != nil { err = msgp.WrapError(err, "struct-from-array") return @@ -6393,11 +7558,11 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err) return } - if zb0008 { + if zb0009 { (*z) = Transaction{} } - for zb0007 > 0 { - zb0007-- + for zb0008 > 0 { + zb0008-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err) @@ -6435,14 +7600,14 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } case "note": - var zb0028 int - zb0028, err = msgp.ReadBytesBytesHeader(bts) + var zb0031 int + zb0031, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "Note") return } - if zb0028 > config.MaxTxnNoteBytes { - err = msgp.ErrOverflow(uint64(zb0028), uint64(config.MaxTxnNoteBytes)) + if zb0031 > config.MaxTxnNoteBytes { + err = msgp.ErrOverflow(uint64(zb0031), uint64(config.MaxTxnNoteBytes)) return } (*z).Header.Note, bts, err = msgp.ReadBytesBytes(bts, (*z).Header.Note) @@ -6451,14 +7616,14 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } case "gen": - var zb0029 int - zb0029, err = msgp.ReadBytesBytesHeader(bts) + var zb0032 int + zb0032, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "GenesisID") return } - if zb0029 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0029), uint64(config.MaxGenesisIDLen)) + if zb0032 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0032), uint64(config.MaxGenesisIDLen)) return } (*z).Header.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -6618,33 +7783,33 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } case "apan": { - var zb0030 uint64 - zb0030, bts, err = msgp.ReadUint64Bytes(bts) + var zb0033 uint64 + zb0033, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "OnCompletion") return } - (*z).ApplicationCallTxnFields.OnCompletion = OnCompletion(zb0030) + (*z).ApplicationCallTxnFields.OnCompletion = OnCompletion(zb0033) } case "apaa": - var zb0031 int - var zb0032 bool - zb0031, zb0032, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0034 int + var zb0035 bool + zb0034, zb0035, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ApplicationArgs") return } - if zb0031 > encodedMaxApplicationArgs { - err = msgp.ErrOverflow(uint64(zb0031), uint64(encodedMaxApplicationArgs)) + if zb0034 > encodedMaxApplicationArgs { + err = msgp.ErrOverflow(uint64(zb0034), uint64(encodedMaxApplicationArgs)) err = msgp.WrapError(err, "ApplicationArgs") return } - if zb0032 { + if zb0035 { (*z).ApplicationCallTxnFields.ApplicationArgs = nil - } else if (*z).ApplicationCallTxnFields.ApplicationArgs != nil && cap((*z).ApplicationCallTxnFields.ApplicationArgs) >= zb0031 { - (*z).ApplicationCallTxnFields.ApplicationArgs = ((*z).ApplicationCallTxnFields.ApplicationArgs)[:zb0031] + } else if (*z).ApplicationCallTxnFields.ApplicationArgs != nil && cap((*z).ApplicationCallTxnFields.ApplicationArgs) >= zb0034 { + (*z).ApplicationCallTxnFields.ApplicationArgs = ((*z).ApplicationCallTxnFields.ApplicationArgs)[:zb0034] } else { - (*z).ApplicationCallTxnFields.ApplicationArgs = make([][]byte, zb0031) + (*z).ApplicationCallTxnFields.ApplicationArgs = make([][]byte, zb0034) } for zb0002 := range (*z).ApplicationCallTxnFields.ApplicationArgs { (*z).ApplicationCallTxnFields.ApplicationArgs[zb0002], bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.ApplicationArgs[zb0002]) @@ -6654,24 +7819,24 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } } case "apat": - var zb0033 int - var zb0034 bool - zb0033, zb0034, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0036 int + var zb0037 bool + zb0036, zb0037, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "Accounts") return } - if zb0033 > encodedMaxAccounts { - err = msgp.ErrOverflow(uint64(zb0033), uint64(encodedMaxAccounts)) + if zb0036 > encodedMaxAccounts { + err = msgp.ErrOverflow(uint64(zb0036), uint64(encodedMaxAccounts)) err = msgp.WrapError(err, "Accounts") return } - if zb0034 { + if zb0037 { (*z).ApplicationCallTxnFields.Accounts = nil - } else if (*z).ApplicationCallTxnFields.Accounts != nil && cap((*z).ApplicationCallTxnFields.Accounts) >= zb0033 { - (*z).ApplicationCallTxnFields.Accounts = ((*z).ApplicationCallTxnFields.Accounts)[:zb0033] + } else if (*z).ApplicationCallTxnFields.Accounts != nil && cap((*z).ApplicationCallTxnFields.Accounts) >= zb0036 { + (*z).ApplicationCallTxnFields.Accounts = ((*z).ApplicationCallTxnFields.Accounts)[:zb0036] } else { - (*z).ApplicationCallTxnFields.Accounts = make([]basics.Address, zb0033) + (*z).ApplicationCallTxnFields.Accounts = make([]basics.Address, zb0036) } for zb0003 := range (*z).ApplicationCallTxnFields.Accounts { bts, err = (*z).ApplicationCallTxnFields.Accounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -6680,171 +7845,198 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } + case "apas": + var zb0038 int + var zb0039 bool + zb0038, zb0039, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "ForeignAssets") + return + } + if zb0038 > encodedMaxForeignAssets { + err = msgp.ErrOverflow(uint64(zb0038), uint64(encodedMaxForeignAssets)) + err = msgp.WrapError(err, "ForeignAssets") + return + } + if zb0039 { + (*z).ApplicationCallTxnFields.ForeignAssets = nil + } else if (*z).ApplicationCallTxnFields.ForeignAssets != nil && cap((*z).ApplicationCallTxnFields.ForeignAssets) >= zb0038 { + (*z).ApplicationCallTxnFields.ForeignAssets = ((*z).ApplicationCallTxnFields.ForeignAssets)[:zb0038] + } else { + (*z).ApplicationCallTxnFields.ForeignAssets = make([]basics.AssetIndex, zb0038) + } + for zb0004 := range (*z).ApplicationCallTxnFields.ForeignAssets { + bts, err = (*z).ApplicationCallTxnFields.ForeignAssets[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "ForeignAssets", zb0004) + return + } + } case "apfa": - var zb0035 int - var zb0036 bool - zb0035, zb0036, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0040 int + var zb0041 bool + zb0040, zb0041, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ForeignApps") return } - if zb0035 > encodedMaxForeignApps { - err = msgp.ErrOverflow(uint64(zb0035), uint64(encodedMaxForeignApps)) + if zb0040 > encodedMaxForeignApps { + err = msgp.ErrOverflow(uint64(zb0040), uint64(encodedMaxForeignApps)) err = msgp.WrapError(err, "ForeignApps") return } - if zb0036 { + if zb0041 { (*z).ApplicationCallTxnFields.ForeignApps = nil - } else if (*z).ApplicationCallTxnFields.ForeignApps != nil && cap((*z).ApplicationCallTxnFields.ForeignApps) >= zb0035 { - (*z).ApplicationCallTxnFields.ForeignApps = ((*z).ApplicationCallTxnFields.ForeignApps)[:zb0035] + } else if (*z).ApplicationCallTxnFields.ForeignApps != nil && cap((*z).ApplicationCallTxnFields.ForeignApps) >= zb0040 { + (*z).ApplicationCallTxnFields.ForeignApps = ((*z).ApplicationCallTxnFields.ForeignApps)[:zb0040] } else { - (*z).ApplicationCallTxnFields.ForeignApps = make([]basics.AppIndex, zb0035) + (*z).ApplicationCallTxnFields.ForeignApps = make([]basics.AppIndex, zb0040) } - for zb0004 := range (*z).ApplicationCallTxnFields.ForeignApps { - bts, err = (*z).ApplicationCallTxnFields.ForeignApps[zb0004].UnmarshalMsgWithState(bts, st) + for zb0005 := range (*z).ApplicationCallTxnFields.ForeignApps { + bts, err = (*z).ApplicationCallTxnFields.ForeignApps[zb0005].UnmarshalMsgWithState(bts, st) if err != nil { - err = msgp.WrapError(err, "ForeignApps", zb0004) + err = msgp.WrapError(err, "ForeignApps", zb0005) + return + } + } + case "al": + var zb0042 int + var zb0043 bool + zb0042, zb0043, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Access") + return + } + if zb0042 > encodedMaxAccess { + err = msgp.ErrOverflow(uint64(zb0042), uint64(encodedMaxAccess)) + err = msgp.WrapError(err, "Access") + return + } + if zb0043 { + (*z).ApplicationCallTxnFields.Access = nil + } else if (*z).ApplicationCallTxnFields.Access != nil && cap((*z).ApplicationCallTxnFields.Access) >= zb0042 { + (*z).ApplicationCallTxnFields.Access = ((*z).ApplicationCallTxnFields.Access)[:zb0042] + } else { + (*z).ApplicationCallTxnFields.Access = make([]ResourceRef, zb0042) + } + for zb0006 := range (*z).ApplicationCallTxnFields.Access { + bts, err = (*z).ApplicationCallTxnFields.Access[zb0006].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Access", zb0006) return } } case "apbx": - var zb0037 int - var zb0038 bool - zb0037, zb0038, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0044 int + var zb0045 bool + zb0044, zb0045, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "Boxes") return } - if zb0037 > encodedMaxBoxes { - err = msgp.ErrOverflow(uint64(zb0037), uint64(encodedMaxBoxes)) + if zb0044 > encodedMaxBoxes { + err = msgp.ErrOverflow(uint64(zb0044), uint64(encodedMaxBoxes)) err = msgp.WrapError(err, "Boxes") return } - if zb0038 { + if zb0045 { (*z).ApplicationCallTxnFields.Boxes = nil - } else if (*z).ApplicationCallTxnFields.Boxes != nil && cap((*z).ApplicationCallTxnFields.Boxes) >= zb0037 { - (*z).ApplicationCallTxnFields.Boxes = ((*z).ApplicationCallTxnFields.Boxes)[:zb0037] + } else if (*z).ApplicationCallTxnFields.Boxes != nil && cap((*z).ApplicationCallTxnFields.Boxes) >= zb0044 { + (*z).ApplicationCallTxnFields.Boxes = ((*z).ApplicationCallTxnFields.Boxes)[:zb0044] } else { - (*z).ApplicationCallTxnFields.Boxes = make([]BoxRef, zb0037) + (*z).ApplicationCallTxnFields.Boxes = make([]BoxRef, zb0044) } - for zb0005 := range (*z).ApplicationCallTxnFields.Boxes { - var zb0039 int - var zb0040 bool - zb0039, zb0040, bts, err = msgp.ReadMapHeaderBytes(bts) + for zb0007 := range (*z).ApplicationCallTxnFields.Boxes { + var zb0046 int + var zb0047 bool + zb0046, zb0047, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0039, zb0040, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0046, zb0047, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005) + err = msgp.WrapError(err, "Boxes", zb0007) return } - if zb0039 > 0 { - zb0039-- - (*z).ApplicationCallTxnFields.Boxes[zb0005].Index, bts, err = msgp.ReadUint64Bytes(bts) + if zb0046 > 0 { + zb0046-- + (*z).ApplicationCallTxnFields.Boxes[zb0007].Index, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005, "struct-from-array", "Index") + err = msgp.WrapError(err, "Boxes", zb0007, "struct-from-array", "Index") return } } - if zb0039 > 0 { - zb0039-- - var zb0041 int - zb0041, err = msgp.ReadBytesBytesHeader(bts) + if zb0046 > 0 { + zb0046-- + var zb0048 int + zb0048, err = msgp.ReadBytesBytesHeader(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005, "struct-from-array", "Name") + err = msgp.WrapError(err, "Boxes", zb0007, "struct-from-array", "Name") return } - if zb0041 > config.MaxBytesKeyValueLen { - err = msgp.ErrOverflow(uint64(zb0041), uint64(config.MaxBytesKeyValueLen)) + if zb0048 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0048), uint64(config.MaxBytesKeyValueLen)) return } - (*z).ApplicationCallTxnFields.Boxes[zb0005].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.Boxes[zb0005].Name) + (*z).ApplicationCallTxnFields.Boxes[zb0007].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.Boxes[zb0007].Name) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005, "struct-from-array", "Name") + err = msgp.WrapError(err, "Boxes", zb0007, "struct-from-array", "Name") return } } - if zb0039 > 0 { - err = msgp.ErrTooManyArrayFields(zb0039) + if zb0046 > 0 { + err = msgp.ErrTooManyArrayFields(zb0046) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005, "struct-from-array") + err = msgp.WrapError(err, "Boxes", zb0007, "struct-from-array") return } } } else { if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005) + err = msgp.WrapError(err, "Boxes", zb0007) return } - if zb0040 { - (*z).ApplicationCallTxnFields.Boxes[zb0005] = BoxRef{} + if zb0047 { + (*z).ApplicationCallTxnFields.Boxes[zb0007] = BoxRef{} } - for zb0039 > 0 { - zb0039-- + for zb0046 > 0 { + zb0046-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005) + err = msgp.WrapError(err, "Boxes", zb0007) return } switch string(field) { case "i": - (*z).ApplicationCallTxnFields.Boxes[zb0005].Index, bts, err = msgp.ReadUint64Bytes(bts) + (*z).ApplicationCallTxnFields.Boxes[zb0007].Index, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005, "Index") + err = msgp.WrapError(err, "Boxes", zb0007, "Index") return } case "n": - var zb0042 int - zb0042, err = msgp.ReadBytesBytesHeader(bts) + var zb0049 int + zb0049, err = msgp.ReadBytesBytesHeader(bts) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005, "Name") + err = msgp.WrapError(err, "Boxes", zb0007, "Name") return } - if zb0042 > config.MaxBytesKeyValueLen { - err = msgp.ErrOverflow(uint64(zb0042), uint64(config.MaxBytesKeyValueLen)) + if zb0049 > config.MaxBytesKeyValueLen { + err = msgp.ErrOverflow(uint64(zb0049), uint64(config.MaxBytesKeyValueLen)) return } - (*z).ApplicationCallTxnFields.Boxes[zb0005].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.Boxes[zb0005].Name) + (*z).ApplicationCallTxnFields.Boxes[zb0007].Name, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.Boxes[zb0007].Name) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005, "Name") + err = msgp.WrapError(err, "Boxes", zb0007, "Name") return } default: err = msgp.ErrNoField(string(field)) if err != nil { - err = msgp.WrapError(err, "Boxes", zb0005) + err = msgp.WrapError(err, "Boxes", zb0007) return } } } } } - case "apas": - var zb0043 int - var zb0044 bool - zb0043, zb0044, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "ForeignAssets") - return - } - if zb0043 > encodedMaxForeignAssets { - err = msgp.ErrOverflow(uint64(zb0043), uint64(encodedMaxForeignAssets)) - err = msgp.WrapError(err, "ForeignAssets") - return - } - if zb0044 { - (*z).ApplicationCallTxnFields.ForeignAssets = nil - } else if (*z).ApplicationCallTxnFields.ForeignAssets != nil && cap((*z).ApplicationCallTxnFields.ForeignAssets) >= zb0043 { - (*z).ApplicationCallTxnFields.ForeignAssets = ((*z).ApplicationCallTxnFields.ForeignAssets)[:zb0043] - } else { - (*z).ApplicationCallTxnFields.ForeignAssets = make([]basics.AssetIndex, zb0043) - } - for zb0006 := range (*z).ApplicationCallTxnFields.ForeignAssets { - bts, err = (*z).ApplicationCallTxnFields.ForeignAssets[zb0006].UnmarshalMsgWithState(bts, st) - if err != nil { - err = msgp.WrapError(err, "ForeignAssets", zb0006) - return - } - } case "apls": bts, err = (*z).ApplicationCallTxnFields.LocalStateSchema.UnmarshalMsgWithState(bts, st) if err != nil { @@ -6858,14 +8050,14 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } case "apap": - var zb0045 int - zb0045, err = msgp.ReadBytesBytesHeader(bts) + var zb0050 int + zb0050, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "ApprovalProgram") return } - if zb0045 > config.MaxAvailableAppProgramLen { - err = msgp.ErrOverflow(uint64(zb0045), uint64(config.MaxAvailableAppProgramLen)) + if zb0050 > config.MaxAvailableAppProgramLen { + err = msgp.ErrOverflow(uint64(zb0050), uint64(config.MaxAvailableAppProgramLen)) return } (*z).ApplicationCallTxnFields.ApprovalProgram, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.ApprovalProgram) @@ -6874,14 +8066,14 @@ func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } case "apsu": - var zb0046 int - zb0046, err = msgp.ReadBytesBytesHeader(bts) + var zb0051 int + zb0051, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "ClearStateProgram") return } - if zb0046 > config.MaxAvailableAppProgramLen { - err = msgp.ErrOverflow(uint64(zb0046), uint64(config.MaxAvailableAppProgramLen)) + if zb0051 > config.MaxAvailableAppProgramLen { + err = msgp.ErrOverflow(uint64(zb0051), uint64(config.MaxAvailableAppProgramLen)) return } (*z).ApplicationCallTxnFields.ClearStateProgram, bts, err = msgp.ReadBytesBytes(bts, (*z).ApplicationCallTxnFields.ClearStateProgram) @@ -6968,16 +8160,20 @@ func (z *Transaction) Msgsize() (s int) { s += (*z).ApplicationCallTxnFields.Accounts[zb0003].Msgsize() } s += 5 + msgp.ArrayHeaderSize - for zb0004 := range (*z).ApplicationCallTxnFields.ForeignApps { - s += (*z).ApplicationCallTxnFields.ForeignApps[zb0004].Msgsize() + for zb0004 := range (*z).ApplicationCallTxnFields.ForeignAssets { + s += (*z).ApplicationCallTxnFields.ForeignAssets[zb0004].Msgsize() } s += 5 + msgp.ArrayHeaderSize - for zb0005 := range (*z).ApplicationCallTxnFields.Boxes { - s += 1 + 2 + msgp.Uint64Size + 2 + msgp.BytesPrefixSize + len((*z).ApplicationCallTxnFields.Boxes[zb0005].Name) + for zb0005 := range (*z).ApplicationCallTxnFields.ForeignApps { + s += (*z).ApplicationCallTxnFields.ForeignApps[zb0005].Msgsize() + } + s += 3 + msgp.ArrayHeaderSize + for zb0006 := range (*z).ApplicationCallTxnFields.Access { + s += (*z).ApplicationCallTxnFields.Access[zb0006].Msgsize() } s += 5 + msgp.ArrayHeaderSize - for zb0006 := range (*z).ApplicationCallTxnFields.ForeignAssets { - s += (*z).ApplicationCallTxnFields.ForeignAssets[zb0006].Msgsize() + for zb0007 := range (*z).ApplicationCallTxnFields.Boxes { + s += 1 + 2 + msgp.Uint64Size + 2 + msgp.BytesPrefixSize + len((*z).ApplicationCallTxnFields.Boxes[zb0007].Name) } s += 5 + (*z).ApplicationCallTxnFields.LocalStateSchema.Msgsize() + 5 + (*z).ApplicationCallTxnFields.GlobalStateSchema.Msgsize() + 5 + msgp.BytesPrefixSize + len((*z).ApplicationCallTxnFields.ApprovalProgram) + 5 + msgp.BytesPrefixSize + len((*z).ApplicationCallTxnFields.ClearStateProgram) + 5 + msgp.Uint32Size + 5 + msgp.Uint64Size + 7 + (*z).StateProofTxnFields.StateProofType.Msgsize() + 3 + (*z).StateProofTxnFields.StateProof.Msgsize() + 6 + (*z).StateProofTxnFields.Message.Msgsize() + 3 if (*z).HeartbeatTxnFields == nil { @@ -6990,7 +8186,7 @@ func (z *Transaction) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *Transaction) MsgIsZero() bool { - return ((*z).Type.MsgIsZero()) && ((*z).Header.Sender.MsgIsZero()) && ((*z).Header.Fee.MsgIsZero()) && ((*z).Header.FirstValid.MsgIsZero()) && ((*z).Header.LastValid.MsgIsZero()) && (len((*z).Header.Note) == 0) && ((*z).Header.GenesisID == "") && ((*z).Header.GenesisHash.MsgIsZero()) && ((*z).Header.Group.MsgIsZero()) && ((*z).Header.Lease == ([32]byte{})) && ((*z).Header.RekeyTo.MsgIsZero()) && ((*z).KeyregTxnFields.VotePK.MsgIsZero()) && ((*z).KeyregTxnFields.SelectionPK.MsgIsZero()) && ((*z).KeyregTxnFields.StateProofPK.MsgIsZero()) && ((*z).KeyregTxnFields.VoteFirst.MsgIsZero()) && ((*z).KeyregTxnFields.VoteLast.MsgIsZero()) && ((*z).KeyregTxnFields.VoteKeyDilution == 0) && ((*z).KeyregTxnFields.Nonparticipation == false) && ((*z).PaymentTxnFields.Receiver.MsgIsZero()) && ((*z).PaymentTxnFields.Amount.MsgIsZero()) && ((*z).PaymentTxnFields.CloseRemainderTo.MsgIsZero()) && ((*z).AssetConfigTxnFields.ConfigAsset.MsgIsZero()) && ((*z).AssetConfigTxnFields.AssetParams.MsgIsZero()) && ((*z).AssetTransferTxnFields.XferAsset.MsgIsZero()) && ((*z).AssetTransferTxnFields.AssetAmount == 0) && ((*z).AssetTransferTxnFields.AssetSender.MsgIsZero()) && ((*z).AssetTransferTxnFields.AssetReceiver.MsgIsZero()) && ((*z).AssetTransferTxnFields.AssetCloseTo.MsgIsZero()) && ((*z).AssetFreezeTxnFields.FreezeAccount.MsgIsZero()) && ((*z).AssetFreezeTxnFields.FreezeAsset.MsgIsZero()) && ((*z).AssetFreezeTxnFields.AssetFrozen == false) && ((*z).ApplicationCallTxnFields.ApplicationID.MsgIsZero()) && ((*z).ApplicationCallTxnFields.OnCompletion == 0) && (len((*z).ApplicationCallTxnFields.ApplicationArgs) == 0) && (len((*z).ApplicationCallTxnFields.Accounts) == 0) && (len((*z).ApplicationCallTxnFields.ForeignApps) == 0) && (len((*z).ApplicationCallTxnFields.Boxes) == 0) && (len((*z).ApplicationCallTxnFields.ForeignAssets) == 0) && ((*z).ApplicationCallTxnFields.LocalStateSchema.MsgIsZero()) && ((*z).ApplicationCallTxnFields.GlobalStateSchema.MsgIsZero()) && (len((*z).ApplicationCallTxnFields.ApprovalProgram) == 0) && (len((*z).ApplicationCallTxnFields.ClearStateProgram) == 0) && ((*z).ApplicationCallTxnFields.ExtraProgramPages == 0) && ((*z).ApplicationCallTxnFields.RejectVersion == 0) && ((*z).StateProofTxnFields.StateProofType.MsgIsZero()) && ((*z).StateProofTxnFields.StateProof.MsgIsZero()) && ((*z).StateProofTxnFields.Message.MsgIsZero()) && ((*z).HeartbeatTxnFields == nil) + return ((*z).Type.MsgIsZero()) && ((*z).Header.Sender.MsgIsZero()) && ((*z).Header.Fee.MsgIsZero()) && ((*z).Header.FirstValid.MsgIsZero()) && ((*z).Header.LastValid.MsgIsZero()) && (len((*z).Header.Note) == 0) && ((*z).Header.GenesisID == "") && ((*z).Header.GenesisHash.MsgIsZero()) && ((*z).Header.Group.MsgIsZero()) && ((*z).Header.Lease == ([32]byte{})) && ((*z).Header.RekeyTo.MsgIsZero()) && ((*z).KeyregTxnFields.VotePK.MsgIsZero()) && ((*z).KeyregTxnFields.SelectionPK.MsgIsZero()) && ((*z).KeyregTxnFields.StateProofPK.MsgIsZero()) && ((*z).KeyregTxnFields.VoteFirst.MsgIsZero()) && ((*z).KeyregTxnFields.VoteLast.MsgIsZero()) && ((*z).KeyregTxnFields.VoteKeyDilution == 0) && ((*z).KeyregTxnFields.Nonparticipation == false) && ((*z).PaymentTxnFields.Receiver.MsgIsZero()) && ((*z).PaymentTxnFields.Amount.MsgIsZero()) && ((*z).PaymentTxnFields.CloseRemainderTo.MsgIsZero()) && ((*z).AssetConfigTxnFields.ConfigAsset.MsgIsZero()) && ((*z).AssetConfigTxnFields.AssetParams.MsgIsZero()) && ((*z).AssetTransferTxnFields.XferAsset.MsgIsZero()) && ((*z).AssetTransferTxnFields.AssetAmount == 0) && ((*z).AssetTransferTxnFields.AssetSender.MsgIsZero()) && ((*z).AssetTransferTxnFields.AssetReceiver.MsgIsZero()) && ((*z).AssetTransferTxnFields.AssetCloseTo.MsgIsZero()) && ((*z).AssetFreezeTxnFields.FreezeAccount.MsgIsZero()) && ((*z).AssetFreezeTxnFields.FreezeAsset.MsgIsZero()) && ((*z).AssetFreezeTxnFields.AssetFrozen == false) && ((*z).ApplicationCallTxnFields.ApplicationID.MsgIsZero()) && ((*z).ApplicationCallTxnFields.OnCompletion == 0) && (len((*z).ApplicationCallTxnFields.ApplicationArgs) == 0) && (len((*z).ApplicationCallTxnFields.Accounts) == 0) && (len((*z).ApplicationCallTxnFields.ForeignAssets) == 0) && (len((*z).ApplicationCallTxnFields.ForeignApps) == 0) && (len((*z).ApplicationCallTxnFields.Access) == 0) && (len((*z).ApplicationCallTxnFields.Boxes) == 0) && ((*z).ApplicationCallTxnFields.LocalStateSchema.MsgIsZero()) && ((*z).ApplicationCallTxnFields.GlobalStateSchema.MsgIsZero()) && (len((*z).ApplicationCallTxnFields.ApprovalProgram) == 0) && (len((*z).ApplicationCallTxnFields.ClearStateProgram) == 0) && ((*z).ApplicationCallTxnFields.ExtraProgramPages == 0) && ((*z).ApplicationCallTxnFields.RejectVersion == 0) && ((*z).StateProofTxnFields.StateProofType.MsgIsZero()) && ((*z).StateProofTxnFields.StateProof.MsgIsZero()) && ((*z).StateProofTxnFields.Message.MsgIsZero()) && ((*z).HeartbeatTxnFields == nil) } // MaxSize returns a maximum valid message size for this message type @@ -7004,14 +8200,17 @@ func TransactionMaxSize() (s int) { // Calculating size of slice: z.ApplicationCallTxnFields.Accounts s += msgp.ArrayHeaderSize + ((encodedMaxAccounts) * (basics.AddressMaxSize())) s += 5 + // Calculating size of slice: z.ApplicationCallTxnFields.ForeignAssets + s += msgp.ArrayHeaderSize + ((encodedMaxForeignAssets) * (basics.AssetIndexMaxSize())) + s += 5 // Calculating size of slice: z.ApplicationCallTxnFields.ForeignApps s += msgp.ArrayHeaderSize + ((encodedMaxForeignApps) * (basics.AppIndexMaxSize())) + s += 3 + // Calculating size of slice: z.ApplicationCallTxnFields.Access + s += msgp.ArrayHeaderSize + ((encodedMaxAccess) * (ResourceRefMaxSize())) s += 5 // Calculating size of slice: z.ApplicationCallTxnFields.Boxes s += msgp.ArrayHeaderSize + ((encodedMaxBoxes) * (BoxRefMaxSize())) - s += 5 - // Calculating size of slice: z.ApplicationCallTxnFields.ForeignAssets - s += msgp.ArrayHeaderSize + ((encodedMaxForeignAssets) * (basics.AssetIndexMaxSize())) s += 5 + basics.StateSchemaMaxSize() + 5 + basics.StateSchemaMaxSize() + 5 + msgp.BytesPrefixSize + config.MaxAvailableAppProgramLen + 5 + msgp.BytesPrefixSize + config.MaxAvailableAppProgramLen + 5 + msgp.Uint32Size + 5 + msgp.Uint64Size + 7 + protocol.StateProofTypeMaxSize() + 3 + stateproof.StateProofMaxSize() + 6 + stateproofmsg.MessageMaxSize() + 3 s += HeartbeatTxnFieldsMaxSize() return diff --git a/data/transactions/msgp_gen_test.go b/data/transactions/msgp_gen_test.go index 49ed14f6e3..8cc316425c 100644 --- a/data/transactions/msgp_gen_test.go +++ b/data/transactions/msgp_gen_test.go @@ -554,6 +554,66 @@ func BenchmarkUnmarshalHeartbeatTxnFields(b *testing.B) { } } +func TestMarshalUnmarshalHoldingRef(t *testing.T) { + partitiontest.PartitionTest(t) + v := HoldingRef{} + bts := v.MarshalMsg(nil) + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func TestRandomizedEncodingHoldingRef(t *testing.T) { + protocol.RunEncodingTest(t, &HoldingRef{}) +} + +func BenchmarkMarshalMsgHoldingRef(b *testing.B) { + v := HoldingRef{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgHoldingRef(b *testing.B) { + v := HoldingRef{} + bts := make([]byte, 0, v.Msgsize()) + bts = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalHoldingRef(b *testing.B) { + v := HoldingRef{} + bts := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + func TestMarshalUnmarshalKeyregTxnFields(t *testing.T) { partitiontest.PartitionTest(t) v := KeyregTxnFields{} @@ -614,6 +674,66 @@ func BenchmarkUnmarshalKeyregTxnFields(b *testing.B) { } } +func TestMarshalUnmarshalLocalsRef(t *testing.T) { + partitiontest.PartitionTest(t) + v := LocalsRef{} + bts := v.MarshalMsg(nil) + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func TestRandomizedEncodingLocalsRef(t *testing.T) { + protocol.RunEncodingTest(t, &LocalsRef{}) +} + +func BenchmarkMarshalMsgLocalsRef(b *testing.B) { + v := LocalsRef{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgLocalsRef(b *testing.B) { + v := LocalsRef{} + bts := make([]byte, 0, v.Msgsize()) + bts = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalLocalsRef(b *testing.B) { + v := LocalsRef{} + bts := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + func TestMarshalUnmarshalLogicSig(t *testing.T) { partitiontest.PartitionTest(t) v := LogicSig{} @@ -794,6 +914,66 @@ func BenchmarkUnmarshalPayset(b *testing.B) { } } +func TestMarshalUnmarshalResourceRef(t *testing.T) { + partitiontest.PartitionTest(t) + v := ResourceRef{} + bts := v.MarshalMsg(nil) + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func TestRandomizedEncodingResourceRef(t *testing.T) { + protocol.RunEncodingTest(t, &ResourceRef{}) +} + +func BenchmarkMarshalMsgResourceRef(b *testing.B) { + v := ResourceRef{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgResourceRef(b *testing.B) { + v := ResourceRef{} + bts := make([]byte, 0, v.Msgsize()) + bts = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalResourceRef(b *testing.B) { + v := ResourceRef{} + bts := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + func TestMarshalUnmarshalSignedTxn(t *testing.T) { partitiontest.PartitionTest(t) v := SignedTxn{} diff --git a/data/txntest/txn.go b/data/txntest/txn.go index c677a70d28..7b46a93d44 100644 --- a/data/txntest/txn.go +++ b/data/txntest/txn.go @@ -83,6 +83,7 @@ type Txn struct { ForeignApps []basics.AppIndex ForeignAssets []basics.AssetIndex Boxes []transactions.BoxRef + Access []transactions.ResourceRef LocalStateSchema basics.StateSchema GlobalStateSchema basics.StateSchema ApprovalProgram interface{} // string, nil, or []bytes if already compiled @@ -117,6 +118,10 @@ func (tx *Txn) internalCopy() { for i := 0; i < len(tx.Boxes); i++ { tx.Boxes[i].Name = append([]byte(nil), tx.Boxes[i].Name...) } + tx.Access = append([]transactions.ResourceRef(nil), tx.Access...) + for i := 0; i < len(tx.Access); i++ { + tx.Access[i].Box.Name = append([]byte(nil), tx.Access[i].Box.Name...) + } // Programs may or may not actually be byte slices. The other // possibilitiues don't require copies. @@ -288,6 +293,7 @@ func (tx Txn) Txn() transactions.Transaction { ForeignApps: append([]basics.AppIndex(nil), tx.ForeignApps...), ForeignAssets: append([]basics.AssetIndex(nil), tx.ForeignAssets...), Boxes: tx.Boxes, + Access: tx.Access, LocalStateSchema: tx.LocalStateSchema, GlobalStateSchema: tx.GlobalStateSchema, ApprovalProgram: assemble(tx.ApprovalProgram), diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index 4509b66c0b..3bed7283ad 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -3170,7 +3170,7 @@ app_local_put var problem string switch { case ver < 34: // before v7, app accounts not available at all - problem = "invalid Account reference " + id0.Address().String() + problem = "unavailable Account " + id0.Address().String() case ver < 38: // as of v7, it's the mutation that's the problem problem = "invalid Account reference for mutation" } diff --git a/ledger/boxtxn_test.go b/ledger/boxtxn_test.go index 2cbec642ab..93f228d596 100644 --- a/ledger/boxtxn_test.go +++ b/ledger/boxtxn_test.go @@ -128,6 +128,7 @@ var passThruSource = main(` `) const boxVersion = 36 +const boxQuotaBumpVersion = 41 func boxFee(p config.ConsensusParams, nameAndValueSize uint64) uint64 { return p.BoxFlatMinBalance + p.BoxByteMinBalance*(nameAndValueSize) @@ -539,16 +540,20 @@ func TestBoxIOBudgets(t *testing.T) { ApplicationID: appID, Boxes: []transactions.BoxRef{{Index: 0, Name: []byte("x")}}, } - dl.txn(call.Args("create", "x", "\x10\x00"), // 4096 - "write budget (1024) exceeded") - call.Boxes = append(call.Boxes, transactions.BoxRef{}) + if ver < boxQuotaBumpVersion { + dl.txn(call.Args("create", "x", "\x10\x00"), // 4096 + "write budget (1024) exceeded") + call.Boxes = append(call.Boxes, transactions.BoxRef{}) + } dl.txn(call.Args("create", "x", "\x10\x00"), // 4096 "write budget (2048) exceeded") call.Boxes = append(call.Boxes, transactions.BoxRef{}) - dl.txn(call.Args("create", "x", "\x10\x00"), // 4096 - "write budget (3072) exceeded") - call.Boxes = append(call.Boxes, transactions.BoxRef{}) - dl.txn(call.Args("create", "x", "\x10\x00"), // now there are 4 box refs + if ver < boxQuotaBumpVersion { + dl.txn(call.Args("create", "x", "\x10\x00"), // 4096 + "write budget (3072) exceeded") + call.Boxes = append(call.Boxes, transactions.BoxRef{}) + } + dl.txn(call.Args("create", "x", "\x10\x00"), // now there are enough box refs "below min") // big box would need more balance dl.txn(call.Args("create", "x", "\x10\x01"), // 4097 "write budget (4096) exceeded") @@ -572,11 +577,10 @@ func TestBoxIOBudgets(t *testing.T) { dl.txgroup("", &fundApp, create) // Now that we've created a 4,096 byte box, test READ budget - // It works at the start, because call still has 4 brs. + // It works at the start, because call still has enough brs. dl.txn(call.Args("check", "x", "\x00")) - call.Boxes = call.Boxes[:3] - dl.txn(call.Args("check", "x", "\x00"), - "box read budget (3072) exceeded") + call.Boxes = call.Boxes[:len(call.Boxes)-1] // remove one ref + dl.txn(call.Args("check", "x", "\x00"), "box read budget") // Give a budget over 32768, confirm failure anyway empties := [32]transactions.BoxRef{} @@ -603,7 +607,7 @@ func TestBoxInners(t *testing.T) { dl.txn(&txntest.Txn{Type: "pay", Sender: addrs[0], Receiver: addrs[0]}) dl.txn(&txntest.Txn{Type: "pay", Sender: addrs[0], Receiver: addrs[0]}) - boxID := dl.fundedApp(addrs[0], 2_000_000, boxAppSource) // there are some big boxes made + boxID := dl.fundedApp(addrs[0], 4_000_000, boxAppSource) // there are some big boxes made passID := dl.fundedApp(addrs[0], 120_000, passThruSource) // lowish, show it's not paying for boxes call := txntest.Txn{ Type: "appl", @@ -621,11 +625,19 @@ func TestBoxInners(t *testing.T) { require.Error(t, call.Txn().WellFormed(transactions.SpecialAddresses{}, dl.generator.genesisProto)) call.Boxes = []transactions.BoxRef{{Index: 1, Name: []byte("x")}} - dl.txn(call.Args("create", "x", "\x10\x00"), // 4096 - "write budget (1024) exceeded") - dl.txn(call.Args("create", "x", "\x04\x00")) // 1024 - call.Boxes = append(call.Boxes, transactions.BoxRef{Index: 1, Name: []byte("y")}) - dl.txn(call.Args("create", "y", "\x08\x00")) // 2048 + if ver < boxQuotaBumpVersion { + dl.txn(call.Args("create", "x", "\x10\x00"), // 4096 + "write budget (1024) exceeded") + dl.txn(call.Args("create", "x", "\x04\x00")) // 1024 + call.Boxes = append(call.Boxes, transactions.BoxRef{Index: 1, Name: []byte("y")}) + dl.txn(call.Args("create", "y", "\x08\x00")) // 2048 + } else { + dl.txn(call.Args("create", "x", "\x10\x00"), // 4096 + "write budget (2048) exceeded") + dl.txn(call.Args("create", "x", "\x08\x00")) // 2048 + call.Boxes = append(call.Boxes, transactions.BoxRef{Index: 1, Name: []byte("y")}) + dl.txn(call.Args("create", "y", "\x10\x00")) // 4096 + } require.Len(t, call.Boxes, 2) setX := call.Args("set", "x", "A") diff --git a/ledger/simulation/resources.go b/ledger/simulation/resources.go index 2132d20710..ae255c5e8d 100644 --- a/ledger/simulation/resources.go +++ b/ledger/simulation/resources.go @@ -42,7 +42,7 @@ type ResourceTracker struct { // The map value is the size of the box loaded from the ledger prior to any writes. This is used // to track the box read budget. - Boxes map[logic.BoxRef]uint64 + Boxes map[basics.BoxRef]uint64 MaxBoxes int NumEmptyBoxRefs int maxWriteBudget uint64 @@ -224,7 +224,7 @@ func (a *ResourceTracker) removeAppSlot() bool { func (a *ResourceTracker) hasBox(app basics.AppIndex, name string) bool { // nil map lookup is ok - _, ok := a.Boxes[logic.BoxRef{App: app, Name: name}] + _, ok := a.Boxes[basics.BoxRef{App: app, Name: name}] return ok } @@ -257,9 +257,9 @@ func (a *ResourceTracker) addBox(app basics.AppIndex, name string, readSize, add return false } if a.Boxes == nil { - a.Boxes = make(map[logic.BoxRef]uint64) + a.Boxes = make(map[basics.BoxRef]uint64) } - a.Boxes[logic.BoxRef{App: app, Name: name}] = readSize + a.Boxes[basics.BoxRef{App: app, Name: name}] = readSize a.NumEmptyBoxRefs += emptyRefs return true } diff --git a/ledger/simulation/simulation_eval_test.go b/ledger/simulation/simulation_eval_test.go index c2b1b64726..b1512859ee 100644 --- a/ledger/simulation/simulation_eval_test.go +++ b/ledger/simulation/simulation_eval_test.go @@ -7067,7 +7067,7 @@ func TestUnnamedResources(t *testing.T) { if v >= 8 { // boxes introduced program += `byte "A"; int 64; box_create; assert;` program += `byte "B"; box_len; !; assert; !; assert;` - expectedUnnamedResourceGroupAssignment.Boxes = map[logic.BoxRef]uint64{ + expectedUnnamedResourceGroupAssignment.Boxes = map[basics.BoxRef]uint64{ {App: 0, Name: "A"}: 0, {App: 0, Name: "B"}: 0, } @@ -7107,7 +7107,7 @@ func TestUnnamedResources(t *testing.T) { local.Address = testAppID.Address() expectedUnnamedResourceGroupAssignment.AppLocals[local] = struct{}{} } - var boxesToFix []logic.BoxRef + var boxesToFix []basics.BoxRef for box := range expectedUnnamedResourceGroupAssignment.Boxes { if box.App == 0 { // replace with app ID @@ -7602,7 +7602,7 @@ func (o boxOperation) boxRefs() []transactions.BoxRef { } type boxTestResult struct { - Boxes map[logic.BoxRef]uint64 + Boxes map[basics.BoxRef]uint64 NumEmptyBoxRefs int FailureMessage string @@ -7780,21 +7780,21 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { testBoxOps([]boxOperation{ {op: logic.BoxReadOperation, name: "A"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "A"}: proto.BytesPerBoxReference, }, }) testBoxOps([]boxOperation{ {op: logic.BoxReadOperation, name: "B"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "B"}: 1, }, }) testBoxOps([]boxOperation{ {op: logic.BoxReadOperation, name: "C"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "C"}: 2*proto.BytesPerBoxReference - 1, }, // We need an additional empty box ref because the size of C exceeds BytesPerBoxReference @@ -7804,7 +7804,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxReadOperation, name: "A"}, {op: logic.BoxReadOperation, name: "B"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "A"}: proto.BytesPerBoxReference, {App: appID, Name: "B"}: 1, }, @@ -7813,7 +7813,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxReadOperation, name: "A"}, {op: logic.BoxReadOperation, name: "C"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "A"}: proto.BytesPerBoxReference, {App: appID, Name: "C"}: 2*proto.BytesPerBoxReference - 1, }, @@ -7824,7 +7824,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxReadOperation, name: "B"}, {op: logic.BoxReadOperation, name: "C"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "A"}: proto.BytesPerBoxReference, {App: appID, Name: "B"}: 1, {App: appID, Name: "C"}: 2*proto.BytesPerBoxReference - 1, @@ -7834,7 +7834,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { testBoxOps([]boxOperation{ {op: logic.BoxReadOperation, name: "Q"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "Q"}: 0, }, }) @@ -7843,14 +7843,14 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { testBoxOps([]boxOperation{ {op: logic.BoxCreateOperation, name: "D", createSize: proto.BytesPerBoxReference}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, }, }) testBoxOps([]boxOperation{ {op: logic.BoxCreateOperation, name: "D", createSize: proto.BytesPerBoxReference + 1}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, }, NumEmptyBoxRefs: 1, @@ -7858,7 +7858,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { testBoxOps([]boxOperation{ {op: logic.BoxCreateOperation, name: "D", createSize: proto.BytesPerBoxReference * 3}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, }, NumEmptyBoxRefs: 2, @@ -7867,7 +7867,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxCreateOperation, name: "D", createSize: 1}, {op: logic.BoxCreateOperation, name: "E", createSize: 1}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, {App: appID, Name: "E"}: 0, }, @@ -7878,7 +7878,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxCreateOperation, name: "D", createSize: proto.BytesPerBoxReference + 2}, {op: logic.BoxReadOperation, name: "A"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, {App: appID, Name: "A"}: proto.BytesPerBoxReference, }, @@ -7889,7 +7889,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxReadOperation, name: "A"}, {op: logic.BoxCreateOperation, name: "D", createSize: proto.BytesPerBoxReference + 2}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, {App: appID, Name: "A"}: proto.BytesPerBoxReference, }, @@ -7901,7 +7901,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxCreateOperation, name: "D", createSize: proto.BytesPerBoxReference + 2}, {op: logic.BoxWriteOperation, name: "A", contents: []byte{1}}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, {App: appID, Name: "A"}: proto.BytesPerBoxReference, }, @@ -7911,7 +7911,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxCreateOperation, name: "D", createSize: proto.BytesPerBoxReference + 2}, {op: logic.BoxWriteOperation, name: "B", contents: []byte{1}}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, {App: appID, Name: "B"}: 1, }, @@ -7924,7 +7924,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxCreateOperation, name: "D", createSize: 4 * proto.BytesPerBoxReference}, {op: logic.BoxDeleteOperation, name: "D"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, }, // Still need 3 empty box refs because we went over the write budget before deletion. @@ -7937,7 +7937,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { {op: logic.BoxDeleteOperation, name: "D"}, {op: logic.BoxReadOperation, name: "C"}, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "D"}: 0, {App: appID, Name: "C"}: 2*proto.BytesPerBoxReference - 1, }, @@ -7956,7 +7956,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { otherRefCount: proto.MaxAppBoxReferences - 1, }, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "A"}: proto.BytesPerBoxReference, }, }) @@ -7987,7 +7987,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { otherRefCount: proto.MaxAppBoxReferences, }, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "C"}: 2*proto.BytesPerBoxReference - 1, {App: appID, Name: "X"}: 0, }, @@ -8002,7 +8002,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { otherRefCount: proto.MaxAppBoxReferences - 1, }, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "X"}: 0, }, }) @@ -8016,7 +8016,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { otherRefCount: proto.MaxAppBoxReferences - 1, }, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "X"}: 0, }, FailureMessage: fmt.Sprintf("logic eval error: write budget (%d) exceeded %d", proto.BytesPerBoxReference, proto.BytesPerBoxReference+1), @@ -8037,7 +8037,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { otherRefCount: proto.MaxAppBoxReferences, }, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "X"}: 0, {App: appID, Name: "A"}: proto.BytesPerBoxReference, }, @@ -8056,7 +8056,7 @@ func TestUnnamedResourcesBoxIOBudget(t *testing.T) { otherRefCount: proto.MaxAppBoxReferences, }, }, boxTestResult{ - Boxes: map[logic.BoxRef]uint64{ + Boxes: map[basics.BoxRef]uint64{ {App: appID, Name: "A"}: proto.BytesPerBoxReference, }, FailureMessage: fmt.Sprintf("logic eval error: invalid Box reference %#x", "B"), @@ -8440,14 +8440,14 @@ func mapWithKeys[K comparable, V any](keys []K, defaultValue V) map[K]V { return m } -func boxNamesToRefs(app basics.AppIndex, names []string) []logic.BoxRef { +func boxNamesToRefs(app basics.AppIndex, names []string) []basics.BoxRef { if names == nil { return nil } - refs := make([]logic.BoxRef, len(names)) + refs := make([]basics.BoxRef, len(names)) for i, name := range names { - refs[i] = logic.BoxRef{ + refs[i] = basics.BoxRef{ App: app, Name: name, } @@ -8660,7 +8660,7 @@ func TestUnnamedResourcesLimits(t *testing.T) { unnamedResourceArguments{}. addAccounts(otherAccounts[:proto.MaxAppTotalTxnReferences+1]...). markLimitExceeded(), - fmt.Sprintf("logic eval error: invalid Account reference %s", otherAccounts[proto.MaxAppTotalTxnReferences]), + fmt.Sprintf("logic eval error: unavailable Account %s", otherAccounts[proto.MaxAppTotalTxnReferences]), ) // Exactly at asset limit @@ -8726,7 +8726,7 @@ func TestUnnamedResourcesLimits(t *testing.T) { // Adding 1 more of any is over the limit testResourceAccess( atLimit.addAccounts(otherAccounts[len(otherAccounts)-1]).markLimitExceeded(), - fmt.Sprintf("logic eval error: invalid Account reference %s", otherAccounts[len(otherAccounts)-1]), + fmt.Sprintf("logic eval error: unavailable Account %s", otherAccounts[len(otherAccounts)-1]), ) testResourceAccess( atLimit.addAssets(assets[len(assets)-1]).markLimitExceeded(), diff --git a/libgoal/libgoal_test.go b/libgoal/libgoal_test.go index 746f40a0cf..7e0870987c 100644 --- a/libgoal/libgoal_test.go +++ b/libgoal/libgoal_test.go @@ -17,9 +17,14 @@ package libgoal import ( + "fmt" "testing" + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/data/transactions" + "github.com/algorand/go-algorand/data/txntest" "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -141,3 +146,237 @@ func TestValidRounds(t *testing.T) { a.Equal(uint64(100), fv) a.Equal(maxTxnLife, lv) } + +// bbrs just saves typing +func bbrs(args ...any) []basics.BoxRef { + if len(args)%2 != 0 { + panic(fmt.Sprintf("odd number of args %v", args)) + } + var refs []basics.BoxRef + for i := 0; i < len(args); i += 2 { + app := basics.AppIndex(args[i].(int)) + name := args[i+1].(string) + refs = append(refs, basics.BoxRef{ + App: app, + Name: name, + }) + } + return refs +} + +func TestForeignResolution(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + a := assert.New(t) + + tx := txntest.Txn{ + ApplicationID: 111, + }.Txn() + + accounts := []basics.Address{{0x22}, {0x33}} + foreignApps := []basics.AppIndex{222, 333} + foreignAssets := []basics.AssetIndex{2222, 3333} + + attachForeignRefs(&tx, RefBundle{Accounts: accounts}) + a.Equal(accounts, tx.Accounts) + + attachForeignRefs(&tx, RefBundle{Assets: foreignAssets}) + a.Equal(foreignAssets, tx.ForeignAssets) + + attachForeignRefs(&tx, RefBundle{Apps: foreignApps}) + a.Equal(foreignApps, tx.ForeignApps) + + attachForeignRefs(&tx, RefBundle{Apps: foreignApps}) + a.Equal(append(foreignApps, foreignApps...), tx.ForeignApps) + + boxes := bbrs(3, "aaa") + attachForeignRefs(&tx, RefBundle{Boxes: boxes}) + a.Equal([]basics.AppIndex{222, 333, 222, 333, 3}, tx.ForeignApps) + a.Equal([]transactions.BoxRef{{Index: 5, Name: []byte("aaa")}}, tx.Boxes) + + boxes = bbrs(3, "aaa", 0, "bbb") + tx.Boxes = nil + attachForeignRefs(&tx, RefBundle{Boxes: boxes}) + a.Equal([]basics.AppIndex{222, 333, 222, 333, 3}, tx.ForeignApps) + a.Equal([]transactions.BoxRef{ + {Index: 5, Name: []byte("aaa")}, + {Index: 0, Name: []byte("bbb")}, + }, tx.Boxes) + + boxes = bbrs(3, "aaa", 3, "xxx") + attachForeignRefs(&tx, RefBundle{Boxes: boxes}) + a.Equal([]basics.AppIndex{222, 333, 222, 333, 3}, tx.ForeignApps) + a.Equal([]transactions.BoxRef{ + {Index: 5, Name: []byte("aaa")}, + {Index: 0, Name: []byte("bbb")}, + {Index: 5, Name: []byte("aaa")}, + {Index: 5, Name: []byte("xxx")}, + }, tx.Boxes) + + boxes = bbrs(111, "aaa", 333, "xxx") + attachForeignRefs(&tx, RefBundle{Boxes: boxes}) + a.Equal([]basics.AppIndex{222, 333, 222, 333, 3}, tx.ForeignApps) + a.Equal([]transactions.BoxRef{ + {Index: 5, Name: []byte("aaa")}, + {Index: 0, Name: []byte("bbb")}, + {Index: 5, Name: []byte("aaa")}, + {Index: 5, Name: []byte("xxx")}, + {Index: 0, Name: []byte("aaa")}, + {Index: 2, Name: []byte("xxx")}, + }, tx.Boxes) + + zero := basics.Address{0x00} + one := basics.Address{0x01} + two := basics.Address{0x02} + holdings := []basics.HoldingRef{{Asset: 111, Address: one}, {Asset: 3333, Address: zero}} + attachForeignRefs(&tx, RefBundle{Holdings: holdings}) + a.Equal([]basics.AssetIndex{2222, 3333, 111}, tx.ForeignAssets) // it's added, 111 is the APP id + a.Equal(append(accounts, one), tx.Accounts) + + locals := []basics.LocalRef{{App: 111, Address: two}, {App: 333, Address: zero}, {App: 444, Address: one}} + attachForeignRefs(&tx, RefBundle{Locals: locals}) + a.Equal([]basics.AppIndex{222, 333, 222, 333, 3, 444}, tx.ForeignApps) // 111 not added, it's being called + a.Equal(append(accounts, one, two), tx.Accounts) +} + +func TestAccessResolution(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + a := assert.New(t) + + tx := txntest.Txn{ + ApplicationID: 111, + }.Txn() + + accounts := []basics.Address{{0x22}, {0x33}} + foreignApps := []basics.AppIndex{222, 333} + foreignAssets := []basics.AssetIndex{2222, 3333} + + attachAccessList(&tx, RefBundle{Accounts: accounts}) + a.Nil(tx.Accounts) + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + }, tx.Access) + + attachAccessList(&tx, RefBundle{Assets: foreignAssets}) + a.Nil(tx.ForeignAssets) + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + {Asset: foreignAssets[0]}, {Asset: foreignAssets[1]}, + }, tx.Access) + + attachAccessList(&tx, RefBundle{Apps: foreignApps}) + a.Nil(tx.ForeignApps) + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + {Asset: foreignAssets[0]}, {Asset: foreignAssets[1]}, + {App: foreignApps[0]}, {App: foreignApps[1]}, + }, tx.Access) + + attachAccessList(&tx, RefBundle{Apps: foreignApps}) + // no change + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + {Asset: foreignAssets[0]}, {Asset: foreignAssets[1]}, + {App: foreignApps[0]}, {App: foreignApps[1]}, + }, tx.Access) + + boxes := bbrs(3, "aaa") + attachAccessList(&tx, RefBundle{Boxes: boxes}) + a.Nil(tx.Boxes) + a.Nil(tx.ForeignApps) + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + {Asset: foreignAssets[0]}, {Asset: foreignAssets[1]}, + {App: foreignApps[0]}, {App: foreignApps[1]}, + {App: 3}, {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + }, tx.Access) + + boxes = bbrs(3, "aaa", 0, "bbb") + attachAccessList(&tx, RefBundle{Boxes: boxes}) + a.Nil(tx.Boxes) + a.Nil(tx.ForeignApps) + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + {Asset: foreignAssets[0]}, {Asset: foreignAssets[1]}, + {App: foreignApps[0]}, {App: foreignApps[1]}, + {App: 3}, {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 0, Name: []byte("bbb")}}, + }, tx.Access) + + boxes = bbrs(3, "aaa", 3, "xxx") + attachAccessList(&tx, RefBundle{Boxes: boxes}) + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + {Asset: foreignAssets[0]}, {Asset: foreignAssets[1]}, + {App: foreignApps[0]}, {App: foreignApps[1]}, + {App: 3}, {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 0, Name: []byte("bbb")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("xxx")}}, + }, tx.Access) + + boxes = bbrs(111, "aaa", 333, "xxx") + attachAccessList(&tx, RefBundle{Boxes: boxes}) + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + {Asset: foreignAssets[0]}, {Asset: foreignAssets[1]}, + {App: foreignApps[0]}, {App: foreignApps[1]}, + {App: 3}, {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 0, Name: []byte("bbb")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("xxx")}}, + {Box: transactions.BoxRef{Index: 0, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 6, Name: []byte("xxx")}}, + }, tx.Access) + + zero := basics.Address{0x00} + one := basics.Address{0x01} + two := basics.Address{0x02} + holdings := []basics.HoldingRef{{Asset: 111, Address: one}, {Asset: 3333, Address: zero}} + attachAccessList(&tx, RefBundle{Holdings: holdings}) + a.Nil(tx.ForeignAssets) + a.Nil(tx.Accounts) + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + {Asset: foreignAssets[0]}, {Asset: foreignAssets[1]}, + {App: foreignApps[0]}, {App: foreignApps[1]}, + {App: 3}, {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 0, Name: []byte("bbb")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("xxx")}}, + {Box: transactions.BoxRef{Index: 0, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 6, Name: []byte("xxx")}}, + {Address: one}, {Asset: 111}, {Holding: transactions.HoldingRef{Asset: 16, Address: 15}}, + {Holding: transactions.HoldingRef{Asset: 4, Address: 0}}, + }, tx.Access) + + locals := []basics.LocalRef{{App: 111, Address: two}, {App: 333, Address: zero}, {App: 444, Address: one}} + attachAccessList(&tx, RefBundle{Locals: locals}) + a.Nil(tx.ForeignApps) + a.Nil(tx.Accounts) + a.Equal([]transactions.ResourceRef{ + {Address: accounts[0]}, {Address: accounts[1]}, + {Asset: foreignAssets[0]}, {Asset: foreignAssets[1]}, + {App: foreignApps[0]}, {App: foreignApps[1]}, + {App: 3}, {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 0, Name: []byte("bbb")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 7, Name: []byte("xxx")}}, + {Box: transactions.BoxRef{Index: 0, Name: []byte("aaa")}}, + {Box: transactions.BoxRef{Index: 6, Name: []byte("xxx")}}, + {Address: one}, {Asset: 111}, {Holding: transactions.HoldingRef{Asset: 16, Address: 15}}, + {Holding: transactions.HoldingRef{Asset: 4, Address: 0}}, + + {Address: two}, + {Locals: transactions.LocalsRef{App: 0, Address: 19}}, + {Locals: transactions.LocalsRef{App: 6, Address: 0}}, + {App: 444}, + {Locals: transactions.LocalsRef{App: 22, Address: 15}}, + }, tx.Access) +} diff --git a/libgoal/transactions.go b/libgoal/transactions.go index 3c80b69dea..d52468960a 100644 --- a/libgoal/transactions.go +++ b/libgoal/transactions.go @@ -19,6 +19,7 @@ package libgoal import ( "errors" "fmt" + "slices" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" @@ -524,65 +525,74 @@ func (c *Client) FillUnsignedTxTemplate(sender string, firstValid, lastValid, fe return tx, nil } +// RefBundle holds all of the "foreign" references an app call needs, and +// handles converting to the proper form in the transaction. Depending on +// UseAccess, it can pack the references in the tx.Access list, or use the older +// foreign arrays. +type RefBundle struct { + UseAccess bool + + Accounts []basics.Address + Assets []basics.AssetIndex + Holdings []basics.HoldingRef + Apps []basics.AppIndex + Locals []basics.LocalRef + Boxes []basics.BoxRef +} + // MakeUnsignedAppCreateTx makes a transaction for creating an application -func (c *Client) MakeUnsignedAppCreateTx(onComplete transactions.OnCompletion, approvalProg []byte, clearProg []byte, globalSchema basics.StateSchema, localSchema basics.StateSchema, appArgs [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef, extrapages uint32) (tx transactions.Transaction, err error) { - return c.MakeUnsignedApplicationCallTx(0, appArgs, accounts, foreignApps, foreignAssets, boxes, onComplete, approvalProg, clearProg, globalSchema, localSchema, extrapages, 0) +func (c *Client) MakeUnsignedAppCreateTx(onComplete transactions.OnCompletion, approvalProg []byte, clearProg []byte, globalSchema basics.StateSchema, localSchema basics.StateSchema, appArgs [][]byte, refs RefBundle, extrapages uint32) (tx transactions.Transaction, err error) { + return c.MakeUnsignedApplicationCallTx(0, appArgs, refs, onComplete, approvalProg, clearProg, globalSchema, localSchema, extrapages, 0) } // MakeUnsignedAppUpdateTx makes a transaction for updating an application's programs -func (c *Client) MakeUnsignedAppUpdateTx(appIdx uint64, appArgs [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef, approvalProg []byte, clearProg []byte, rejectVersion uint64) (tx transactions.Transaction, err error) { - return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, accounts, foreignApps, foreignAssets, boxes, transactions.UpdateApplicationOC, approvalProg, clearProg, emptySchema, emptySchema, 0, rejectVersion) +func (c *Client) MakeUnsignedAppUpdateTx(appIdx uint64, appArgs [][]byte, approvalProg []byte, clearProg []byte, refs RefBundle, rejectVersion uint64) (tx transactions.Transaction, err error) { + return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, refs, transactions.UpdateApplicationOC, approvalProg, clearProg, emptySchema, emptySchema, 0, rejectVersion) } // MakeUnsignedAppDeleteTx makes a transaction for deleting an application -func (c *Client) MakeUnsignedAppDeleteTx(appIdx uint64, appArgs [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef, rejectVersion uint64) (tx transactions.Transaction, err error) { - return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, accounts, foreignApps, foreignAssets, boxes, transactions.DeleteApplicationOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) +func (c *Client) MakeUnsignedAppDeleteTx(appIdx uint64, appArgs [][]byte, refs RefBundle, rejectVersion uint64) (tx transactions.Transaction, err error) { + return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, refs, transactions.DeleteApplicationOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) } // MakeUnsignedAppOptInTx makes a transaction for opting in to (allocating // some account-specific state for) an application -func (c *Client) MakeUnsignedAppOptInTx(appIdx uint64, appArgs [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef, rejectVersion uint64) (tx transactions.Transaction, err error) { - return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, accounts, foreignApps, foreignAssets, boxes, transactions.OptInOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) +func (c *Client) MakeUnsignedAppOptInTx(appIdx uint64, appArgs [][]byte, refs RefBundle, rejectVersion uint64) (tx transactions.Transaction, err error) { + return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, refs, transactions.OptInOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) } // MakeUnsignedAppCloseOutTx makes a transaction for closing out of // (deallocating all account-specific state for) an application -func (c *Client) MakeUnsignedAppCloseOutTx(appIdx uint64, appArgs [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef, rejectVersion uint64) (tx transactions.Transaction, err error) { - return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, accounts, foreignApps, foreignAssets, boxes, transactions.CloseOutOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) +func (c *Client) MakeUnsignedAppCloseOutTx(appIdx uint64, appArgs [][]byte, refs RefBundle, rejectVersion uint64) (tx transactions.Transaction, err error) { + return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, refs, transactions.CloseOutOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) } // MakeUnsignedAppClearStateTx makes a transaction for clearing out all // account-specific state for an application. It may not be rejected by the // application's logic. -func (c *Client) MakeUnsignedAppClearStateTx(appIdx uint64, appArgs [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef, rejectVersion uint64) (tx transactions.Transaction, err error) { - return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, accounts, foreignApps, foreignAssets, boxes, transactions.ClearStateOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) +func (c *Client) MakeUnsignedAppClearStateTx(appIdx uint64, appArgs [][]byte, refs RefBundle, rejectVersion uint64) (tx transactions.Transaction, err error) { + return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, refs, transactions.ClearStateOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) } // MakeUnsignedAppNoOpTx makes a transaction for interacting with an existing // application, potentially updating any account-specific local state and // global state associated with it. -func (c *Client) MakeUnsignedAppNoOpTx(appIdx uint64, appArgs [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef, rejectVersion uint64) (tx transactions.Transaction, err error) { - return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, accounts, foreignApps, foreignAssets, boxes, transactions.NoOpOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) +func (c *Client) MakeUnsignedAppNoOpTx(appIdx uint64, appArgs [][]byte, refs RefBundle, rejectVersion uint64) (tx transactions.Transaction, err error) { + return c.MakeUnsignedApplicationCallTx(appIdx, appArgs, refs, transactions.NoOpOC, nil, nil, emptySchema, emptySchema, 0, rejectVersion) } // MakeUnsignedApplicationCallTx is a helper for the above ApplicationCall // transaction constructors. A fully custom ApplicationCall transaction may // be constructed using this method. -func (c *Client) MakeUnsignedApplicationCallTx(appIdx uint64, appArgs [][]byte, accounts []string, foreignApps []uint64, foreignAssets []uint64, boxes []transactions.BoxRef, onCompletion transactions.OnCompletion, approvalProg []byte, clearProg []byte, globalSchema basics.StateSchema, localSchema basics.StateSchema, extrapages uint32, rejectVersion uint64) (tx transactions.Transaction, err error) { +func (c *Client) MakeUnsignedApplicationCallTx(callee uint64, appArgs [][]byte, refs RefBundle, onCompletion transactions.OnCompletion, approvalProg []byte, clearProg []byte, globalSchema basics.StateSchema, localSchema basics.StateSchema, extrapages uint32, rejectVersion uint64) (tx transactions.Transaction, err error) { tx.Type = protocol.ApplicationCallTx - tx.ApplicationID = basics.AppIndex(appIdx) + tx.ApplicationID = basics.AppIndex(callee) tx.OnCompletion = onCompletion tx.RejectVersion = rejectVersion tx.ApplicationArgs = appArgs - tx.Accounts, err = parseTxnAccounts(accounts) - if err != nil { - return tx, err - } - tx.ForeignApps = parseTxnForeignApps(foreignApps) - tx.ForeignAssets = parseTxnForeignAssets(foreignAssets) - tx.Boxes = boxes + attachReferences(&tx, refs) tx.ApprovalProgram = approvalProg tx.ClearStateProgram = clearProg tx.LocalStateSchema = localSchema @@ -592,29 +602,134 @@ func (c *Client) MakeUnsignedApplicationCallTx(appIdx uint64, appArgs [][]byte, return tx, nil } -func parseTxnAccounts(accounts []string) (parsed []basics.Address, err error) { - for _, acct := range accounts { - addr, err := basics.UnmarshalChecksumAddress(acct) - if err != nil { - return nil, err +// attachReferences adds the foreign arrays or access list required to access +// the resources in the RefBundle. +func attachReferences(tx *transactions.Transaction, refs RefBundle) { + if refs.UseAccess { + attachAccessList(tx, refs) + } else { + attachForeignRefs(tx, refs) + } +} + +// attachAccessList populates the transaction with the new style access list. +func attachAccessList(tx *transactions.Transaction, refs RefBundle) { + // ensure looks for a "simple" resource ref that is needed by a cross-product + // ref. If found, return the 1-based index. If not found, insert and return + // its (new) index. + ensure := func(target transactions.ResourceRef) uint64 { + // We always check all three, though calls will only have one set. Less code duplication. + idx := slices.IndexFunc(tx.Access, func(present transactions.ResourceRef) bool { + return present.Address == target.Address && + present.Asset == target.Asset && + present.App == target.App + }) + if idx != -1 { + return uint64(idx) + 1 } - parsed = append(parsed, addr) + tx.Access = append(tx.Access, target) + return uint64(len(tx.Access)) } - return -} -func parseTxnForeignApps(foreignApps []uint64) (parsed []basics.AppIndex) { - for _, aidx := range foreignApps { - parsed = append(parsed, basics.AppIndex(aidx)) + for _, addr := range refs.Accounts { + ensure(transactions.ResourceRef{Address: addr}) + } + for _, asset := range refs.Assets { + ensure(transactions.ResourceRef{Asset: asset}) + } + for _, app := range refs.Apps { + ensure(transactions.ResourceRef{App: app}) + } + + for _, hr := range refs.Holdings { + addrIdx := uint64(0) + if !hr.Address.IsZero() { + addrIdx = ensure(transactions.ResourceRef{Address: hr.Address}) + } + tx.Access = append(tx.Access, transactions.ResourceRef{Holding: transactions.HoldingRef{ + Asset: ensure(transactions.ResourceRef{Asset: hr.Asset}), + Address: addrIdx, + }}) } - return -} -func parseTxnForeignAssets(foreignAssets []uint64) (parsed []basics.AssetIndex) { - for _, aidx := range foreignAssets { - parsed = append(parsed, basics.AssetIndex(aidx)) + for _, lr := range refs.Locals { + appIdx := uint64(0) + if lr.App != 0 && lr.App != tx.ApplicationID { + appIdx = ensure(transactions.ResourceRef{App: lr.App}) + } + addrIdx := uint64(0) + if !lr.Address.IsZero() { + addrIdx = ensure(transactions.ResourceRef{Address: lr.Address}) + } + tx.Access = append(tx.Access, transactions.ResourceRef{Locals: transactions.LocalsRef{ + App: appIdx, + Address: addrIdx, + }}) + } + + for _, br := range refs.Boxes { + appIdx := uint64(0) + if br.App != 0 && br.App != tx.ApplicationID { + appIdx = ensure(transactions.ResourceRef{App: br.App}) + } + tx.Access = append(tx.Access, transactions.ResourceRef{Box: transactions.BoxRef{ + Index: appIdx, + Name: []byte(br.Name), + }}) + } +} + +// maybeAppend looks for something in a slice. If found, it returns its index. If +// not found, append and return the (new) index. +func maybeAppend[S ~[]E, E comparable](slice S, target E) (S, int) { + idx := slices.Index(slice, target) + if idx != -1 { + return slice, idx + } + slice = append(slice, target) + return slice, len(slice) - 1 +} + +func attachForeignRefs(tx *transactions.Transaction, refs RefBundle) { + // We must add these as given, (not dedupe) + tx.Accounts = append(tx.Accounts, refs.Accounts...) + tx.ForeignAssets = append(tx.ForeignAssets, refs.Assets...) + tx.ForeignApps = append(tx.ForeignApps, refs.Apps...) + + // add assets, addresses if Holdings need them + for _, hr := range refs.Holdings { + tx.ForeignAssets, _ = maybeAppend(tx.ForeignAssets, hr.Asset) + if !hr.Address.IsZero() && // Zero address used to convey "Sender" + !slices.ContainsFunc(tx.ForeignApps, func(id basics.AppIndex) bool { + return id.Address() == hr.Address + }) { + tx.Accounts, _ = maybeAppend(tx.Accounts, hr.Address) + } + } + // add apps, addresses if Locals need them + for _, lr := range refs.Locals { + if lr.App != 0 && lr.App != tx.ApplicationID { + tx.ForeignApps, _ = maybeAppend(tx.ForeignApps, lr.App) + } + if !lr.Address.IsZero() && // Zero address used to convey "Sender" + !slices.ContainsFunc(tx.ForeignApps, func(id basics.AppIndex) bool { + return id.Address() == lr.Address + }) { + tx.Accounts, _ = maybeAppend(tx.Accounts, lr.Address) + } + } + // add boxes (and their app, if needed) + for _, br := range refs.Boxes { + index := 0 + if br.App != 0 && br.App != tx.ApplicationID { + tx.ForeignApps, index = maybeAppend(tx.ForeignApps, br.App) + index++ // 1-based index + } + tx.Boxes = append(tx.Boxes, transactions.BoxRef{ + Index: uint64(index), + Name: []byte(br.Name), + }) } - return } // MakeUnsignedAssetCreateTx creates a tx template for creating diff --git a/shared/pingpong/accounts.go b/shared/pingpong/accounts.go index 4281206948..578a09f2bf 100644 --- a/shared/pingpong/accounts.go +++ b/shared/pingpong/accounts.go @@ -892,7 +892,7 @@ func (pps *WorkerState) newApp(addr string, client *libgoal.Client) (tx transact globSchema := basics.StateSchema{NumByteSlice: proto.MaxGlobalSchemaEntries} locSchema := basics.StateSchema{NumByteSlice: proto.MaxLocalSchemaEntries} - tx, err = client.MakeUnsignedAppCreateTx(transactions.NoOpOC, prog, prog, globSchema, locSchema, nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppCreateTx(transactions.NoOpOC, prog, prog, globSchema, locSchema, nil, libgoal.RefBundle{}, 0) if err != nil { fmt.Printf("Cannot create app txn\n") panic(err) @@ -913,7 +913,7 @@ func (pps *WorkerState) newApp(addr string, client *libgoal.Client) (tx transact } func (pps *WorkerState) appOptIn(addr string, appID uint64, client *libgoal.Client) (tx transactions.Transaction, err error) { - tx, err = client.MakeUnsignedAppOptInTx(appID, nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppOptInTx(appID, nil, libgoal.RefBundle{}, 0) if err != nil { fmt.Printf("Cannot create app txn\n") panic(err) diff --git a/shared/pingpong/pingpong.go b/shared/pingpong/pingpong.go index 5727bc00dd..dabb74bb54 100644 --- a/shared/pingpong/pingpong.go +++ b/shared/pingpong/pingpong.go @@ -30,6 +30,7 @@ import ( "math" "math/rand" "os" + "slices" "strings" "sync/atomic" "time" @@ -1183,13 +1184,12 @@ func (pps *WorkerState) constructAppTxn(from, to string, fee uint64, client *lib } // construct box ref array - var boxRefs []transactions.BoxRef + var boxRefs []basics.BoxRef for i := uint32(0); i < pps.getNumBoxes(); i++ { - boxRefs = append(boxRefs, transactions.BoxRef{Index: 0, Name: []byte{fmt.Sprintf("%d", i)[0]}}) + boxRefs = append(boxRefs, basics.BoxRef{App: 0, Name: fmt.Sprintf("%d", i)}) } appOptIns := pps.cinfo.OptIns[aidx] - sender = from if len(appOptIns) > 0 { indices := rand.Perm(len(appOptIns)) limit := 5 @@ -1200,24 +1200,27 @@ func (pps *WorkerState) constructAppTxn(from, to string, fee uint64, client *lib idx := indices[i] accounts = append(accounts, appOptIns[idx]) } - if pps.cinfo.AppParams[aidx].Creator == from { - // if the application was created by the "from" account, then we don't need to worry about it being opted-in. - } else { - fromIsOptedIn := false - for i := 0; i < len(appOptIns); i++ { - if appOptIns[i] == from { - fromIsOptedIn = true - break - } - } - if !fromIsOptedIn { - sender = accounts[0] - from = sender - } + // change `from` to an account that's opted-in. creator also allowed. + if pps.cinfo.AppParams[aidx].Creator != from && + !slices.Contains(appOptIns, from) { + from = accounts[0] } accounts = accounts[1:] } - txn, err = client.MakeUnsignedAppNoOpTx(aidx, nil, accounts, nil, nil, boxRefs, 0) + addresses := make([]basics.Address, 0, len(accounts)) + for _, acct := range accounts { + var addr basics.Address + addr, err = basics.UnmarshalChecksumAddress(acct) + if err != nil { + return + } + addresses = append(addresses, addr) + } + refs := libgoal.RefBundle{ + Accounts: addresses, + Boxes: boxRefs, + } + txn, err = client.MakeUnsignedAppNoOpTx(aidx, nil, refs, 0) if err != nil { return } diff --git a/test/e2e-go/features/accountPerf/sixMillion_test.go b/test/e2e-go/features/accountPerf/sixMillion_test.go index 1315fd041e..dd2139d746 100644 --- a/test/e2e-go/features/accountPerf/sixMillion_test.go +++ b/test/e2e-go/features/accountPerf/sixMillion_test.go @@ -1154,7 +1154,7 @@ int 1 // create the app appTx, err = client.MakeUnsignedAppCreateTx( - transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, nil, nil, nil, nil, 0) + transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, libgoal.RefBundle{}, 0) require.NoError(t, err) note := make([]byte, 8) @@ -1181,7 +1181,7 @@ func makeOptInAppTransaction( tLife uint64, genesisHash crypto.Digest) (appTx transactions.Transaction) { - appTx, err := client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, nil, nil, nil, nil, 0) + appTx, err := client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, libgoal.RefBundle{}, 0) require.NoError(t, err) appTx.Header = transactions.Header{ @@ -1287,7 +1287,7 @@ func callAppTransaction( tLife uint64, genesisHash crypto.Digest) (appTx transactions.Transaction) { - appTx, err := client.MakeUnsignedAppNoOpTx(uint64(appIdx), nil, nil, nil, nil, nil, 0) + appTx, err := client.MakeUnsignedAppNoOpTx(uint64(appIdx), nil, libgoal.RefBundle{}, 0) require.NoError(t, err) appTx.Header = transactions.Header{ diff --git a/test/e2e-go/features/transactions/accountv2_test.go b/test/e2e-go/features/transactions/accountv2_test.go index b833b55951..45591e9010 100644 --- a/test/e2e-go/features/transactions/accountv2_test.go +++ b/test/e2e-go/features/transactions/accountv2_test.go @@ -160,7 +160,7 @@ int 1 // create the app tx, err := client.MakeUnsignedAppCreateTx( - transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, nil, nil, nil, nil, 0) + transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(creator, 0, 0, fee, tx) a.NoError(err) @@ -215,7 +215,7 @@ int 1 checkEvalDelta(t, &client, txnRound, txnRound+1, 1, 1) // call the app - tx, err = client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(user, 0, 0, fee, tx) a.NoError(err) @@ -295,7 +295,7 @@ int 1 a.Equal(creator, app.Params.Creator) // call the app - tx, err = client.MakeUnsignedAppNoOpTx(uint64(appIdx), nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppNoOpTx(uint64(appIdx), nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(user, 0, 0, fee, tx) a.NoError(err) @@ -465,7 +465,7 @@ int 1 // create the app tx, err := client.MakeUnsignedAppCreateTx( - transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, nil, nil, nil, nil, 0) + transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(creator, 0, 0, fee, tx) a.NoError(err) @@ -520,7 +520,7 @@ int 1 checkEvalDelta(t, &client, txnRound, txnRound+1, 1, 1) // call the app - tx, err = client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, libgoal.RefBundle{}, 0) a.NoError(err) if foreignAssets != nil { tx.ForeignAssets = foreignAssets @@ -610,7 +610,7 @@ int 1 a.Equal(creator, app.Params.Creator) // call the app - tx, err = client.MakeUnsignedAppNoOpTx(uint64(appIdx), nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppNoOpTx(uint64(appIdx), nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(user, 0, 0, fee, tx) a.NoError(err) diff --git a/test/e2e-go/features/transactions/app_pages_test.go b/test/e2e-go/features/transactions/app_pages_test.go index d9a6b79753..27d791acbc 100644 --- a/test/e2e-go/features/transactions/app_pages_test.go +++ b/test/e2e-go/features/transactions/app_pages_test.go @@ -27,6 +27,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" + "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -89,7 +90,7 @@ return // create app 1 with 1 extra page app1ExtraPages := uint32(1) - tx, err := client.MakeUnsignedAppCreateTx(transactions.NoOpOC, smallProgram, smallProgram, globalSchema, localSchema, nil, nil, nil, nil, nil, app1ExtraPages) + tx, err := client.MakeUnsignedAppCreateTx(transactions.NoOpOC, smallProgram, smallProgram, globalSchema, localSchema, nil, libgoal.RefBundle{}, app1ExtraPages) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(baseAcct, 0, 0, 0, tx) a.NoError(err) @@ -110,7 +111,7 @@ return a.Equal(*accountInfo.AppsTotalExtraPages, uint64(app1ExtraPages)) // update app 1 and ensure the extra page still works - tx, err = client.MakeUnsignedAppUpdateTx(app1ID, nil, nil, nil, nil, nil, bigProgram, smallProgram, 0) + tx, err = client.MakeUnsignedAppUpdateTx(app1ID, nil, bigProgram, smallProgram, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(baseAcct, 0, 0, 0, tx) a.NoError(err) @@ -130,7 +131,7 @@ return // create app 2 with 2 extra pages app2ExtraPages := uint32(2) - tx, err = client.MakeUnsignedAppCreateTx(transactions.NoOpOC, bigProgram, smallProgram, globalSchema, localSchema, nil, nil, nil, nil, nil, app2ExtraPages) + tx, err = client.MakeUnsignedAppCreateTx(transactions.NoOpOC, bigProgram, smallProgram, globalSchema, localSchema, nil, libgoal.RefBundle{}, app2ExtraPages) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(baseAcct, 0, 0, 0, tx) a.NoError(err) @@ -151,7 +152,7 @@ return a.Equal(*accountInfo.AppsTotalExtraPages, uint64(app1ExtraPages+app2ExtraPages)) // delete app 1 - tx, err = client.MakeUnsignedAppDeleteTx(app1ID, nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppDeleteTx(app1ID, nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(baseAcct, 0, 0, 0, tx) a.NoError(err) @@ -170,7 +171,7 @@ return a.Equal(*accountInfo.AppsTotalExtraPages, uint64(app2ExtraPages)) // delete app 2 - tx, err = client.MakeUnsignedAppDeleteTx(app2ID, nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppDeleteTx(app2ID, nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(baseAcct, 0, 0, 0, tx) a.NoError(err) diff --git a/test/e2e-go/features/transactions/application_test.go b/test/e2e-go/features/transactions/application_test.go index b9530347e5..a3aa476b61 100644 --- a/test/e2e-go/features/transactions/application_test.go +++ b/test/e2e-go/features/transactions/application_test.go @@ -27,6 +27,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" + "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" @@ -85,7 +86,7 @@ log // create the app tx, err := client.MakeUnsignedAppCreateTx( - transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, nil, nil, nil, nil, 0) + transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(creator, 0, 0, fee, tx) a.NoError(err) diff --git a/test/e2e-go/restAPI/other/appsRestAPI_test.go b/test/e2e-go/restAPI/other/appsRestAPI_test.go index 781630806b..5f815846e4 100644 --- a/test/e2e-go/restAPI/other/appsRestAPI_test.go +++ b/test/e2e-go/restAPI/other/appsRestAPI_test.go @@ -32,6 +32,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" + "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" @@ -95,7 +96,7 @@ return lc := basics.StateSchema{} // create app - appCreateTxn, err := testClient.MakeUnsignedApplicationCallTx(0, nil, nil, nil, nil, nil, transactions.NoOpOC, approv, clst, gl, lc, 0, 0) + appCreateTxn, err := testClient.MakeUnsignedApplicationCallTx(0, nil, libgoal.RefBundle{}, transactions.NoOpOC, approv, clst, gl, lc, 0, 0) a.NoError(err) appCreateTxn, err = testClient.FillUnsignedTxTemplate(someAddress, 0, 0, 0, appCreateTxn) a.NoError(err) @@ -119,7 +120,7 @@ return a.NoError(err) // call app, which will issue an ASA create inner txn - appCallTxn, err := testClient.MakeUnsignedAppNoOpTx(uint64(createdAppID), nil, nil, nil, nil, nil, 0) + appCallTxn, err := testClient.MakeUnsignedAppNoOpTx(uint64(createdAppID), nil, libgoal.RefBundle{}, 0) a.NoError(err) appCallTxn, err = testClient.FillUnsignedTxTemplate(someAddress, 0, 0, 0, appCallTxn) a.NoError(err) @@ -231,8 +232,7 @@ end: // create app appCreateTxn, err := testClient.MakeUnsignedApplicationCallTx( - 0, nil, nil, nil, - nil, nil, transactions.NoOpOC, + 0, nil, libgoal.RefBundle{}, transactions.NoOpOC, approval, clearState, gl, lc, 0, 0, ) a.NoError(err) @@ -274,15 +274,10 @@ end: []byte(boxNames[i]), []byte(boxValues[i]), } - boxRef := transactions.BoxRef{ - Name: []byte(boxNames[i]), - Index: 0, - } + refs := libgoal.RefBundle{Boxes: []basics.BoxRef{{App: 0, Name: boxNames[i]}}} txns[i], err = testClient.MakeUnsignedAppNoOpTx( - uint64(createdAppID), appArgs, - nil, nil, nil, - []transactions.BoxRef{boxRef}, 0, + uint64(createdAppID), appArgs, refs, 0, ) a.NoError(err) txns[i], err = testClient.FillUnsignedTxTemplate(someAddress, 0, 0, 0, txns[i]) @@ -573,7 +568,7 @@ end: a.Equal(uint64(30), appAccountData.TotalBoxBytes) // delete the app - appDeleteTxn, err := testClient.MakeUnsignedAppDeleteTx(uint64(createdAppID), nil, nil, nil, nil, nil, 0) + appDeleteTxn, err := testClient.MakeUnsignedAppDeleteTx(uint64(createdAppID), nil, libgoal.RefBundle{}, 0) a.NoError(err) appDeleteTxn, err = testClient.FillUnsignedTxTemplate(someAddress, 0, 0, 0, appDeleteTxn) a.NoError(err) @@ -655,8 +650,7 @@ func TestBlockLogs(t *testing.T) { // create app appCreateTxn, err := testClient.MakeUnsignedApplicationCallTx( - 0, nil, nil, nil, - nil, nil, transactions.NoOpOC, + 0, nil, libgoal.RefBundle{}, transactions.NoOpOC, outerApproval, clearState, gl, lc, 0, 0, ) a.NoError(err) @@ -686,8 +680,7 @@ func TestBlockLogs(t *testing.T) { // call app twice appCallTxn, err := testClient.MakeUnsignedAppNoOpTx( - uint64(createdAppID), nil, nil, nil, - nil, nil, 0, + uint64(createdAppID), nil, libgoal.RefBundle{}, 0, ) a.NoError(err) appCallTxn0, err := testClient.FillUnsignedTxTemplate(someAddress, 0, 0, 0, appCallTxn) diff --git a/test/e2e-go/restAPI/simulate/simulateRestAPI_test.go b/test/e2e-go/restAPI/simulate/simulateRestAPI_test.go index 90e59773e8..ca5cc3441c 100644 --- a/test/e2e-go/restAPI/simulate/simulateRestAPI_test.go +++ b/test/e2e-go/restAPI/simulate/simulateRestAPI_test.go @@ -34,6 +34,7 @@ import ( "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/ledger/simulation" + "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" @@ -263,8 +264,7 @@ int 1` clearState := ops.Program txn, err := testClient.MakeUnsignedApplicationCallTx( - 0, nil, nil, nil, - nil, nil, transactions.NoOpOC, + 0, nil, libgoal.RefBundle{}, transactions.NoOpOC, approval, clearState, basics.StateSchema{}, basics.StateSchema{}, 0, 0, ) a.NoError(err) @@ -471,8 +471,7 @@ int 1` // create app appCreateTxn, err := testClient.MakeUnsignedApplicationCallTx( - 0, nil, nil, nil, - nil, nil, transactions.NoOpOC, + 0, nil, libgoal.RefBundle{}, transactions.NoOpOC, approval, clearState, gl, lc, 0, 0, ) a.NoError(err) @@ -501,8 +500,7 @@ int 1` // construct app call appCallTxn, err := testClient.MakeUnsignedAppNoOpTx( - uint64(createdAppID), [][]byte{[]byte("first-arg")}, - nil, nil, nil, nil, 0, + uint64(createdAppID), [][]byte{[]byte("first-arg")}, libgoal.RefBundle{}, 0, ) a.NoError(err) appCallTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, 0, appCallTxn) @@ -599,8 +597,7 @@ int 1` // create app appCreateTxn, err := testClient.MakeUnsignedApplicationCallTx( - 0, nil, nil, nil, - nil, nil, transactions.NoOpOC, + 0, nil, libgoal.RefBundle{}, transactions.NoOpOC, approval, clearState, gl, lc, 0, 0, ) a.NoError(err) @@ -629,7 +626,7 @@ int 1` // construct app call appCallTxn, err := testClient.MakeUnsignedAppNoOpTx( - uint64(createdAppID), nil, nil, nil, nil, nil, 0, + uint64(createdAppID), nil, libgoal.RefBundle{}, 0, ) a.NoError(err) appCallTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, 0, appCallTxn) @@ -868,7 +865,7 @@ func TestMaxDepthAppWithPCandStackTrace(t *testing.T) { // create app and get the application ID appCreateTxn, err := testClient.MakeUnsignedAppCreateTx( transactions.NoOpOC, approval, clearState, gl, - lc, nil, nil, nil, nil, nil, 0) + lc, nil, libgoal.RefBundle{}, 0) a.NoError(err) appCreateTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, 0, appCreateTxn) a.NoError(err) @@ -894,7 +891,7 @@ func TestMaxDepthAppWithPCandStackTrace(t *testing.T) { // construct app calls appCallTxn, err := testClient.MakeUnsignedAppNoOpTx( - uint64(futureAppID), [][]byte{uint64ToBytes(uint64(MaxDepth))}, nil, nil, nil, nil, 0, + uint64(futureAppID), [][]byte{uint64ToBytes(uint64(MaxDepth))}, libgoal.RefBundle{}, 0, ) a.NoError(err) appCallTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee*uint64(3*MaxDepth+2), appCallTxn) @@ -1716,7 +1713,7 @@ func TestSimulateScratchSlotChange(t *testing.T) { // create app and get the application ID appCreateTxn, err := testClient.MakeUnsignedAppCreateTx( transactions.NoOpOC, approval, clearState, gl, - lc, nil, nil, nil, nil, nil, 0) + lc, nil, libgoal.RefBundle{}, 0) a.NoError(err) appCreateTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, 0, appCreateTxn) a.NoError(err) @@ -1736,7 +1733,7 @@ func TestSimulateScratchSlotChange(t *testing.T) { // construct app calls appCallTxn, err := testClient.MakeUnsignedAppNoOpTx( - uint64(futureAppID), [][]byte{}, nil, nil, nil, nil, 0, + uint64(futureAppID), [][]byte{}, libgoal.RefBundle{}, 0, ) a.NoError(err) appCallTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appCallTxn) @@ -1910,7 +1907,7 @@ end: // create app and get the application ID appCreateTxn, err := testClient.MakeUnsignedAppCreateTx( transactions.NoOpOC, approval, clearState, gl, - lc, nil, nil, nil, nil, nil, 0) + lc, nil, libgoal.RefBundle{}, 0) a.NoError(err) appCreateTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, 0, appCreateTxn) a.NoError(err) @@ -1930,18 +1927,18 @@ end: // construct app call "global" appCallGlobalTxn, err := testClient.MakeUnsignedAppNoOpTx( - uint64(futureAppID), [][]byte{[]byte("global")}, nil, nil, nil, nil, 0, + uint64(futureAppID), [][]byte{[]byte("global")}, libgoal.RefBundle{}, 0, ) a.NoError(err) appCallGlobalTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appCallGlobalTxn) a.NoError(err) // construct app optin - appOptInTxn, err := testClient.MakeUnsignedAppOptInTx(uint64(futureAppID), nil, nil, nil, nil, nil, 0) + appOptInTxn, err := testClient.MakeUnsignedAppOptInTx(uint64(futureAppID), nil, libgoal.RefBundle{}, 0) a.NoError(err) appOptInTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appOptInTxn) // construct app call "global" appCallLocalTxn, err := testClient.MakeUnsignedAppNoOpTx( - uint64(futureAppID), [][]byte{[]byte("local")}, nil, nil, nil, nil, 0, + uint64(futureAppID), [][]byte{[]byte("local")}, libgoal.RefBundle{}, 0, ) a.NoError(err) appCallLocalTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appCallLocalTxn) @@ -2192,7 +2189,7 @@ end: // create app and get the application ID appCreateTxn, err := testClient.MakeUnsignedAppCreateTx( transactions.NoOpOC, approval, clearState, gl, - lc, nil, nil, nil, nil, nil, 0) + lc, nil, libgoal.RefBundle{}, 0) a.NoError(err) appCreateTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, 0, appCreateTxn) a.NoError(err) @@ -2212,18 +2209,18 @@ end: // construct app call "global" appCallGlobalTxn, err := testClient.MakeUnsignedAppNoOpTx( - uint64(futureAppID), [][]byte{[]byte("global")}, nil, nil, nil, nil, 0, + uint64(futureAppID), [][]byte{[]byte("global")}, libgoal.RefBundle{}, 0, ) a.NoError(err) appCallGlobalTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appCallGlobalTxn) a.NoError(err) // construct app optin - appOptInTxn, err := testClient.MakeUnsignedAppOptInTx(uint64(futureAppID), nil, nil, nil, nil, nil, 0) + appOptInTxn, err := testClient.MakeUnsignedAppOptInTx(uint64(futureAppID), nil, libgoal.RefBundle{}, 0) a.NoError(err) appOptInTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appOptInTxn) // construct app call "local" appCallLocalTxn, err := testClient.MakeUnsignedAppNoOpTx( - uint64(futureAppID), [][]byte{[]byte("local")}, nil, nil, nil, nil, 0, + uint64(futureAppID), [][]byte{[]byte("local")}, libgoal.RefBundle{}, 0, ) a.NoError(err) appCallLocalTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appCallLocalTxn) @@ -2520,7 +2517,7 @@ func TestSimulateWithUnnamedResources(t *testing.T) { lc := basics.StateSchema{} // create app - txn, err = testClient.MakeUnsignedAppCreateTx(transactions.OptInOC, alwaysApprove, alwaysApprove, gl, lc, nil, nil, nil, nil, nil, 0) + txn, err = testClient.MakeUnsignedAppCreateTx(transactions.OptInOC, alwaysApprove, alwaysApprove, gl, lc, nil, libgoal.RefBundle{}, 0) a.NoError(err) txn, err = testClient.FillUnsignedTxTemplate(otherAddress, 0, 0, 0, txn) a.NoError(err) @@ -2585,7 +2582,7 @@ assert // Box access byte "A" -int 1025 +int 2049 // need three refs with old quota, two after the bump (we only test latest) box_create assert @@ -2598,7 +2595,7 @@ int 1 approval := ops.Program // create app - txn, err = testClient.MakeUnsignedAppCreateTx(transactions.NoOpOC, approval, alwaysApprove, gl, lc, nil, nil, nil, nil, nil, 0) + txn, err = testClient.MakeUnsignedAppCreateTx(transactions.NoOpOC, approval, alwaysApprove, gl, lc, nil, libgoal.RefBundle{}, 0) a.NoError(err) txn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, 0, txn) a.NoError(err) @@ -2624,7 +2621,7 @@ int 1 // construct app call txn, err = testClient.MakeUnsignedAppNoOpTx( - uint64(testAppID), nil, nil, nil, nil, nil, 0, + uint64(testAppID), nil, libgoal.RefBundle{}, 0, ) a.NoError(err) txn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, 0, txn) @@ -2642,7 +2639,7 @@ int 1 AllowUnnamedResources: false, }) a.NoError(err) - a.Contains(*resp.TxnGroups[0].FailureMessage, "logic eval error: invalid Account reference "+otherAddress) + a.Contains(*resp.TxnGroups[0].FailureMessage, "logic eval error: unavailable Account "+otherAddress) a.Equal([]uint64{0}, *resp.TxnGroups[0].FailedAt) // It should work with AllowUnnamedResources=true diff --git a/test/e2e-go/upgrades/application_support_test.go b/test/e2e-go/upgrades/application_support_test.go index 2570a76046..b61b350481 100644 --- a/test/e2e-go/upgrades/application_support_test.go +++ b/test/e2e-go/upgrades/application_support_test.go @@ -27,6 +27,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" + "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" @@ -150,7 +151,7 @@ int 1 // create the app tx, err := client.MakeUnsignedAppCreateTx( - transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, nil, nil, nil, nil, 0) + transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(creator, 0, 0, fee, tx) a.NoError(err) @@ -233,7 +234,7 @@ int 1 a.Equal(uint64(1), value.Uint) // call the app - tx, err = client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(user, 0, 0, fee, tx) a.NoError(err) @@ -392,7 +393,7 @@ int 1 // create the app tx, err := client.MakeUnsignedAppCreateTx( - transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, nil, nil, nil, nil, 0) + transactions.OptInOC, approvalOps.Program, clearstateOps.Program, schema, schema, nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(creator, round, round+primaryNodeUnupgradedProtocol.DefaultUpgradeWaitRounds, fee, tx) a.NoError(err) @@ -484,7 +485,7 @@ int 1 a.Equal(uint64(1), value.Uint) // call the app - tx, err = client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, nil, nil, nil, nil, 0) + tx, err = client.MakeUnsignedAppOptInTx(uint64(appIdx), nil, libgoal.RefBundle{}, 0) a.NoError(err) tx, err = client.FillUnsignedTxTemplate(user, 0, 0, fee, tx) a.NoError(err) diff --git a/test/scripts/e2e_subs/e2e-app-simulate.sh b/test/scripts/e2e_subs/e2e-app-simulate.sh index 71f9a22b04..c09e1f0fe7 100755 --- a/test/scripts/e2e_subs/e2e-app-simulate.sh +++ b/test/scripts/e2e_subs/e2e-app-simulate.sh @@ -466,7 +466,7 @@ if [[ $(echo "$RES" | jq '."txn-groups" | any(has("failure-message"))') != $CONS false fi -EXPECTED_FAILURE="logic eval error: invalid Account reference $OTHERADDR" +EXPECTED_FAILURE="logic eval error: unavailable Account $OTHERADDR" if [[ $(echo "$RES" | jq '."txn-groups"[0]."failure-message"') != *"${EXPECTED_FAILURE}"* ]]; then date '+app-simulate-test FAIL the app call without allow unnamed resources should fail with the expected error %Y%m%d_%H%M%S' diff --git a/test/scripts/e2e_subs/shared-resources.py b/test/scripts/e2e_subs/shared-resources.py index 2df56f7893..7899a583f2 100755 --- a/test/scripts/e2e_subs/shared-resources.py +++ b/test/scripts/e2e_subs/shared-resources.py @@ -52,7 +52,7 @@ # Won't work, because v8 can't modify an account (goal.account) that # isn't in the `grp2` txn assert err -assert "invalid Account reference "+goal.account in str(err) +assert "unavailable Account "+goal.account in str(err) # Now, upgrade program to same thing, but v9 diff --git a/util/fn.go b/util/fn.go new file mode 100644 index 0000000000..34372a5cd0 --- /dev/null +++ b/util/fn.go @@ -0,0 +1,54 @@ +// Copyright (C) 2019-2025 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package util + +/* Functions inspired by functional languages. */ + +// Map applies a function to each element of the input slice and returns a new +// slice with the transformed elements. A nil slice returns nil. +func Map[X any, Y any](input []X, fn func(X) Y) []Y { + // preserve nil-ness + if input == nil { + return nil + } + + output := make([]Y, len(input)) + for i := range input { + output[i] = fn(input[i]) + } + return output +} + +// MapErr applies a function to each element of the input slice and returns a +// new slice with the transformed elements. If the function returns a non-nil +// error, MapErr returns immediately with a nil slice and the error. +func MapErr[X any, Y any](input []X, fn func(X) (Y, error)) ([]Y, error) { + // preserve nil-ness + if input == nil { + return nil, nil + } + + output := make([]Y, len(input)) + for i := range input { + y, err := fn(input[i]) + if err != nil { + return nil, err + } + output[i] = y + } + return output, nil +}