diff options
author | Hang Su <87964331+ahangsu@users.noreply.github.com> | 2022-06-30 17:14:27 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-30 17:14:27 -0400 |
commit | f59d3acd7b9c3f1b6bcf7df19e24fd0850c7b94e (patch) | |
tree | a8f6426e9d80bccce0015c9dca8ffc1bacfcda4e | |
parent | 43435b7c2ff8ea5d6e7792f60a341200e50cc788 (diff) | |
parent | a1553588f74aeaaafde89809e8515ea659f3e649 (diff) |
Merge pull request #4183 from algochoi/algochoi/box-search-apiahangsu/box-names-by-app-id
Algod: Modify Box Search API to take name as query parameter
-rw-r--r-- | cmd/goal/application.go | 98 | ||||
-rw-r--r-- | cmd/goal/formatting_test.go | 19 | ||||
-rw-r--r-- | cmd/goal/interact.go | 4 | ||||
-rw-r--r-- | daemon/algod/api/algod.oas2.json | 14 | ||||
-rw-r--r-- | daemon/algod/api/algod.oas3.yml | 70 | ||||
-rw-r--r-- | daemon/algod/api/client/restClient.go | 10 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/generated/private/routes.go | 34 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/generated/routes.go | 463 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/generated/types.go | 7 | ||||
-rw-r--r-- | daemon/algod/api/server/v2/handlers.go | 18 | ||||
-rw-r--r-- | data/transactions/logic/parsing.go | 105 | ||||
-rw-r--r-- | data/transactions/logic/parsing_test.go | 49 | ||||
-rw-r--r-- | libgoal/libgoal.go | 9 | ||||
-rw-r--r-- | test/e2e-go/restAPI/restClient_test.go | 54 |
14 files changed, 556 insertions, 398 deletions
diff --git a/cmd/goal/application.go b/cmd/goal/application.go index 08497552d..bf0ca9230 100644 --- a/cmd/goal/application.go +++ b/cmd/goal/application.go @@ -19,9 +19,6 @@ package main import ( "bytes" "crypto/sha512" - "encoding/base32" - "encoding/base64" - "encoding/binary" "encoding/hex" "errors" "fmt" @@ -195,33 +192,25 @@ func init() { methodAppCmd.Flags().MarkHidden("app-arg") // nolint:errcheck } -type appCallBytes struct { - Encoding string `codec:"encoding"` - Value string `codec:"value"` -} - -func newAppCallBytes(arg string) appCallBytes { - parts := strings.SplitN(arg, ":", 2) - if len(parts) != 2 { - reportErrorf("all arguments and box names should be of the form 'encoding:value'") - } - return appCallBytes{ - Encoding: parts[0], - Value: parts[1], +func newAppCallBytes(arg string) logic.AppCallBytes { + appBytes, err := logic.NewAppCallBytes(arg) + if err != nil { + reportErrorf(err.Error()) } + return appBytes } type appCallInputs struct { - Accounts []string `codec:"accounts"` - ForeignApps []uint64 `codec:"foreignapps"` - ForeignAssets []uint64 `codec:"foreignassets"` - Boxes []boxRef `codec:"boxes"` - Args []appCallBytes `codec:"args"` + Accounts []string `codec:"accounts"` + ForeignApps []uint64 `codec:"foreignapps"` + ForeignAssets []uint64 `codec:"foreignassets"` + Boxes []boxRef `codec:"boxes"` + Args []logic.AppCallBytes `codec:"args"` } type boxRef struct { - appID uint64 `codec:"app"` - name appCallBytes `codec:"name"` + appID uint64 `codec:"app"` + name logic.AppCallBytes `codec:"name"` } // newBoxRef parses a command-line box ref, which is an optional appId, a comma, @@ -270,67 +259,10 @@ func stringsToBoxRefs(strs []string) []boxRef { return out } -func (arg appCallBytes) raw() (rawValue []byte, parseErr error) { - switch arg.Encoding { - case "str", "string": - rawValue = []byte(arg.Value) - case "int", "integer": - num, err := strconv.ParseUint(arg.Value, 10, 64) - if err != nil { - parseErr = fmt.Errorf("Could not parse uint64 from string (%s): %v", arg.Value, err) - return - } - ibytes := make([]byte, 8) - binary.BigEndian.PutUint64(ibytes, num) - rawValue = ibytes - case "addr", "address": - addr, err := basics.UnmarshalChecksumAddress(arg.Value) - if err != nil { - parseErr = fmt.Errorf("Could not unmarshal checksummed address from string (%s): %v", arg.Value, err) - return - } - rawValue = addr[:] - case "b32", "base32", "byte base32": - data, err := base32.StdEncoding.DecodeString(arg.Value) - if err != nil { - parseErr = fmt.Errorf("Could not decode base32-encoded string (%s): %v", arg.Value, err) - return - } - rawValue = data - case "b64", "base64", "byte base64": - data, err := base64.StdEncoding.DecodeString(arg.Value) - if err != nil { - parseErr = fmt.Errorf("Could not decode base64-encoded string (%s): %v", arg.Value, err) - return - } - rawValue = data - case "abi": - typeAndValue := strings.SplitN(arg.Value, ":", 2) - if len(typeAndValue) != 2 { - parseErr = fmt.Errorf("Could not decode abi string (%s): should split abi-type and abi-value with colon", arg.Value) - return - } - abiType, err := abi.TypeOf(typeAndValue[0]) - if err != nil { - parseErr = fmt.Errorf("Could not decode abi type string (%s): %v", typeAndValue[0], err) - return - } - value, err := abiType.UnmarshalFromJSON([]byte(typeAndValue[1])) - if err != nil { - parseErr = fmt.Errorf("Could not decode abi value string (%s):%v ", typeAndValue[1], err) - return - } - return abiType.Encode(value) - default: - parseErr = fmt.Errorf("Unknown encoding: %s", arg.Encoding) - } - return -} - func translateBoxRefs(input []boxRef, foreignApps []uint64) []transactions.BoxRef { output := make([]transactions.BoxRef, len(input)) for i, tbr := range input { - rawName, err := tbr.name.raw() + rawName, err := tbr.name.Raw() if err != nil { reportErrorf("Could not decode box name %s: %v", tbr.name, err) } @@ -372,7 +304,7 @@ func parseAppInputs(inputs appCallInputs) (args [][]byte, accounts []string, for boxes = translateBoxRefs(inputs.Boxes, foreignApps) args = make([][]byte, len(inputs.Args)) for i, arg := range inputs.Args { - rawValue, err := arg.raw() + rawValue, err := arg.Raw() if err != nil { reportErrorf("Could not decode input at index %d: %v", i, err) } @@ -410,7 +342,7 @@ func getAppInputs() (args [][]byte, accounts []string, apps []uint64, assets []u // on it. appArgs became `StringArrayVar` in order to support abi arguments // which contain commas. - var encodedArgs []appCallBytes + var encodedArgs []logic.AppCallBytes for _, arg := range appArgs { if len(arg) > 0 { encodedArgs = append(encodedArgs, newAppCallBytes(arg)) diff --git a/cmd/goal/formatting_test.go b/cmd/goal/formatting_test.go index 725044ebe..ab42e285d 100644 --- a/cmd/goal/formatting_test.go +++ b/cmd/goal/formatting_test.go @@ -45,23 +45,14 @@ func TestUnicodePrintable(t *testing.T) { func TestNewAppCallBytes(t *testing.T) { partitiontest.PartitionTest(t) - acb := newAppCallBytes("str:hello") - require.Equal(t, "str", acb.Encoding) - require.Equal(t, "hello", acb.Value) - _, err := acb.raw() - require.NoError(t, err) - - require.Panics(t, func() { newAppCallBytes("hello") }) - acb = newAppCallBytes("str:1:2") - require.Equal(t, "str", acb.Encoding) - require.Equal(t, "1:2", acb.Value) - _, err = acb.raw() + acb := newAppCallBytes("int:3") + require.Equal(t, "int", acb.Encoding) + require.Equal(t, "3", acb.Value) + _, err := acb.Raw() require.NoError(t, err) - acb = newAppCallBytes(":x") - _, err = acb.raw() - require.Error(t, err) + require.Panics(t, func() { newAppCallBytes("hello") }) } func TestNewBoxRef(t *testing.T) { diff --git a/cmd/goal/interact.go b/cmd/goal/interact.go index 8677f4738..caf4db717 100644 --- a/cmd/goal/interact.go +++ b/cmd/goal/interact.go @@ -512,7 +512,7 @@ var appExecuteCmd = &cobra.Command{ var inputs appCallInputs for _, arg := range proc.Args { - var callArg appCallBytes + var callArg logic.AppCallBytes callArg.Encoding = arg.Kind if !procFlags.Changed(arg.Name) && arg.Default != "" { @@ -564,7 +564,7 @@ var appExecuteCmd = &cobra.Command{ appArgs := make([][]byte, len(inputs.Args)) for i, arg := range inputs.Args { - rawValue, err := arg.raw() + rawValue, err := arg.Raw() if err != nil { reportErrorf("Could not parse argument corresponding to '%s': %v", proc.Args[i].Name, err) } diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index df624fc33..ea7c5651a 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -1372,9 +1372,9 @@ } ] }, - "/v2/applications/{application-id}/boxes/{box-name}": { + "/v2/applications/{application-id}/box": { "get": { - "description": "Given an application ID and box name, it returns the box name and value (each base64 encoded).", + "description": "Given an application ID and box name, it returns the box name and value (each base64 encoded). Box names must be in the goal app call arg encoding form 'encoding:value'. 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...'.", "produces": [ "application/json" ], @@ -1393,9 +1393,9 @@ }, { "type": "string", - "description": "A box name", - "name": "box-name", - "in": "path", + "description": "A box name, in the goal app call arg form 'encoding:value'. 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...'.", + "name": "name", + "in": "query", "required": true } ], @@ -1442,8 +1442,8 @@ }, { "type": "string", - "name": "box-name", - "in": "path", + "name": "name", + "in": "query", "required": true } ] diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index d622baf1e..822a074a8 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -2424,10 +2424,10 @@ "summary": "Get application information." } }, - "/v2/applications/{application-id}/boxes": { + "/v2/applications/{application-id}/box": { "get": { - "description": "Given an application ID, it returns the box names of that application. No particular ordering is guaranteed.", - "operationId": "GetApplicationBoxes", + "description": "Given an application ID and box name, it returns the box name and value (each base64 encoded). Box names must be in the goal app call arg encoding form 'encoding:value'. 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...'.", + "operationId": "GetApplicationBoxByName", "parameters": [ { "description": "An application identifier", @@ -2439,11 +2439,12 @@ } }, { - "description": "Max number of box names to return. If max is not set, or max == 0, returns all box-names.", + "description": "A box name, in the goal app call arg form 'encoding:value'. 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...'.", "in": "query", - "name": "max", + "name": "name", + "required": true, "schema": { - "type": "integer" + "type": "string" } } ], @@ -2452,16 +2453,11 @@ "content": { "application/json": { "schema": { - "items": { - "format": "byte", - "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", - "type": "string" - }, - "type": "array" + "$ref": "#/components/schemas/Box" } } }, - "description": "Box names of an application" + "description": "Box information" }, "400": { "content": { @@ -2483,6 +2479,16 @@ }, "description": "Invalid API Token" }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Box Not Found" + }, "500": { "content": { "application/json": { @@ -2498,13 +2504,13 @@ "description": "Unknown Error" } }, - "summary": "Get box names for a given application." + "summary": "Get box information for a given application." } }, - "/v2/applications/{application-id}/boxes/{box-name}": { + "/v2/applications/{application-id}/boxes": { "get": { - "description": "Given an application ID and box name, it returns the box name and value (each base64 encoded).", - "operationId": "GetApplicationBoxByName", + "description": "Given an application ID, it returns the box names of that application. No particular ordering is guaranteed.", + "operationId": "GetApplicationBoxes", "parameters": [ { "description": "An application identifier", @@ -2516,12 +2522,11 @@ } }, { - "description": "A box name", - "in": "path", - "name": "box-name", - "required": true, + "description": "Max number of box names to return. If max is not set, or max == 0, returns all box-names.", + "in": "query", + "name": "max", "schema": { - "type": "string" + "type": "integer" } } ], @@ -2530,11 +2535,16 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Box" + "items": { + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "type": "array" } } }, - "description": "Box information" + "description": "Box names of an application" }, "400": { "content": { @@ -2556,16 +2566,6 @@ }, "description": "Invalid API Token" }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - }, - "description": "Box Not Found" - }, "500": { "content": { "application/json": { @@ -2581,7 +2581,7 @@ "description": "Unknown Error" } }, - "summary": "Get box information for a given application." + "summary": "Get box names for a given application." } }, "/v2/assets/{asset-id}": { diff --git a/daemon/algod/api/client/restClient.go b/daemon/algod/api/client/restClient.go index b44ceff80..19d142967 100644 --- a/daemon/algod/api/client/restClient.go +++ b/daemon/algod/api/client/restClient.go @@ -428,6 +428,16 @@ func (client RestClient) ApplicationBoxes(appID uint64) (response generatedV2.Bo return } +type applicationBoxByNameParams struct { + Name string `url:"name"` +} + +// GetApplicationBoxByName gets the BoxResponse associated with the passed application ID and box name +func (client RestClient) GetApplicationBoxByName(appID uint64, name string) (response generatedV2.BoxResponse, err error) { + err = client.get(&response, fmt.Sprintf("/v2/applications/%d/box", appID), applicationBoxByNameParams{name}) + return +} + // AccountInformationV2 gets the AccountData associated with the passed address func (client RestClient) AccountInformationV2(address string, includeCreatables bool) (response generatedV2.Account, err error) { var infoParams accountInformationParams diff --git a/daemon/algod/api/server/v2/generated/private/routes.go b/daemon/algod/api/server/v2/generated/private/routes.go index 9cb47a1b0..cc0191404 100644 --- a/daemon/algod/api/server/v2/generated/private/routes.go +++ b/daemon/algod/api/server/v2/generated/private/routes.go @@ -445,23 +445,23 @@ var swaggerSpec = []string{ "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", + "Gvuf65vX7cLVp6v/HwAA//84pB4p47UAAA==", } // GetSwagger returns the Swagger specification corresponding to the generated code diff --git a/daemon/algod/api/server/v2/generated/routes.go b/daemon/algod/api/server/v2/generated/routes.go index afe0dc119..f2ac89364 100644 --- a/daemon/algod/api/server/v2/generated/routes.go +++ b/daemon/algod/api/server/v2/generated/routes.go @@ -32,12 +32,12 @@ type ServerInterface interface { // Get application information. // (GET /v2/applications/{application-id}) GetApplicationByID(ctx echo.Context, applicationId uint64) error + // Get box information for a given application. + // (GET /v2/applications/{application-id}/box) + GetApplicationBoxByName(ctx echo.Context, applicationId uint64, params GetApplicationBoxByNameParams) error // Get box names for a given application. // (GET /v2/applications/{application-id}/boxes) GetApplicationBoxes(ctx echo.Context, applicationId uint64, params GetApplicationBoxesParams) error - // Get box information for a given application. - // (GET /v2/applications/{application-id}/boxes/{box-name}) - GetApplicationBoxByName(ctx echo.Context, applicationId uint64, boxName string) error // Get asset information. // (GET /v2/assets/{asset-id}) GetAssetByID(ctx echo.Context, assetId uint64) error @@ -324,12 +324,12 @@ func (w *ServerInterfaceWrapper) GetApplicationByID(ctx echo.Context) error { return err } -// GetApplicationBoxes converts echo context to params. -func (w *ServerInterfaceWrapper) GetApplicationBoxes(ctx echo.Context) error { +// GetApplicationBoxByName converts echo context to params. +func (w *ServerInterfaceWrapper) GetApplicationBoxByName(ctx echo.Context) error { validQueryParams := map[string]bool{ "pretty": true, - "max": true, + "name": true, } // Check for unknown query parameters. @@ -351,27 +351,30 @@ func (w *ServerInterfaceWrapper) GetApplicationBoxes(ctx echo.Context) error { ctx.Set("api_key.Scopes", []string{""}) // Parameter object where we will unmarshal all parameters from the context - var params GetApplicationBoxesParams - // ------------- Optional query parameter "max" ------------- - if paramValue := ctx.QueryParam("max"); paramValue != "" { + var params GetApplicationBoxByNameParams + // ------------- Required query parameter "name" ------------- + if paramValue := ctx.QueryParam("name"); paramValue != "" { + } else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Query argument name is required, but not found")) } - err = runtime.BindQueryParameter("form", true, false, "max", ctx.QueryParams(), ¶ms.Max) + err = runtime.BindQueryParameter("form", true, true, "name", ctx.QueryParams(), ¶ms.Name) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter max: %s", err)) + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter name: %s", err)) } // Invoke the callback with all the unmarshalled arguments - err = w.Handler.GetApplicationBoxes(ctx, applicationId, params) + err = w.Handler.GetApplicationBoxByName(ctx, applicationId, params) return err } -// GetApplicationBoxByName converts echo context to params. -func (w *ServerInterfaceWrapper) GetApplicationBoxByName(ctx echo.Context) error { +// GetApplicationBoxes converts echo context to params. +func (w *ServerInterfaceWrapper) GetApplicationBoxes(ctx echo.Context) error { validQueryParams := map[string]bool{ "pretty": true, + "max": true, } // Check for unknown query parameters. @@ -390,18 +393,22 @@ func (w *ServerInterfaceWrapper) GetApplicationBoxByName(ctx echo.Context) error return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter application-id: %s", err)) } - // ------------- Path parameter "box-name" ------------- - var boxName string + ctx.Set("api_key.Scopes", []string{""}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetApplicationBoxesParams + // ------------- Optional query parameter "max" ------------- + if paramValue := ctx.QueryParam("max"); paramValue != "" { - err = runtime.BindStyledParameter("simple", false, "box-name", ctx.Param("box-name"), &boxName) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter box-name: %s", err)) } - ctx.Set("api_key.Scopes", []string{""}) + err = runtime.BindQueryParameter("form", true, false, "max", ctx.QueryParams(), ¶ms.Max) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter max: %s", err)) + } // Invoke the callback with all the unmarshalled arguments - err = w.Handler.GetApplicationBoxByName(ctx, applicationId, boxName) + err = w.Handler.GetApplicationBoxes(ctx, applicationId, params) return err } @@ -856,8 +863,8 @@ func RegisterHandlers(router interface { router.GET("/v2/accounts/:address/assets/:asset-id", wrapper.AccountAssetInformation, m...) router.GET("/v2/accounts/:address/transactions/pending", wrapper.GetPendingTransactionsByAddress, m...) router.GET("/v2/applications/:application-id", wrapper.GetApplicationByID, m...) + router.GET("/v2/applications/:application-id/box", wrapper.GetApplicationBoxByName, m...) router.GET("/v2/applications/:application-id/boxes", wrapper.GetApplicationBoxes, m...) - router.GET("/v2/applications/:application-id/boxes/:box-name", wrapper.GetApplicationBoxByName, m...) router.GET("/v2/assets/:asset-id", wrapper.GetAssetByID, m...) router.GET("/v2/blocks/:round", wrapper.GetBlock, m...) router.GET("/v2/blocks/:round/transactions/:txid/proof", wrapper.GetProof, m...) @@ -877,211 +884,213 @@ func RegisterHandlers(router interface { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the Swagger specification corresponding to the generated code diff --git a/daemon/algod/api/server/v2/generated/types.go b/daemon/algod/api/server/v2/generated/types.go index b6448a196..3447dd37d 100644 --- a/daemon/algod/api/server/v2/generated/types.go +++ b/daemon/algod/api/server/v2/generated/types.go @@ -830,6 +830,13 @@ type GetPendingTransactionsByAddressParams struct { Format *string `json:"format,omitempty"` } +// GetApplicationBoxByNameParams defines parameters for GetApplicationBoxByName. +type GetApplicationBoxByNameParams struct { + + // A box name, in the goal app call arg form 'encoding:value'. 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...'. + Name string `json:"name"` +} + // GetApplicationBoxesParams defines parameters for GetApplicationBoxes. type GetApplicationBoxesParams struct { diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 5c9a8820a..e7f737d51 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -24,7 +24,6 @@ import ( "io" "math" "net/http" - "net/url" "strings" "time" @@ -1174,25 +1173,30 @@ func (v2 *Handlers) GetApplicationBoxes(ctx echo.Context, applicationID uint64, } // GetApplicationBoxByName returns the value of an application's box -// (GET /v2/applications/{application-id}/boxes/{box-name}) -func (v2 *Handlers) GetApplicationBoxByName(ctx echo.Context, applicationID uint64, boxName string) error { +// (GET /v2/applications/{application-id}/box) +func (v2 *Handlers) GetApplicationBoxByName(ctx echo.Context, applicationID uint64, params generated.GetApplicationBoxByNameParams) error { appIdx := basics.AppIndex(applicationID) ledger := v2.Node.LedgerForAPI() - lastRound := ledger.Latest() - boxName, err := url.PathUnescape(boxName) + encodedBoxName := params.Name + boxNameBytes, err := logic.NewAppCallBytes(encodedBoxName) if err != nil { return badRequest(ctx, err, err.Error(), v2.Log) } - value, err := ledger.LookupKv(lastRound, logic.MakeBoxKey(appIdx, boxName)) + boxName, err := boxNameBytes.Raw() if err != nil { - return internalError(ctx, err, errFailedLookingUpLedger, v2.Log) + return badRequest(ctx, err, err.Error(), v2.Log) } + value, err := ledger.LookupKv(lastRound, logic.MakeBoxKey(appIdx, string(boxName))) + if err != nil { + return internalError(ctx, err, errFailedLookingUpLedger, v2.Log) + } if value == nil { return notFound(ctx, errors.New(errBoxDoesNotExist), errBoxDoesNotExist, v2.Log) } + response := generated.BoxResponse{ Name: []byte(boxName), Value: []byte(*value), diff --git a/data/transactions/logic/parsing.go b/data/transactions/logic/parsing.go new file mode 100644 index 000000000..95526ec19 --- /dev/null +++ b/data/transactions/logic/parsing.go @@ -0,0 +1,105 @@ +// Copyright (C) 2019-2022 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 <https://www.gnu.org/licenses/>. + +package logic + +import ( + "encoding/base32" + "encoding/base64" + "encoding/binary" + "fmt" + "strconv" + "strings" + + "github.com/algorand/go-algorand/data/abi" + "github.com/algorand/go-algorand/data/basics" +) + +// AppCallBytes represents an encoding and a value of an app call argument. +type AppCallBytes struct { + Encoding string `codec:"encoding"` + Value string `codec:"value"` +} + +// NewAppCallBytes parses an argument of "form encoding:value" to AppCallBytes. +func NewAppCallBytes(arg string) (AppCallBytes, error) { + parts := strings.SplitN(arg, ":", 2) + if len(parts) != 2 { + return AppCallBytes{}, fmt.Errorf("all arguments and box names should be of the form 'encoding:value'") + } + return AppCallBytes{ + Encoding: parts[0], + Value: parts[1], + }, nil +} + +// Raw converts an AppCallBytes arg to a byte array. +func (arg AppCallBytes) Raw() (rawValue []byte, parseErr error) { + switch arg.Encoding { + case "str", "string": + rawValue = []byte(arg.Value) + case "int", "integer": + num, err := strconv.ParseUint(arg.Value, 10, 64) + if err != nil { + parseErr = fmt.Errorf("Could not parse uint64 from string (%s): %v", arg.Value, err) + return + } + ibytes := make([]byte, 8) + binary.BigEndian.PutUint64(ibytes, num) + rawValue = ibytes + case "addr", "address": + addr, err := basics.UnmarshalChecksumAddress(arg.Value) + if err != nil { + parseErr = fmt.Errorf("Could not unmarshal checksummed address from string (%s): %v", arg.Value, err) + return + } + rawValue = addr[:] + case "b32", "base32", "byte base32": + data, err := base32.StdEncoding.DecodeString(arg.Value) + if err != nil { + parseErr = fmt.Errorf("Could not decode base32-encoded string (%s): %v", arg.Value, err) + return + } + rawValue = data + case "b64", "base64", "byte base64": + data, err := base64.StdEncoding.DecodeString(arg.Value) + if err != nil { + parseErr = fmt.Errorf("Could not decode base64-encoded string (%s): %v", arg.Value, err) + return + } + rawValue = data + case "abi": + typeAndValue := strings.SplitN(arg.Value, ":", 2) + if len(typeAndValue) != 2 { + parseErr = fmt.Errorf("Could not decode abi string (%s): should split abi-type and abi-value with colon", arg.Value) + return + } + abiType, err := abi.TypeOf(typeAndValue[0]) + if err != nil { + parseErr = fmt.Errorf("Could not decode abi type string (%s): %v", typeAndValue[0], err) + return + } + value, err := abiType.UnmarshalFromJSON([]byte(typeAndValue[1])) + if err != nil { + parseErr = fmt.Errorf("Could not decode abi value string (%s):%v ", typeAndValue[1], err) + return + } + return abiType.Encode(value) + default: + parseErr = fmt.Errorf("Unknown encoding: %s", arg.Encoding) + } + return +} diff --git a/data/transactions/logic/parsing_test.go b/data/transactions/logic/parsing_test.go new file mode 100644 index 000000000..5fbe7f55d --- /dev/null +++ b/data/transactions/logic/parsing_test.go @@ -0,0 +1,49 @@ +// Copyright (C) 2019-2022 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 <https://www.gnu.org/licenses/>. + +package logic + +import ( + "testing" + + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +func TestNewAppCallBytes(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + acb, err := NewAppCallBytes("str:hello") + require.NoError(t, err) + require.Equal(t, "str", acb.Encoding) + require.Equal(t, "hello", acb.Value) + _, err = acb.Raw() + require.NoError(t, err) + + acb, err = NewAppCallBytes("hello") + require.Error(t, err) + + acb, err = NewAppCallBytes("str:1:2") + require.Equal(t, "str", acb.Encoding) + require.Equal(t, "1:2", acb.Value) + _, err = acb.Raw() + require.NoError(t, err) + + acb, err = NewAppCallBytes(":x") + _, err = acb.Raw() + require.Error(t, err) +} diff --git a/libgoal/libgoal.go b/libgoal/libgoal.go index 722e3c72b..798d93bf7 100644 --- a/libgoal/libgoal.go +++ b/libgoal/libgoal.go @@ -785,6 +785,15 @@ func (c *Client) ApplicationBoxes(index uint64) (resp generatedV2.BoxesResponse, return } +// GetApplicationBoxByName takes an app's index and box name and returns its value +func (c *Client) GetApplicationBoxByName(index uint64, name string) (resp generatedV2.BoxResponse, err error) { + algod, err := c.ensureAlgodClient() + if err == nil { + resp, err = algod.GetApplicationBoxByName(index, name) + } + return +} + // TransactionInformation takes an address and associated txid and return its information func (c *Client) TransactionInformation(addr, txid string) (resp v1.Transaction, err error) { algod, err := c.ensureAlgodClient() diff --git a/test/e2e-go/restAPI/restClient_test.go b/test/e2e-go/restAPI/restClient_test.go index 4c9b5de41..e227aae8f 100644 --- a/test/e2e-go/restAPI/restClient_test.go +++ b/test/e2e-go/restAPI/restClient_test.go @@ -18,10 +18,10 @@ package restapi import ( "context" + "encoding/binary" "encoding/hex" "errors" "flag" - "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated" "math" "math/rand" "os" @@ -31,6 +31,8 @@ import ( "time" "unicode" + "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated" + "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/config" @@ -1268,10 +1270,20 @@ del: // delete box arg[1] txn ApplicationArgs 0 // [arg[0]] byte "delete" // [arg[0], "delete"] == // [arg[0]=?="delete"] - bz bad // "delete" ? continue : goto bad + bz set // "delete" ? continue : goto set txn ApplicationArgs 1 // [arg[1]] box_del // del boxes[arg[1]] b end +set: // put arg[1] at start of box arg[0] ... so actually a _partial_ "set" + txn ApplicationArgs 0 // [arg[0]] + byte "set" // [arg[0], "set"] + == // [arg[0]=?="set"] + bz bad // "delete" ? continue : goto bad + txn ApplicationArgs 1 // [arg[1]] + int 0 // [arg[1], 0] + txn ApplicationArgs 2 // [arg[1], 0, arg[2]] + box_replace // [] // boxes: arg[1] -> replace(boxes[arg[1]], 0, arg[2]) + b end bad: err end: @@ -1320,7 +1332,7 @@ end: var createdBoxCount uint64 = 0 // define operate box helper - operateBoxAndSendTxn := func(operation string, boxNames []string) { + operateBoxAndSendTxn := func(operation string, boxNames []string, boxValues []string) { txns := make([]transactions.Transaction, len(boxNames)) txIDs := make(map[string]string, len(boxNames)) @@ -1328,6 +1340,7 @@ end: appArgs := [][]byte{ []byte(operation), []byte(boxNames[i]), + []byte(boxValues[i]), } boxRef := transactions.BoxRef{ Name: []byte(boxNames[i]), @@ -1370,21 +1383,24 @@ end: // then submit transaction group containing all operations on box names // Then we check these boxes are appropriately created/deleted operateAndMatchRes := func(operation string, boxNames []string) { + boxValues := make([]string, len(boxNames)) if operation == "create" { - for _, box := range boxNames { + for i, box := range boxNames { keyValid, ok := createdBoxName[box] a.False(ok && keyValid) + boxValues[i] = "" } } else if operation == "delete" { - for _, box := range boxNames { + for i, box := range boxNames { keyValid, ok := createdBoxName[box] a.True(keyValid == ok) + boxValues[i] = "" } } else { a.True(false) } - operateBoxAndSendTxn(operation, boxNames) + operateBoxAndSendTxn(operation, boxNames, boxValues) if operation == "create" { for _, box := range boxNames { @@ -1474,4 +1490,30 @@ end: resp, err = testClient.ApplicationBoxes(uint64(createdAppID)) a.NoError(err) a.Empty(resp) + + // Get Box value from box name + encodeInt := func(n uint64) []byte { + ibytes := make([]byte, 8) + binary.BigEndian.PutUint64(ibytes, n) + return ibytes + } + + boxTests := [][]interface{}{ + {[]byte("foo"), "str:foo", []byte("bar12")}, + {encodeInt(12321), "int:12321", []byte{0, 1, 254, 3, 2}}, + {[]byte{0, 248, 255, 32}, "b64:APj/IA==", []byte("lux56")}, + } + for _, boxTest := range boxTests { + boxName := boxTest[0].([]byte) + encodedName := boxTest[1].(string) + // Box values are 5 bytes, as defined by the test TEAL program. + boxValue := boxTest[2].([]byte) + operateBoxAndSendTxn("create", []string{string(boxName)}, []string{""}) + operateBoxAndSendTxn("set", []string{string(boxName)}, []string{string(boxValue)}) + + boxResponse, err := testClient.GetApplicationBoxByName(uint64(createdAppID), encodedName) + a.NoError(err) + a.Equal(boxName, boxResponse.Name) + a.Equal(boxValue, boxResponse.Value) + } } |