Skip to content

Commit

Permalink
Add UTs for PreExecutor.PreExecute() (#1858)
Browse files Browse the repository at this point in the history
  • Loading branch information
RodrigoVillar authored Jan 20, 2025
1 parent 4440a22 commit 296bad6
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 10 deletions.
4 changes: 2 additions & 2 deletions chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ func (c *Chain) AsyncVerify(
func (c *Chain) PreExecute(
ctx context.Context,
parentBlk *ExecutionBlock,
view state.View,
im state.Immutable,
tx *Transaction,
) error {
return c.preExecutor.PreExecute(ctx, parentBlk, view, tx)
return c.preExecutor.PreExecute(ctx, parentBlk, im, tx)
}

func (c *Chain) ParseBlock(ctx context.Context, bytes []byte) (*ExecutionBlock, error) {
Expand Down
3 changes: 3 additions & 0 deletions chain/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ var (
ErrBlockTooBig = errors.New("block too big")
ErrKeyNotSpecified = errors.New("key not specified")

// State Correctness
ErrFailedToFetchFee = errors.New("failed to fetch fee")

// Misc
ErrNotImplemented = errors.New("not implemented")
ErrBlockNotProcessed = errors.New("block is not processed")
Expand Down
9 changes: 5 additions & 4 deletions chain/pre_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package chain

import (
"context"
"fmt"
"time"

"github.com/ava-labs/hypersdk/state"
Expand Down Expand Up @@ -36,12 +37,12 @@ func NewPreExecutor(
func (p *PreExecutor) PreExecute(
ctx context.Context,
parentBlk *ExecutionBlock,
view state.View,
im state.Immutable,
tx *Transaction,
) error {
feeRaw, err := view.GetValue(ctx, FeeKey(p.metadataManager.FeePrefix()))
feeRaw, err := im.GetValue(ctx, FeeKey(p.metadataManager.FeePrefix()))
if err != nil {
return err
return fmt.Errorf("%w: %w", ErrFailedToFetchFee, err)
}
feeManager := internalfees.NewManager(feeRaw)
now := time.Now().UnixMilli()
Expand Down Expand Up @@ -80,7 +81,7 @@ func (p *PreExecutor) PreExecute(
// Note, [PreExecute] ensures that the pending transaction does not have
// an expiry time further ahead than [ValidityWindow]. This ensures anything
// added to the [Mempool] is immediately executable.
if err := tx.PreExecute(ctx, nextFeeManager, p.balanceHandler, r, view, now); err != nil {
if err := tx.PreExecute(ctx, nextFeeManager, p.balanceHandler, r, im, now); err != nil {
return err
}
return nil
Expand Down
171 changes: 171 additions & 0 deletions chain/pre_executor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package chain_test

import (
"context"
"errors"
"testing"
"time"

"github.com/ava-labs/avalanchego/utils/set"
"github.com/stretchr/testify/require"

"github.com/ava-labs/hypersdk/chain"
"github.com/ava-labs/hypersdk/genesis"
"github.com/ava-labs/hypersdk/internal/validitywindow"
"github.com/ava-labs/hypersdk/internal/validitywindow/validitywindowtest"
"github.com/ava-labs/hypersdk/state"
"github.com/ava-labs/hypersdk/state/metadata"
"github.com/ava-labs/hypersdk/utils"
)

var (
feeKey = string(chain.FeeKey([]byte{2}))

errMockAuth = errors.New("mock auth error")
errMockValidityWindow = errors.New("mock validity window error")
)

func TestPreExecutor(t *testing.T) {
testRules := genesis.NewDefaultRules()
ruleFactory := genesis.ImmutableRuleFactory{
Rules: testRules,
}
validTx := &chain.Transaction{
TransactionData: chain.TransactionData{
Base: &chain.Base{
Timestamp: utils.UnixRMilli(
time.Now().UnixMilli(),
testRules.GetValidityWindow(),
),
},
},
Auth: &mockAuth{
start: -1,
end: -1,
},
}

tests := []struct {
name string
state map[string][]byte
tx *chain.Transaction
validityWindow chain.ValidityWindow
err error
}{
{
name: "valid tx",
state: map[string][]byte{
feeKey: {},
},
tx: validTx,
validityWindow: &validitywindowtest.MockTimeValidityWindow[*chain.Transaction]{},
},
{
name: "raw fee missing",
tx: validTx,
err: chain.ErrFailedToFetchFee,
},
{
name: "validity window error",
tx: validTx,
state: map[string][]byte{
feeKey: {},
},
validityWindow: &validitywindowtest.MockTimeValidityWindow[*chain.Transaction]{
OnIsRepeat: func(context.Context, validitywindow.ExecutionBlock[*chain.Transaction], []*chain.Transaction, int64) (set.Bits, error) {
return set.NewBits(), errMockValidityWindow
},
},
err: errMockValidityWindow,
},
{
name: "duplicate tx",
tx: validTx,
state: map[string][]byte{
feeKey: {},
},
validityWindow: &validitywindowtest.MockTimeValidityWindow[*chain.Transaction]{
OnIsRepeat: func(context.Context, validitywindow.ExecutionBlock[*chain.Transaction], []*chain.Transaction, int64) (set.Bits, error) {
return set.NewBits(0), nil
},
},
err: chain.ErrDuplicateTx,
},
{
name: "invalid state keys",
state: map[string][]byte{
feeKey: {},
},
tx: &chain.Transaction{
TransactionData: chain.TransactionData{
Actions: []chain.Action{
&mockAction{
stateKeys: state.Keys{
"": state.None,
},
},
},
},
Auth: &mockAuth{},
},
validityWindow: &validitywindowtest.MockTimeValidityWindow[*chain.Transaction]{},
err: chain.ErrInvalidKeyValue,
},
{
name: "verify auth error",
state: map[string][]byte{
feeKey: {},
},
tx: &chain.Transaction{
TransactionData: chain.TransactionData{
Base: &chain.Base{},
},
Auth: &mockAuth{
verifyError: errMockAuth,
},
},
validityWindow: &validitywindowtest.MockTimeValidityWindow[*chain.Transaction]{},
err: errMockAuth,
},
{
name: "tx pre-execute error",
state: map[string][]byte{
feeKey: {},
},
tx: &chain.Transaction{
TransactionData: chain.TransactionData{
Base: &chain.Base{},
},
Auth: &mockAuth{},
},
validityWindow: &validitywindowtest.MockTimeValidityWindow[*chain.Transaction]{},
err: chain.ErrTimestampTooLate,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := require.New(t)
ctx := context.Background()

preExecutor := chain.NewPreExecutor(
&ruleFactory,
tt.validityWindow,
metadata.NewDefaultManager(),
&mockBalanceHandler{},
)

r.ErrorIs(
preExecutor.PreExecute(
ctx,
nil,
state.ImmutableStorage(tt.state),
tt.tx,
), tt.err,
)
})
}
}
10 changes: 6 additions & 4 deletions chain/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type mockAction struct {
end int64
computeUnits uint64
typeID uint8
stateKeys state.Keys
}

func (m *mockAction) ComputeUnits(chain.Rules) uint64 {
Expand All @@ -56,8 +57,8 @@ func (m *mockAction) GetTypeID() uint8 {
return m.typeID
}

func (*mockAction) StateKeys(codec.Address, ids.ID) state.Keys {
return state.Keys{}
func (m *mockAction) StateKeys(codec.Address, ids.ID) state.Keys {
return m.stateKeys
}

func (m *mockAction) ValidRange(chain.Rules) (int64, int64) {
Expand Down Expand Up @@ -127,6 +128,7 @@ type mockAuth struct {
computeUnits uint64
actor codec.Address
sponsor codec.Address
verifyError error
}

func (m *mockAuth) Actor() codec.Address {
Expand Down Expand Up @@ -157,8 +159,8 @@ func (m *mockAuth) ValidRange(chain.Rules) (int64, int64) {
return m.start, m.end
}

func (*mockAuth) Verify(context.Context, []byte) error {
panic("unimplemented")
func (m *mockAuth) Verify(context.Context, []byte) error {
return m.verifyError
}

func TestJSONMarshalUnmarshal(t *testing.T) {
Expand Down

0 comments on commit 296bad6

Please sign in to comment.