From 9c74334cae6a946d221ca2e82977e055eedf7ebc Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:25:00 -0400 Subject: [PATCH] update per CR --- api/jsonrpc/server.go | 17 ++++++----- examples/vmwithcontracts/actions/call.go | 6 ---- .../cmd/vmwithcontracts-cli/cmd/action.go | 4 ++- state/keys.go | 4 +-- state/keys_test.go | 2 +- state/recorder.go | 28 ++++++++++--------- state/recorder_test.go | 23 ++++++++------- 7 files changed, 42 insertions(+), 42 deletions(-) diff --git a/api/jsonrpc/server.go b/api/jsonrpc/server.go index aa98bf8e33..66be584fa6 100644 --- a/api/jsonrpc/server.go +++ b/api/jsonrpc/server.go @@ -28,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) { @@ -96,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 @@ -263,22 +268,21 @@ func (j *JSONRPCServer) SimulateActions( return err } if !actionsReader.Empty() { - return errors.New("tx has extra bytes") + return errTransactionExtraBytes } actions = append(actions, action) } if len(actions) == 0 { - return errors.New("simulateAction expects at least a single action, none found") + return errSimulateZeroActions } currentState, err := j.vm.ImmutableState(ctx) if err != nil { return err } - recorder := state.NewRecorder(currentState) - 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 @@ -295,8 +299,7 @@ func (j *JSONRPCServer) SimulateActions( } actionResult.StateKeys = recorder.GetStateKeys() reply.ActionResults = append(reply.ActionResults, actionResult) - // create another recorder to recored the next action. - recorder = state.NewRecorder(recorder) + currentState = recorder } return nil } diff --git a/examples/vmwithcontracts/actions/call.go b/examples/vmwithcontracts/actions/call.go index 839a19e5b7..60271a94c5 100644 --- a/examples/vmwithcontracts/actions/call.go +++ b/examples/vmwithcontracts/actions/call.go @@ -146,9 +146,3 @@ type Result struct { func (*Result) GetTypeID() uint8 { return mconsts.ResultOutputID } - -// Size is the number of bytes it takes to represent this [Action]. This is used to preallocate -// memory during encoding and to charge bandwidth fees. -func (r *Result) Size() int { - return consts.Uint64Len + consts.Uint32Len + len(r.Value) -} diff --git a/examples/vmwithcontracts/cmd/vmwithcontracts-cli/cmd/action.go b/examples/vmwithcontracts/cmd/vmwithcontracts-cli/cmd/action.go index a44c24efc9..da13e4255e 100644 --- a/examples/vmwithcontracts/cmd/vmwithcontracts-cli/cmd/action.go +++ b/examples/vmwithcontracts/cmd/vmwithcontracts-cli/cmd/action.go @@ -21,6 +21,8 @@ import ( "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 { @@ -164,7 +166,7 @@ var callCmd = &cobra.Command{ } simulationResult, ok := simulationResultOutput.(*actions.Result) if !ok { - return errors.New("returned output from SimulateActions was not actions.Result") + return errUnexpectedSimulateActionsOutput } action.SpecifiedStateKeys = make([]actions.StateKeyPermission, 0, len(actionSimulationResult.StateKeys)) diff --git a/state/keys.go b/state/keys.go index fce4d8bfa6..13cb8d934c 100644 --- a/state/keys.go +++ b/state/keys.go @@ -70,7 +70,7 @@ func (k Keys) MarshalJSON() ([]byte, error) { return json.Marshal(keysJSON) } -func (k Keys) UnmarshalJSON(b []byte) error { +func (k *Keys) UnmarshalJSON(b []byte) error { var keysJSON keysJSON if err := json.Unmarshal(b, &keysJSON); err != nil { return err @@ -84,7 +84,7 @@ func (k Keys) UnmarshalJSON(b []byte) error { if err != nil { return err } - k[string(key)] = Permissions(perm) + (*k)[string(key)] = Permissions(perm) } } return nil diff --git a/state/keys_test.go b/state/keys_test.go index 8e819f836a..af7fca96a0 100644 --- a/state/keys_test.go +++ b/state/keys_test.go @@ -180,4 +180,4 @@ func TestKeysMarshalingFuzz(t *testing.T) { require.NoError(decodedKeys.UnmarshalJSON(bytes)) require.True(keys.compare(decodedKeys)) } -} +} \ No newline at end of file diff --git a/state/recorder.go b/state/recorder.go index 59d0f94161..d5e533bc25 100644 --- a/state/recorder.go +++ b/state/recorder.go @@ -13,7 +13,7 @@ import ( // The Recorder struct wraps an [Immutable] state object and tracks the permissions used // against the various keys. The Recorder struct implements the [Mutable] interface, allowing // it to act as a direct replacement for a database view. -// The Recorder struct maintain the same semantics as tstate_view in regards to the various +// The Recorder struct maintains the same semantics as TStateView in regards to the various // required access permissions. type Recorder struct { // State is the underlying [Immutable] object @@ -28,33 +28,34 @@ func NewRecorder(db Immutable) *Recorder { return &Recorder{state: db, changedValues: map[string][]byte{}, stateKeys: map[string][]byte{}, keys: Keys{}} } -func (r *Recorder) checkState(ctx context.Context, key []byte) error { - if _, has := r.stateKeys[string(key)]; has { - return nil +func (r *Recorder) checkState(ctx context.Context, key []byte) ([]byte, error) { + if val, has := r.stateKeys[string(key)]; has { + return val, nil } value, err := r.state.GetValue(ctx, key) if err == nil { // no error, key found. r.stateKeys[string(key)] = value - return nil + return value, nil } if errors.Is(err, database.ErrNotFound) { r.stateKeys[string(key)] = nil err = nil } - return err + return nil, err } func (r *Recorder) Insert(ctx context.Context, key []byte, value []byte) error { stringKey := string(key) - if err := r.checkState(ctx, key); err != nil { + var stateKeyVal []byte + var err error + if stateKeyVal, err = r.checkState(ctx, key); err != nil { return err } - stateKeyVal, inStateKeys := r.stateKeys[stringKey] - if inStateKeys && stateKeyVal != nil { + if stateKeyVal != nil { // underlying storage already has that key. r.keys[stringKey] |= Write } else { @@ -77,7 +78,9 @@ func (r *Recorder) Remove(_ context.Context, key []byte) error { func (r *Recorder) GetValue(ctx context.Context, key []byte) (value []byte, err error) { stringKey := string(key) - if err := r.checkState(ctx, key); err != nil { + var stateKeyVal []byte + + if stateKeyVal, err = r.checkState(ctx, key); err != nil { return nil, err } r.keys[stringKey] |= Read @@ -87,11 +90,10 @@ func (r *Recorder) GetValue(ctx context.Context, key []byte) (value []byte, err } return value, nil } - value = r.stateKeys[stringKey] - if value == nil { // no such key exist. + if stateKeyVal == nil { // no such key exist. return nil, database.ErrNotFound } - return value, nil + return stateKeyVal, nil } func (r *Recorder) GetStateKeys() Keys { diff --git a/state/recorder_test.go b/state/recorder_test.go index 7e39d69b1b..bfcf3b21cd 100644 --- a/state/recorder_test.go +++ b/state/recorder_test.go @@ -11,19 +11,18 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/stretchr/testify/require" + "github.com/ava-labs/hypersdk/keys" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/state/tstate" ) func randomNewKey() []byte { - randNewKey := make([]byte, 32) + randNewKey := make([]byte, 30, 32) _, err := rand.Read(randNewKey) if err != nil { panic(err) } - randNewKey[30] = 0 - randNewKey[31] = 1 - return randNewKey + return keys.EncodeChunks(randNewKey, 1) } func randomizeView(tstate *tstate.TState, keyCount int) (*tstate.TStateView, [][]byte, map[string]state.Permissions, map[string][]byte) { @@ -55,7 +54,7 @@ func TestRecorderInnerFuzz(t *testing.T) { removedKeys map[string]bool ) - randomKey := func() []byte { + pickExistingKeyAtRandom := func() []byte { randKey := make([]byte, 1) _, err := rand.Read(randKey) require.NoError(err) @@ -78,7 +77,7 @@ func TestRecorderInnerFuzz(t *testing.T) { require.NoError(err) switch op[0] % 6 { case 0: // insert into existing entry - randKey := randomKey() + randKey := pickExistingKeyAtRandom() err := recorder.Insert(context.Background(), randKey, []byte{1, 2, 3, 4}) require.NoError(err) require.True(recorder.GetStateKeys()[string(randKey)].Has(state.Write)) @@ -91,7 +90,7 @@ func TestRecorderInnerFuzz(t *testing.T) { require.True(recorder.GetStateKeys()[string(randNewKey)].Has(state.Allocate | state.Write)) keys = append(keys, randNewKey) case 2: // remove existing entry - randKey := randomKey() + randKey := pickExistingKeyAtRandom() err := recorder.Remove(context.Background(), randKey) require.NoError(err) removedKeys[string(randKey)] = true @@ -102,7 +101,7 @@ func TestRecorderInnerFuzz(t *testing.T) { require.NoError(err) require.True(recorder.GetStateKeys()[string(randKey)].Has(state.Write)) case 4: // get value of existing entry - randKey := randomKey() + randKey := pickExistingKeyAtRandom() val, err := recorder.GetValue(context.Background(), randKey) require.NoError(err) require.NotEmpty(val) @@ -143,7 +142,7 @@ func TestRecorderSideBySideFuzz(t *testing.T) { storage map[string][]byte ) - randomKey := func() []byte { + pickExistingKeyAtRandom := func() []byte { randKey := make([]byte, 1) _, err := rand.Read(randKey) require.NoError(err) @@ -173,7 +172,7 @@ func TestRecorderSideBySideFuzz(t *testing.T) { require.NoError(err) switch op[0] % 6 { case 0: // insert into existing entry - randKey := randomKey() + randKey := pickExistingKeyAtRandom() randVal := randomValue() err := recorder.Insert(context.Background(), randKey, randVal) @@ -197,7 +196,7 @@ func TestRecorderSideBySideFuzz(t *testing.T) { keys = append(keys, randNewKey) case 2: // remove existing entry - randKey := randomKey() + randKey := pickExistingKeyAtRandom() err := recorder.Remove(context.Background(), randKey) require.NoError(err) @@ -219,7 +218,7 @@ func TestRecorderSideBySideFuzz(t *testing.T) { err = stateView.Remove(context.Background(), randKey) require.NoError(err) case 4: // get value of existing entry - randKey := randomKey() + randKey := pickExistingKeyAtRandom() val, err := recorder.GetValue(context.Background(), randKey) require.NoError(err)