From 02fba812a4b8060d7777835eec71513b067215a2 Mon Sep 17 00:00:00 2001 From: Artem Poltorzhitskiy Date: Wed, 15 Dec 2021 16:13:26 +0300 Subject: [PATCH] Feature: API endpoint for getting opg storage diff (#788) --- cmd/api/docs/docs.go | 133 +++++++++++++++++++++++++++++++++ cmd/api/docs/swagger.json | 133 +++++++++++++++++++++++++++++++++ cmd/api/docs/swagger.yaml | 90 ++++++++++++++++++++++ cmd/api/handlers/operations.go | 61 ++++++++++++++- cmd/api/handlers/requests.go | 6 ++ cmd/api/main.go | 1 + 6 files changed, 423 insertions(+), 1 deletion(-) diff --git a/cmd/api/docs/docs.go b/cmd/api/docs/docs.go index a6fe28af3..a3eba714f 100644 --- a/cmd/api/docs/docs.go +++ b/cmd/api/docs/docs.go @@ -2475,6 +2475,63 @@ var doc = `{ } } }, + "/v1/global_constants/{network}/{address}": { + "get": { + "description": "Get global constant", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "contract" + ], + "summary": "Get global constant", + "operationId": "get-global-constant", + "parameters": [ + { + "type": "string", + "description": "network", + "name": "network", + "in": "path", + "required": true + }, + { + "maxLength": 54, + "minLength": 54, + "type": "string", + "description": "expr address of constant", + "name": "address", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/handlers.GlobalConstant" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + } + } + } + }, "/v1/head": { "get": { "description": "Get indexer head for each network", @@ -2508,6 +2565,59 @@ var doc = `{ } } }, + "/v1/operation/{hash}/{index}/storage_diff": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "operations" + ], + "summary": "Get code line where operation failed", + "operationId": "get-operation-storage-diff", + "parameters": [ + { + "maxLength": 51, + "minLength": 51, + "type": "string", + "description": "Operation group hash", + "name": "hash", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Content index", + "name": "index", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handlers.GetErrorLocationResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + } + } + } + }, "/v1/operation/{id}/error_location": { "get": { "consumes": [ @@ -4084,6 +4194,29 @@ var doc = `{ } } }, + "handlers.GlobalConstant": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "level": { + "type": "integer" + }, + "network": { + "type": "integer" + }, + "timestamp": { + "type": "string" + }, + "value": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "handlers.HeadResponse": { "type": "object", "properties": { diff --git a/cmd/api/docs/swagger.json b/cmd/api/docs/swagger.json index f360be6b8..327df4d42 100644 --- a/cmd/api/docs/swagger.json +++ b/cmd/api/docs/swagger.json @@ -2457,6 +2457,63 @@ } } }, + "/v1/global_constants/{network}/{address}": { + "get": { + "description": "Get global constant", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "contract" + ], + "summary": "Get global constant", + "operationId": "get-global-constant", + "parameters": [ + { + "type": "string", + "description": "network", + "name": "network", + "in": "path", + "required": true + }, + { + "maxLength": 54, + "minLength": 54, + "type": "string", + "description": "expr address of constant", + "name": "address", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/handlers.GlobalConstant" + } + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + } + } + } + }, "/v1/head": { "get": { "description": "Get indexer head for each network", @@ -2490,6 +2547,59 @@ } } }, + "/v1/operation/{hash}/{index}/storage_diff": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "operations" + ], + "summary": "Get code line where operation failed", + "operationId": "get-operation-storage-diff", + "parameters": [ + { + "maxLength": 51, + "minLength": 51, + "type": "string", + "description": "Operation group hash", + "name": "hash", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Content index", + "name": "index", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handlers.GetErrorLocationResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handlers.Error" + } + } + } + } + }, "/v1/operation/{id}/error_location": { "get": { "consumes": [ @@ -4066,6 +4176,29 @@ } } }, + "handlers.GlobalConstant": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "level": { + "type": "integer" + }, + "network": { + "type": "integer" + }, + "timestamp": { + "type": "string" + }, + "value": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "handlers.HeadResponse": { "type": "object", "properties": { diff --git a/cmd/api/docs/swagger.yaml b/cmd/api/docs/swagger.yaml index 3cdd81db5..cbf9157ab 100644 --- a/cmd/api/docs/swagger.yaml +++ b/cmd/api/docs/swagger.yaml @@ -429,6 +429,21 @@ definitions: text: type: string type: object + handlers.GlobalConstant: + properties: + address: + type: string + level: + type: integer + network: + type: integer + timestamp: + type: string + value: + items: + type: integer + type: array + type: object handlers.HeadResponse: properties: contract_calls: @@ -3131,6 +3146,45 @@ paths: summary: Resolve domain tags: - domains + /v1/global_constants/{network}/{address}: + get: + consumes: + - application/json + description: Get global constant + operationId: get-global-constant + parameters: + - description: network + in: path + name: network + required: true + type: string + - description: expr address of constant + in: path + maxLength: 54 + minLength: 54 + name: address + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/handlers.GlobalConstant' + type: array + "404": + description: Not Found + schema: + $ref: '#/definitions/handlers.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.Error' + summary: Get global constant + tags: + - contract /v1/head: get: consumes: @@ -3153,6 +3207,42 @@ paths: summary: Show indexer head tags: - head + /v1/operation/{hash}/{index}/storage_diff: + get: + consumes: + - application/json + operationId: get-operation-storage-diff + parameters: + - description: Operation group hash + in: path + maxLength: 51 + minLength: 51 + name: hash + required: true + type: string + - description: Content index + in: path + name: index + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handlers.GetErrorLocationResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/handlers.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/handlers.Error' + summary: Get code line where operation failed + tags: + - operations /v1/operation/{id}/error_location: get: consumes: diff --git a/cmd/api/handlers/operations.go b/cmd/api/handlers/operations.go index b4b947df8..be9e32f91 100644 --- a/cmd/api/handlers/operations.go +++ b/cmd/api/handlers/operations.go @@ -134,9 +134,68 @@ func (ctx *Context) GetOperation(c *gin.Context) { c.SecureJSON(http.StatusOK, resp) } +// GetContentDiff godoc +// @Summary Get storage diff for content in OPG +// @Description Get storage diff for content in OPG +// @Tags operations +// @ID get-operation-storage-diff +// @Param hash path string true "Operation group hash" minlength(51) maxlength(51) +// @Param index path integer true "Content index" mininum(0) +// @Accept json +// @Produce json +// @Success 200 {object} map[string]interface{} +// @Failure 400 {object} Error +// @Failure 500 {object} Error +// @Router /v1/operation/{hash}/{index}/storage_diff [get] +func (ctx *Context) GetContentDiff(c *gin.Context) { + var req getContent + if err := c.BindUri(&req); ctx.handleError(c, err, http.StatusBadRequest) { + return + } + + operations, err := ctx.Operations.Get(map[string]interface{}{ + "hash": req.Hash, + "content_index": req.ContentIndex, + }, 1, false) + if ctx.handleError(c, err, 0) { + return + } + if len(operations) == 0 { + ctx.handleError(c, errors.Errorf("unknown operation content: %s %d", req.Hash, req.ContentIndex), http.StatusNotFound) + return + } + operation := operations[0] + + data, err := ctx.BigMapDiffs.GetForOperations(operation.ID) + if ctx.handleError(c, err, 0) { + return + } + + if len(operation.DeffatedStorage) > 0 && (operation.IsCall() || operation.IsOrigination()) && operation.IsApplied() { + protocol, err := ctx.Protocols.GetByID(operation.ProtocolID) + if ctx.handleError(c, err, 0) { + return + } + + script, err := ctx.getScript(operation.Network, operation.Destination, protocol.SymLink) + if ctx.handleError(c, err, 0) { + return + } + var op Operation + op.FromModel(operation) + + if err := ctx.setStorageDiff(operation.Destination, operation.DeffatedStorage, &op, data, script); ctx.handleError(c, err, 0) { + return + } + c.SecureJSON(http.StatusOK, op.StorageDiff) + return + } + c.SecureJSON(http.StatusOK, nil) +} + // GetOperationErrorLocation godoc // @Summary Get code line where operation failed -// @DescriptionGet code line where operation failed +// @Description Get code line where operation failed // @Tags operations // @ID get-operation-error-location // @Param id path integer true "Internal BCD operation ID" diff --git a/cmd/api/handlers/requests.go b/cmd/api/handlers/requests.go index 83279eadb..91057621a 100644 --- a/cmd/api/handlers/requests.go +++ b/cmd/api/handlers/requests.go @@ -171,6 +171,12 @@ type getOperationByIDRequest struct { ID int64 `uri:"id" binding:"required"` } +type getContent struct { + OPGRequest + + ContentIndex int64 `uri:"content" example:"1"` +} + type runOperationRequest struct { Data map[string]interface{} `json:"data" binding:"required"` Name string `json:"name" binding:"required"` diff --git a/cmd/api/main.go b/cmd/api/main.go index 19c662858..f1dd736fa 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -89,6 +89,7 @@ func (api *app) makeRouter() { v1.GET("head", cache.CachePage(store, time.Second*10, api.Context.GetHead)) v1.GET("opg/:hash", api.Context.GetOperation) + v1.GET("opg/:hash/:content/storage_diff", api.Context.GetContentDiff) v1.GET("operation/:id/error_location", api.Context.GetOperationErrorLocation) v1.GET("pick_random", api.Context.GetRandomContract) v1.GET("search", api.Context.Search)