Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add simulateTransaction endpoint #1610

Merged
merged 29 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
45749b1
initial version
tsachiherman Sep 30, 2024
23c8b1d
Merge branch 'main' into tsachi/simulate_transaction
tsachiherman Sep 30, 2024
65df47b
update
tsachiherman Sep 30, 2024
6fc64c9
linter
tsachiherman Sep 30, 2024
693b7c0
update
tsachiherman Oct 1, 2024
0da1d84
rollback unwanted changes
tsachiherman Oct 1, 2024
1a2e340
correct typo
tsachiherman Oct 1, 2024
8253c81
Merge branch 'main' into tsachi/simulate_transaction
tsachiherman Oct 1, 2024
7d26831
add encoding to state keys.
tsachiherman Oct 1, 2024
75c07e6
update
tsachiherman Oct 2, 2024
a306b46
Merge branch 'main' into tsachi/simulate_transaction
tsachiherman Oct 2, 2024
5379398
remove comments
tsachiherman Oct 2, 2024
ff86ecf
Merge branch 'tsachi/simulate_transaction' of github.com:ava-labs/hyp…
tsachiherman Oct 2, 2024
f42b284
Merge branch 'main' into tsachi/simulate_transaction
tsachiherman Oct 3, 2024
c08b8cf
add keys marshaler
tsachiherman Oct 3, 2024
793a6b0
step
tsachiherman Oct 3, 2024
7f3868e
Merge branch 'main' into tsachi/simulate_transaction
tsachiherman Oct 4, 2024
5399ff3
update recorder.
tsachiherman Oct 7, 2024
c172c59
update
tsachiherman Oct 7, 2024
89a134d
update
tsachiherman Oct 8, 2024
f5d4dd8
update
tsachiherman Oct 8, 2024
e3855fe
linter
tsachiherman Oct 8, 2024
9c74334
update per CR
tsachiherman Oct 9, 2024
267e13b
lint
tsachiherman Oct 9, 2024
b6bd4f2
Update state/recorder.go
tsachiherman Oct 9, 2024
e51d8fd
Update state/recorder.go
tsachiherman Oct 9, 2024
689d0ec
Update state/recorder.go
tsachiherman Oct 9, 2024
8f020d8
fix PR
tsachiherman Oct 9, 2024
65c9c42
Merge branch 'main' into tsachi/simulate_transaction
tsachiherman Oct 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions api/jsonrpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,30 @@ func Wait(ctx context.Context, interval time.Duration, check func(ctx context.Co
}
return ctx.Err()
}

func (cli *JSONRPCClient) SimulateActions(ctx context.Context, actions chain.Actions, actor codec.Address) ([]SimulateActionResult, error) {
args := &SimulatActionsArgs{
Actor: actor,
}

for _, action := range actions {
marshaledAction, err := chain.MarshalTyped(action)
if err != nil {
return nil, err
}
args.Actions = append(args.Actions, marshaledAction)
}

resp := new(SimulateActionsReply)
err := cli.requester.SendRequest(
ctx,
"simulateActions",
args,
resp,
)
if err != nil {
return nil, err
}

return resp.ActionResults, nil
}
75 changes: 74 additions & 1 deletion api/jsonrpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/ava-labs/hypersdk/codec"
"github.com/ava-labs/hypersdk/consts"
"github.com/ava-labs/hypersdk/fees"
"github.com/ava-labs/hypersdk/state"
"github.com/ava-labs/hypersdk/state/tstate"
)

Expand All @@ -27,6 +28,11 @@ const (

var _ api.HandlerFactory[api.VM] = (*JSONRPCServerFactory)(nil)

var (
errSimulateZeroActions = errors.New("simulateAction expects at least a single action, none found")
errTransactionExtraBytes = errors.New("transaction has extra bytes")
)

type JSONRPCServerFactory struct{}

func (JSONRPCServerFactory) New(vm api.VM) (api.Handler, error) {
Expand Down Expand Up @@ -95,7 +101,7 @@ func (j *JSONRPCServer) SubmitTx(
return fmt.Errorf("%w: unable to unmarshal on public service", err)
}
if !rtx.Empty() {
return errors.New("tx has extra bytes")
return errTransactionExtraBytes
}
if err := tx.Verify(ctx); err != nil {
return err
Expand Down Expand Up @@ -238,3 +244,70 @@ func (j *JSONRPCServer) Execute(
}
return nil
}

type SimulatActionsArgs struct {
Actions []codec.Bytes `json:"actions"`
Actor codec.Address `json:"actor"`
}

type SimulateActionResult struct {
Output codec.Bytes `json:"output"`
StateKeys state.Keys `json:"stateKeys"`
}

type SimulateActionsReply struct {
ActionResults []SimulateActionResult `json:"actionresults"`
}

func (j *JSONRPCServer) SimulateActions(
req *http.Request,
args *SimulatActionsArgs,
reply *SimulateActionsReply,
) error {
ctx, span := j.vm.Tracer().Start(req.Context(), "JSONRPCServer.SimulateActions")
defer span.End()

actionRegistry := j.vm.ActionRegistry()
var actions chain.Actions
for _, actionBytes := range args.Actions {
actionsReader := codec.NewReader(actionBytes, len(actionBytes))
action, err := (*actionRegistry).Unmarshal(actionsReader)
if err != nil {
return err
}
if !actionsReader.Empty() {
return errTransactionExtraBytes
}
actions = append(actions, action)
}
if len(actions) == 0 {
return errSimulateZeroActions
}
currentState, err := j.vm.ImmutableState(ctx)
if err != nil {
return err
}

currentTime := time.Now().UnixMilli()
for _, action := range actions {
recorder := state.NewRecorder(currentState)
actionOutput, err := action.Execute(ctx, j.vm.Rules(currentTime), recorder, currentTime, args.Actor, ids.Empty)

var actionResult SimulateActionResult
if actionOutput == nil {
actionResult.Output = []byte{}
} else {
actionResult.Output, err = chain.MarshalTyped(actionOutput)
if err != nil {
return fmt.Errorf("failed to marshal output: %w", err)
}
}
if err != nil {
return err
}
tsachiherman marked this conversation as resolved.
Show resolved Hide resolved
actionResult.StateKeys = recorder.GetStateKeys()
reply.ActionResults = append(reply.ActionResults, actionResult)
currentState = recorder
}
return nil
}
16 changes: 11 additions & 5 deletions examples/vmwithcontracts/actions/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import (

var _ chain.Action = (*Call)(nil)

const MaxCallDataSize = units.MiB
const (
MaxCallDataSize = units.MiB
MaxResultSizeLimit = units.MiB
)

type StateKeyPermission struct {
Key string
Expand Down Expand Up @@ -68,7 +71,7 @@ func (t *Call) Execute(
actor codec.Address,
_ ids.ID,
) (codec.Typed, error) {
resutBytes, err := t.r.CallContract(ctx, &runtime.CallInfo{
callInfo := &runtime.CallInfo{
Contract: t.ContractAddress,
Actor: actor,
State: &storage.ContractStateManager{Mutable: mu},
Expand All @@ -77,11 +80,13 @@ func (t *Call) Execute(
Timestamp: uint64(timestamp),
Fuel: t.Fuel,
Value: t.Value,
})
}
resultBytes, err := t.r.CallContract(ctx, callInfo)
if err != nil {
return nil, err
}
return &Result{Value: resutBytes}, nil
consumedFuel := t.Fuel - callInfo.RemainingFuel()
return &Result{Value: resultBytes, ConsumedFuel: consumedFuel}, nil
}

func (t *Call) ComputeUnits(chain.Rules) uint64 {
Expand Down Expand Up @@ -134,7 +139,8 @@ func (*Call) ValidRange(chain.Rules) (int64, int64) {
}

type Result struct {
Value []byte `serialize:"true" json:"value"`
Value []byte `serialize:"true" json:"value"`
ConsumedFuel uint64 `serialize:"true" json:"consumedfuel"`
}

func (*Result) GetTypeID() uint8 {
Expand Down
29 changes: 25 additions & 4 deletions examples/vmwithcontracts/cmd/vmwithcontracts-cli/cmd/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package cmd

import (
"context"
"errors"
"fmt"
"os"

"github.com/near/borsh-go"
Expand All @@ -15,9 +17,12 @@ import (
"github.com/ava-labs/hypersdk/cli/prompt"
"github.com/ava-labs/hypersdk/codec"
"github.com/ava-labs/hypersdk/examples/vmwithcontracts/actions"
"github.com/ava-labs/hypersdk/examples/vmwithcontracts/vm"
"github.com/ava-labs/hypersdk/utils"
)

var errUnexpectedSimulateActionsOutput = errors.New("returned output from SimulateActions was not actions.Result")

var actionCmd = &cobra.Command{
Use: "action",
RunE: func(*cobra.Command, []string) error {
Expand Down Expand Up @@ -141,18 +146,34 @@ var callCmd = &cobra.Command{
ContractAddress: contractAddress,
Value: amount,
Function: function,
Fuel: uint64(1000000000),
}

actionSimulationResults, err := cli.SimulateActions(ctx, chain.Actions{action}, priv.Address)
if err != nil {
return err
}
if len(actionSimulationResults) != 1 {
return fmt.Errorf("unexpected number of returned actions. One action expected, %d returned", len(actionSimulationResults))
}
actionSimulationResult := actionSimulationResults[0]

specifiedStateKeysSet, fuel, err := bcli.Simulate(ctx, *action, priv.Address)
rtx := codec.NewReader(actionSimulationResult.Output, len(actionSimulationResult.Output))

simulationResultOutput, err := (*vm.OutputParser).Unmarshal(rtx)
if err != nil {
return err
}
simulationResult, ok := simulationResultOutput.(*actions.Result)
if !ok {
return errUnexpectedSimulateActionsOutput
}

action.SpecifiedStateKeys = make([]actions.StateKeyPermission, 0, len(specifiedStateKeysSet))
for key, value := range specifiedStateKeysSet {
action.SpecifiedStateKeys = make([]actions.StateKeyPermission, 0, len(actionSimulationResult.StateKeys))
for key, value := range actionSimulationResult.StateKeys {
action.SpecifiedStateKeys = append(action.SpecifiedStateKeys, actions.StateKeyPermission{Key: key, Permission: value})
}
action.Fuel = fuel
action.Fuel = simulationResult.ConsumedFuel

// Confirm action
cont, err := prompt.Continue()
Expand Down
70 changes: 0 additions & 70 deletions examples/vmwithcontracts/storage/recorder.go

This file was deleted.

26 changes: 0 additions & 26 deletions examples/vmwithcontracts/vm/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,17 @@ package vm

import (
"context"
"encoding/hex"
"encoding/json"
"strings"
"time"

"github.com/ava-labs/hypersdk/api/jsonrpc"
"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/codec"
"github.com/ava-labs/hypersdk/examples/vmwithcontracts/actions"
"github.com/ava-labs/hypersdk/examples/vmwithcontracts/consts"
"github.com/ava-labs/hypersdk/examples/vmwithcontracts/storage"
"github.com/ava-labs/hypersdk/genesis"
"github.com/ava-labs/hypersdk/requester"
"github.com/ava-labs/hypersdk/state"
"github.com/ava-labs/hypersdk/utils"
)

Expand Down Expand Up @@ -137,26 +134,3 @@ func CreateParser(genesisBytes []byte) (chain.Parser, error) {
}
return NewParser(&genesis), nil
}

func (cli *JSONRPCClient) Simulate(ctx context.Context, callTx actions.Call, actor codec.Address) (state.Keys, uint64, error) {
resp := new(SimulateCallTxReply)
err := cli.requester.SendRequest(
ctx,
"simulateCallContractTx",
&SimulateCallTxArgs{CallTx: callTx, Actor: actor},
resp,
)
if err != nil {
return nil, 0, err
}
result := state.Keys{}
for _, entry := range resp.StateKeys {
hexBytes, err := hex.DecodeString(entry.HexKey)
if err != nil {
return nil, 0, err
}

result.Add(string(hexBytes), state.Permissions(entry.Permissions))
}
return result, resp.FuelConsumed, nil
}
Loading
Loading