Skip to content

Commit b5c679d

Browse files
committed
add executeSC endpoint
1 parent 447cb8d commit b5c679d

File tree

6 files changed

+286
-12
lines changed

6 files changed

+286
-12
lines changed

api/swagger/server/restapi/resource/swagger.yml

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ paths:
247247
default: ""
248248
maxGas:
249249
$ref: "#/definitions/Amount"
250-
description: Maximum number of gas unit that a operation will be able consume.
250+
description: Maximum number of gas unit the operation will be able to consume.
251251
produces:
252252
- application/json
253253
responses:
@@ -274,6 +274,72 @@ paths:
274274
description: Internal Server Error - The server has encountered a situation it does not know how to handle.
275275
schema:
276276
$ref: "#/definitions/Error"
277+
278+
/cmd/executeSC:
279+
post:
280+
description: Execute a smart contract bytecode.
281+
operationId: cmdExecuteSC
282+
parameters:
283+
- in: body
284+
name: body
285+
required: true
286+
x-nullable: false
287+
schema:
288+
type: object
289+
required:
290+
- nickname
291+
- bytecode
292+
properties:
293+
nickname:
294+
description: Account nickname used to sign the operation.
295+
type: string
296+
x-nullable: false
297+
bytecode:
298+
description: Base64 encoded smart contract bytecode.
299+
type: string
300+
x-nullable: false
301+
maxCoins:
302+
$ref: "#/definitions/Amount"
303+
description: Max spendable coins by the execution.
304+
default: "0"
305+
fee:
306+
$ref: "#/definitions/Amount"
307+
description: Set the fee amount (in nanoMassa) that will be given to the block creator.
308+
default: "0"
309+
description:
310+
description: "Description of the operation"
311+
type: string
312+
default: ""
313+
maxGas:
314+
$ref: "#/definitions/Amount"
315+
description: Maximum number of gas unit the operation will be able to consume.
316+
datastore:
317+
$ref: "#/definitions/Datastore"
318+
319+
produces:
320+
- application/json
321+
responses:
322+
"200":
323+
description: OK.
324+
schema:
325+
type: object
326+
properties:
327+
operationId:
328+
description: Operation id.
329+
type: string
330+
"400":
331+
description: Bad request.
332+
schema:
333+
$ref: "#/definitions/Error"
334+
"422":
335+
description: Unprocessable Entity - syntax is correct, but the server was unable to process the contained instructions.
336+
schema:
337+
$ref: "#/definitions/Error"
338+
"500":
339+
description: Internal Server Error - The server has encountered a situation it does not know how to handle.
340+
schema:
341+
$ref: "#/definitions/Error"
342+
277343
/kpi:
278344
get:
279345
operationId: kpi
@@ -824,7 +890,7 @@ definitions:
824890
type: string
825891
x-nullable: false
826892
Amount:
827-
description: Amount in nanoMassa.
893+
description: Amount in integer string.
828894
type: string
829895
x-nullable: false
830896
default: ""
@@ -1104,3 +1170,33 @@ definitions:
11041170
type: object
11051171
additionalProperties:
11061172
type: object
1173+
Base64Bytes:
1174+
title: "Base64Bytes"
1175+
description: "Bytes in base64 string"
1176+
type: string
1177+
1178+
DataStoreEntry:
1179+
title: "Datastore entry"
1180+
description: "A tuple which contains (key, value) in b64 string"
1181+
type: array
1182+
maxItems: 2
1183+
items:
1184+
$ref: "#/definitions/Base64Bytes"
1185+
example: [
1186+
[
1187+
abcdef
1188+
],
1189+
[
1190+
ghjklm
1191+
]
1192+
]
1193+
1194+
Datastore:
1195+
title: "Datastore"
1196+
description: "Contract datastore"
1197+
type: array
1198+
items:
1199+
$ref: "#/definitions/DataStoreEntry"
1200+
1201+
1202+

int/api/cmd/errors.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package cmd
22

33
const (
4-
errorSendOperation = "Execute-0001"
5-
errorInvalidArgs = "Execute-0002"
6-
errorInvalidFee = "Execute-0003"
7-
errorInvalidMaxGas = "Execute-0004"
8-
errorInvalidCoin = "Execute-0005"
9-
errorInvalidNickname = "Execute-0006"
10-
errorInvalidMaxCoins = "Execute-0007"
4+
errorSendOperation = "Execute-0001"
5+
errorInvalidArgs = "Execute-0002"
6+
errorInvalidFee = "Execute-0003"
7+
errorInvalidMaxGas = "Execute-0004"
8+
errorInvalidCoin = "Execute-0005"
9+
errorInvalidNickname = "Execute-0006"
10+
errorInvalidMaxCoins = "Execute-0007"
11+
errorInvalidDatastore = "Execute-0008"
1112
)

int/api/cmd/executeSC.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package cmd
2+
3+
import (
4+
"encoding/base64"
5+
"strconv"
6+
7+
"github.com/go-openapi/runtime/middleware"
8+
"github.com/massalabs/station/api/swagger/server/models"
9+
"github.com/massalabs/station/api/swagger/server/restapi/operations"
10+
"github.com/massalabs/station/int/config"
11+
"github.com/massalabs/station/pkg/node/sendoperation"
12+
"github.com/massalabs/station/pkg/node/sendoperation/signer"
13+
"github.com/massalabs/station/pkg/onchain"
14+
)
15+
16+
func NewExecuteSCHandler(config *config.NetworkInfos) operations.CmdExecuteSCHandler {
17+
return &executeSC{networkInfos: config}
18+
}
19+
20+
type executeSC struct {
21+
networkInfos *config.NetworkInfos
22+
}
23+
24+
//nolint:funlen
25+
func (d *executeSC) Handle(params operations.CmdExecuteSCParams) middleware.Responder {
26+
if params.Body.Bytecode == "" {
27+
return operations.NewCmdExecuteSCUnprocessableEntity().
28+
WithPayload(
29+
&models.Error{
30+
Message: "Smart contract bytecode is required",
31+
})
32+
}
33+
34+
smartContractByteCode, err := base64.StdEncoding.DecodeString(params.Body.Bytecode)
35+
if err != nil {
36+
return operations.NewCmdExecuteSCBadRequest().
37+
WithPayload(
38+
&models.Error{
39+
Code: err.Error(),
40+
Message: err.Error(),
41+
})
42+
}
43+
44+
maxCoins, err := strconv.ParseUint(string(params.Body.MaxCoins), 10, 64)
45+
if err != nil {
46+
return operations.NewCmdExecuteSCBadRequest().
47+
WithPayload(
48+
&models.Error{
49+
Code: errorInvalidMaxCoins,
50+
Message: err.Error(),
51+
})
52+
}
53+
54+
fee, err := strconv.ParseUint(string(params.Body.Fee), 10, 64)
55+
if err != nil {
56+
return operations.NewCmdExecuteSCBadRequest().
57+
WithPayload(
58+
&models.Error{
59+
Code: errorInvalidFee,
60+
Message: err.Error(),
61+
})
62+
}
63+
64+
maxGas := uint64(sendoperation.MaxGasAllowedExecuteSC)
65+
66+
if string(params.Body.MaxGas) != "" {
67+
parsedMaxGas, err := strconv.ParseUint(string(params.Body.MaxGas), 10, 64)
68+
if err != nil {
69+
return operations.NewCmdExecuteSCBadRequest().WithPayload(
70+
&models.Error{
71+
Code: errorInvalidMaxGas,
72+
Message: "Error during max gas conversion: " + err.Error(),
73+
})
74+
}
75+
76+
maxGas = parsedMaxGas
77+
}
78+
79+
var datastore []onchain.DatastoreEntry = []onchain.DatastoreEntry{}
80+
81+
if len(params.Body.Datastore) > 0 {
82+
datastore = make([]onchain.DatastoreEntry, len(params.Body.Datastore))
83+
for i, entry := range params.Body.Datastore {
84+
key, err := base64.StdEncoding.DecodeString(string(entry[0]))
85+
if err != nil {
86+
return operations.NewCmdExecuteSCBadRequest().
87+
WithPayload(
88+
&models.Error{
89+
Code: errorInvalidDatastore,
90+
Message: err.Error(),
91+
})
92+
}
93+
value, err := base64.StdEncoding.DecodeString(string(entry[1]))
94+
if err != nil {
95+
return operations.NewCmdExecuteSCBadRequest().
96+
WithPayload(
97+
&models.Error{
98+
Code: errorInvalidDatastore,
99+
Message: err.Error(),
100+
})
101+
}
102+
datastore[i] = onchain.DatastoreEntry{
103+
Key: key,
104+
Value: value,
105+
}
106+
}
107+
}
108+
109+
operationResponse, err := onchain.ExecuteSC(
110+
d.networkInfos,
111+
params.Body.Nickname,
112+
maxGas,
113+
maxCoins,
114+
fee,
115+
sendoperation.DefaultExpiryInSlot,
116+
smartContractByteCode,
117+
datastore,
118+
&signer.WalletPlugin{},
119+
"Executing contract bytecode: "+params.Body.Description,
120+
)
121+
if err != nil {
122+
return operations.NewCmdExecuteSCInternalServerError().
123+
WithPayload(
124+
&models.Error{
125+
Code: err.Error(),
126+
Message: err.Error(),
127+
})
128+
}
129+
130+
return operations.NewCmdExecuteSCOK().
131+
WithPayload(&operations.CmdExecuteSCOKBody{
132+
OperationID: operationResponse.OperationID,
133+
})
134+
}

int/api/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func initLocalAPI(
3737

3838
localAPI.CmdExecuteFunctionHandler = cmd.NewExecuteFunctionHandler(config)
3939
localAPI.CmdReadOnlyCallSCHandler = cmd.NewReadOnlyCallSCHandler(config)
40+
localAPI.CmdExecuteSCHandler = cmd.NewExecuteSCHandler(config)
4041

4142
localAPI.MassaGetAddressesHandler = massa.NewGetAddressHandler(config)
4243
localAPI.GetNodeHandler = massa.NewGetNodeHandler(config)

pkg/node/sendoperation/sendoperation.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ type Operation interface {
3838
}
3939

4040
type OperationResponse struct {
41-
OperationID string
42-
CorrelationID string
41+
OperationID string
4342
}
4443

4544
type OperationContent struct {
@@ -106,7 +105,7 @@ func Call(
106105
return nil, err
107106
}
108107

109-
return &OperationResponse{CorrelationID: res.CorrelationID, OperationID: resp[0]}, nil
108+
return &OperationResponse{OperationID: resp[0]}, nil
110109
}
111110

112111
func createOperationContent(description string, msgB64 string, chainID uint64) (string, error) {

pkg/onchain/sc.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,46 @@ func FindDeployedAddress(events []node.Event) (string, bool) {
174174

175175
return "", false
176176
}
177+
178+
func ExecuteSC(
179+
networkInfos *config.NetworkInfos,
180+
nickname string,
181+
maxGas uint64,
182+
maxCoins uint64,
183+
fees uint64,
184+
expiry uint64,
185+
bytecode []byte,
186+
datastore []DatastoreEntry,
187+
signer signer.Signer,
188+
description string,
189+
) (*sendOperation.OperationResponse, error) {
190+
client := node.NewClient(networkInfos.NodeURL)
191+
192+
serializedDatastore, err := SerializeDatastore(datastore)
193+
if err != nil {
194+
return nil, err
195+
}
196+
197+
exeSCOperation := executesc.New(
198+
bytecode,
199+
maxGas,
200+
maxCoins,
201+
serializedDatastore,
202+
)
203+
204+
operationResponse, err := sendOperation.Call(
205+
client,
206+
networkInfos.ChainID,
207+
expiry,
208+
fees,
209+
exeSCOperation,
210+
nickname,
211+
signer,
212+
description,
213+
)
214+
if err != nil {
215+
return nil, fmt.Errorf("calling executeSC: %w", err)
216+
}
217+
218+
return operationResponse, nil
219+
}

0 commit comments

Comments
 (0)