From 358669f941b1345a1fbce08bc7d4d2ffdaae787e Mon Sep 17 00:00:00 2001 From: Nathan Seva Date: Thu, 16 Nov 2023 18:31:50 -0500 Subject: [PATCH] read only execute sc --- pkg/node/sendoperation/interfaces.go | 11 ++++ pkg/node/sendoperation/readonlycall.go | 85 +++++++++++++++++++++++++- pkg/onchain/sc.go | 18 +++++- 3 files changed, 109 insertions(+), 5 deletions(-) diff --git a/pkg/node/sendoperation/interfaces.go b/pkg/node/sendoperation/interfaces.go index 136e91f42..386178d0e 100644 --- a/pkg/node/sendoperation/interfaces.go +++ b/pkg/node/sendoperation/interfaces.go @@ -5,6 +5,7 @@ import ( "fmt" ) +// ReadOnlyCallParams is the struct used to send a read only callSC to the node. type ReadOnlyCallParams struct { MaxGas int `json:"max_gas"` Coins int `json:"coins"` @@ -139,3 +140,13 @@ type BitVecHeadInfo struct { type DeferredCreditsInfo struct { Credits map[string]interface{} `json:"credits"` // Empty in provided JSON, assumed to be a map } + +// ReadOnlyExecuteParams is the struct used to send a read only executeSC to the node. +type ReadOnlyExecuteParams struct { + MaxGas int `json:"max_gas"` + Coins int `json:"coins"` + Fee int `json:"fee"` + Address string `json:"address"` + Bytecode JSONableSlice `json:"bytecode"` + OperationDatastore JSONableSlice `json:"operation_datastore"` +} diff --git a/pkg/node/sendoperation/readonlycall.go b/pkg/node/sendoperation/readonlycall.go index 9270c43bd..796ebcf02 100644 --- a/pkg/node/sendoperation/readonlycall.go +++ b/pkg/node/sendoperation/readonlycall.go @@ -23,7 +23,7 @@ func EstimateGasCostCallSC( return 0, fmt.Errorf("loading wallet '%s': %w", nickname, err) } - result, err := ReadOnlyCallSC(targetAddr, function, parameter, coins, fee, acc.Address, client) + result, err := ReadOnlyCallSC(targetAddr, function, parameter, coins, DefaultGasLimit, fee, acc.Address, client) if err != nil { return 0, fmt.Errorf("calling ReadOnlyCall: %w", err) } @@ -42,6 +42,7 @@ func ReadOnlyCallSC( function string, parameter []byte, coins uint64, + maxGas uint64, fee uint64, callerAddr string, client *node.Client, @@ -49,7 +50,7 @@ func ReadOnlyCallSC( readOnlyCallParams := [][]ReadOnlyCallParams{ { ReadOnlyCallParams{ - MaxGas: DefaultGasLimit, + MaxGas: int(maxGas), Coins: int(coins), Fee: int(fee), TargetAddress: targetAddr, @@ -91,3 +92,83 @@ func ReadOnlyCallSC( return &resp[0], nil } + +func EstimateGasCostExecuteSC( + nickname string, + contract []byte, + datastore []byte, + maxCoins uint64, + fee uint64, + client *node.Client, +) (uint64, error) { + acc, err := wallet.Fetch(nickname) + if err != nil { + return 0, fmt.Errorf("loading wallet '%s': %w", nickname, err) + } + + result, err := ReadOnlyExecuteSC(contract, datastore, maxCoins, DefaultGasLimit, fee, acc.Address, client) + if err != nil { + logger.Warnf("ReadOnlyExecuteSC error: %s, caller address is %s", err, acc.Address) + + // Do not return an error because execute_read_only_bytecode v24.1 fail with Internal error + // and on v27 fails with Invalid params + return DefaultGasLimit, nil + } + + estimatedGasCost := uint64(result.GasCost) + + return estimatedGasCost, nil +} + +func ReadOnlyExecuteSC( + contract []byte, + datastore []byte, + maxCoins uint64, + maxGas uint64, + fee uint64, + callerAddr string, + client *node.Client, +) (*ReadOnlyCallResult, error) { + readOnlyExecuteParams := [][]ReadOnlyExecuteParams{ + { + ReadOnlyExecuteParams{ + Coins: int(maxCoins), + MaxGas: int(maxGas), + Fee: int(fee), + Address: callerAddr, + Bytecode: contract, + OperationDatastore: datastore, + }, + }, + } + + rawResponse, err := client.RPCClient.Call( + context.Background(), + "execute_read_only_bytecode", + readOnlyExecuteParams, + ) + if err != nil { + return nil, fmt.Errorf("calling execute_read_only_bytecode jsonrpc with '%+v': %w", readOnlyExecuteParams, err) + } + + if rawResponse.Error != nil { + return nil, fmt.Errorf( + "receiving execute_read_only_bytecode with '%+v': response: %w", + readOnlyExecuteParams, + rawResponse.Error, + ) + } + + var resp []ReadOnlyCallResult // rename if it's the same for call sc and execute sc: ReadOnlyResult + + err = rawResponse.GetObject(&resp) + if err != nil { + return nil, fmt.Errorf("parsing execute_read_only_bytecode jsonrpc response '%+v': %w", rawResponse, err) + } + + if resp[0].Result.Error != "" { + return nil, fmt.Errorf("ReadOnlyExecuteSC error: %s, caller address is %s", resp[0].Result.Error, callerAddr) + } + + return &resp[0], nil +} diff --git a/pkg/onchain/sc.go b/pkg/onchain/sc.go index bc07b1295..5811a8af0 100644 --- a/pkg/onchain/sc.go +++ b/pkg/onchain/sc.go @@ -39,7 +39,7 @@ func CallFunction( if maxGas == sendOperation.DefaultGasLimit || maxGas == 0 { estimatedGasCost, err := sendOperation.EstimateGasCostCallSC(nickname, addr, function, parameter, coins, fee, client) if err != nil { - return nil, fmt.Errorf("calling EstimateGasCost for function '%s' at '%s': %w", function, addr, err) + return nil, fmt.Errorf("estimating Call SC gas cost for function '%s' at '%s': %w", function, addr, err) } logger.Debugf("Estimated gas cost for %s at %s: %d", function, addr, estimatedGasCost) @@ -107,7 +107,7 @@ func CallFunctionSuccess( // The smart contract is deployed with the given account nickname. func DeploySC(client *node.Client, nickname string, - gasLimit uint64, + maxGas uint64, maxCoins uint64, fee uint64, expiry uint64, @@ -116,9 +116,21 @@ func DeploySC(client *node.Client, operationBatch sendOperation.OperationBatch, signer signer.Signer, ) (*sendOperation.OperationResponse, []node.Event, error) { + // Calibrate max_gas + if maxGas == sendOperation.DefaultGasLimit || maxGas == 0 { + estimatedGasCost, err := sendOperation.EstimateGasCostExecuteSC(nickname, contract, datastore, maxCoins, fee, client) + if err != nil { + return nil, nil, fmt.Errorf("estimating Execute SC gas cost: %w", err) + } + + logger.Debugf("Estimated gas cost for execute SC: %d", estimatedGasCost) + + maxGas = estimatedGasCost + } + exeSC := executesc.New( contract, - gasLimit, + maxGas, maxCoins, datastore)