Skip to content

Commit

Permalink
add read only call sc endpoint (#1309)
Browse files Browse the repository at this point in the history
* add read only call sc endpoint

* fix convert struct and add robot test

* refactor

* rename

* fix a response

* improve a doc comment

* improve a doc comment
  • Loading branch information
Thykof authored Jan 9, 2024
1 parent ce20d11 commit b2fb810
Show file tree
Hide file tree
Showing 7 changed files with 427 additions and 22 deletions.
206 changes: 204 additions & 2 deletions api/swagger/server/restapi/resource/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ paths:
fee:
$ref: "#/definitions/Amount"
description: Set the fee amount that will be given to the block creator.
default: 0 # DefaultFee
produces:
- application/json
responses:
Expand All @@ -89,6 +88,63 @@ paths:
description: Internal Server Error - The server has encountered a situation it does not know how to handle.
schema:
$ref: "#/definitions/Error"
/cmd/read-only/callsc:
post:
description: Read-only calls the given function from the given smart contract.
operationId: cmdReadOnlyCallSC
parameters:
- in: body
name: body
required: true
x-nullable: false
schema:
type: object
required:
- nickname
- name
- at
properties:
nickname:
description: Account nickname used to sign the operation.
type: string
x-nullable: false
name:
description: Function name to call.
type: string
x-nullable: false
at:
description: Smart contract address exporting the function to call.
type: string
x-nullable: false
args:
description: Arguments to pass to the function.
type: string
default: ""
coins:
$ref: "#/definitions/Amount"
description: Set the coin amount that will be transferred to the smartContract.
fee:
$ref: "#/definitions/Amount"
description: Set the fee amount that will be given to the block creator.
produces:
- application/json
responses:
"200":
description: OK.
schema:
$ref: "#/definitions/ReadOnlyResult"
"400":
description: Bad request.
schema:
$ref: "#/definitions/Error"
"422":
description: Unprocessable Entity - syntax is correct, but the server was unable to process the contained instructions.
schema:
$ref: "#/definitions/Error"
"500":
description: Internal Server Error - The server has encountered a situation it does not know how to handle.
schema:
$ref: "#/definitions/Error"
/cmd/deploySC:
post:
description: Deploys the given smart contract to the blockchain network.
Expand Down Expand Up @@ -729,6 +785,7 @@ definitions:
description: Amount in nanoMassa.
type: string
x-nullable: false
default: ""
Error:
type: object
description: Error object.
Expand Down Expand Up @@ -859,4 +916,149 @@ definitions:
x-nullable: false
updatable:
description: Whether the plugin can be updated.
type: boolean
type: boolean
ReadOnlyResult:
type: object
properties:
executed_at:
$ref: '#/definitions/Timestamp'
result:
$ref: '#/definitions/Result'
output_events:
type: array
items:
$ref: '#/definitions/Event'
gas_cost:
type: integer
state_changes:
$ref: '#/definitions/StateChange'
Timestamp:
type: object
properties:
period:
type: integer
thread:
type: integer
Result:
type: object
properties:
Ok:
type: array
items:
type: object
Error:
type: string
Event:
type: object
properties:
context:
$ref: '#/definitions/EventContext'
data:
type: string
EventContext:
type: object
properties:
slot:
$ref: '#/definitions/Timestamp'
block:
type: object
read_only:
type: boolean
index_in_slot:
type: integer
call_stack:
type: array
items:
type: string
origin_operation_id:
type: object
is_final:
type: boolean
is_error:
type: boolean
StateChange:
type: object
properties:
ledger_changes:
type: object
additionalProperties:
$ref: '#/definitions/LedgerEntryChange'
async_pool_changes:
type: array
items:
type: object
pos_changes:
$ref: '#/definitions/PosChanges'
executed_ops_changes:
type: object
additionalProperties:
type: object
executed_denunciations_changes:
type: array
items:
type: object
execution_trail_hash_change:
type: object
LedgerEntryChange:
type: object
properties:
Update:
$ref: '#/definitions/LedgerUpdate'
LedgerUpdate:
type: object
properties:
balance:
$ref: '#/definitions/ChangeSet'
bytecode:
type: string
datastore:
type: array
items:
type: object
ChangeSet:
type: object
properties:
Set:
type: object
PosChanges:
type: object
properties:
seed_bits:
$ref: '#/definitions/SeedBitsInfo'
roll_changes:
type: object
additionalProperties:
type: object
production_stats:
type: object
additionalProperties:
type: object
deferred_credits:
$ref: '#/definitions/DeferredCreditsInfo'
SeedBitsInfo:
type: object
properties:
order:
type: string
head:
$ref: '#/definitions/BitVecHeadInfo'
bits:
type: integer
data:
type: array
items:
type: object
BitVecHeadInfo:
type: object
properties:
width:
type: integer
index:
type: integer
DeferredCreditsInfo:
type: object
properties:
credits:
type: object
additionalProperties:
type: object
19 changes: 18 additions & 1 deletion api/test/robot_tests/cmd/cmd.robot
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ POST a Smart Contract
${sc_address}= Get SC address ${response.json()['firstEvent']['data']}
Set Global Variable ${DEPLOYED_SC_ADDR} ${sc_address}

POST /cmd/read-only/callsc
${randomID}= Generate Random String 10
${argument}= keywords.String To Arg ${randomID}
${data}= Create Dictionary
... nickname=${WALLET_NICKNAME}
... name=event
... at=${DEPLOYED_SC_ADDR}
... args=${argument}
${response}= POST
... ${API_URL}/cmd/read-only/callsc
... json=${data}
... expected_status=any
Log To Console json response: ${response.json()}
Should Be Equal As Integers ${response.status_code} ${STATUS_OK} # Assert the status code is 200 OK
Should Contain string(${response.json()}) 'read_only': True, 'slot': {'period':
Should Contain string(${response.json()}) I'm an event! My id is ${randomID}

POST /cmd/executeFunction sync
${randomID}= Generate Random String 10
${argument}= keywords.String To Arg ${randomID}
Expand All @@ -40,7 +57,7 @@ POST /cmd/executeFunction sync
... ${API_URL}/cmd/executeFunction
... json=${data}
... expected_status=any
Log To Console ${response.json()}
Log To Console json response: ${response.json()}
Should Be Equal As Integers ${response.status_code} ${STATUS_OK} # Assert the status code is 200 OK
Should Be Equal ${response.json()['firstEvent']['data']} I'm an event! My id is ${randomID}

Expand Down
11 changes: 6 additions & 5 deletions int/api/cmd/errors.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package cmd

const (
errorSendOperation = "Execute-0001"
errorInvalidArgs = "Execute-0002"
errorInvalidFee = "Execute-0003"
errorInvalidMaxGas = "Execute-0004"
errorInvalidCoin = "Execute-0005"
errorSendOperation = "Execute-0001"
errorInvalidArgs = "Execute-0002"
errorInvalidFee = "Execute-0003"
errorInvalidMaxGas = "Execute-0004"
errorInvalidCoin = "Execute-0005"
errorInvalidNickname = "Execute-0006"
)
17 changes: 3 additions & 14 deletions int/api/cmd/execute_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,9 @@ type executeFunction struct {

//nolint:funlen
func (e *executeFunction) Handle(params operations.CmdExecuteFunctionParams) middleware.Responder {
// convert fee to uint64
fee := uint64(sendOperation.DefaultFee)

if string(params.Body.Fee) != "" {
parsedFee, err := strconv.ParseUint(string(params.Body.Fee), 10, 64)
if err != nil {
return operations.NewCmdExecuteFunctionBadRequest().WithPayload(
&models.Error{
Code: errorInvalidFee,
Message: "Error during amount conversion: " + err.Error(),
})
}

fee = parsedFee
fee, errResponse := amountToUint64(params.Body.Fee, uint64(sendOperation.DefaultFee))
if errResponse != nil {
return errResponse
}

maxGas := uint64(0)
Expand Down
69 changes: 69 additions & 0 deletions int/api/cmd/read_only_call_sc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cmd

import (
"encoding/base64"

"github.com/go-openapi/runtime/middleware"
"github.com/massalabs/station/api/swagger/server/models"
"github.com/massalabs/station/api/swagger/server/restapi/operations"
"github.com/massalabs/station/int/config"
"github.com/massalabs/station/pkg/node"
sendOperation "github.com/massalabs/station/pkg/node/sendoperation"
"github.com/massalabs/station/pkg/wallet"
)

func NewReadOnlyCallSCHandler(config *config.NetworkInfos) operations.CmdReadOnlyCallSCHandler {
return &ReadOnlyCallSC{networkInfos: config}
}

type ReadOnlyCallSC struct {
networkInfos *config.NetworkInfos
}

func (e *ReadOnlyCallSC) Handle(params operations.CmdReadOnlyCallSCParams) middleware.Responder {
args, err := base64.StdEncoding.DecodeString(params.Body.Args)
if err != nil {
return operations.NewCmdReadOnlyCallSCUnprocessableEntity().
WithPayload(
&models.Error{
Code: errorInvalidArgs,
Message: err.Error(),
})
}

acc, err := wallet.Fetch(params.Body.Nickname)
if err != nil {
return operations.NewCmdReadOnlyCallSCBadRequest().WithPayload(
&models.Error{
Code: errorInvalidNickname,
Message: "Error during wallet fetch: " + err.Error(),
})
}

if params.Body.Fee == "" {
params.Body.Fee = "0"
}

coins, errResponse := amountToString(params.Body.Coins, uint64(0))
if errResponse != nil {
return errResponse
}

result, err := sendOperation.ReadOnlyCallSC(
params.Body.At,
params.Body.Name,
args,
coins,
string(params.Body.Fee),
acc.Address,
node.NewClient(e.networkInfos.NodeURL),
)
if err != nil {
return operations.NewCmdReadOnlyCallSCInternalServerError().WithPayload(
&models.Error{Code: errorSendOperation, Message: "Error: read only callSC failed: " + err.Error()})
}

modelResult := CreateReadOnlyResult(*result)

return operations.NewCmdReadOnlyCallSCOK().WithPayload(&modelResult)
}
Loading

0 comments on commit b2fb810

Please sign in to comment.