diff --git a/Makefile b/Makefile index d8b37127d1d..561b55a9d8a 100644 --- a/Makefile +++ b/Makefile @@ -160,6 +160,7 @@ generate-mocks: install-mock-generators mockery --name 'ExecutionState' --dir=engine/execution/state --case=underscore --output="engine/execution/state/mock" --outpkg="mock" mockery --name 'BlockComputer' --dir=engine/execution/computation/computer --case=underscore --output="engine/execution/computation/computer/mock" --outpkg="mock" mockery --name 'ComputationManager' --dir=engine/execution/computation --case=underscore --output="engine/execution/computation/mock" --outpkg="mock" + mockery --name 'Executor' --dir=engine/execution/computation/query --case=underscore --output="engine/execution/computation/query/mock" --outpkg="mock" mockery --name 'EpochComponentsFactory' --dir=engine/collection/epochmgr --case=underscore --output="engine/collection/epochmgr/mock" --outpkg="mock" mockery --name 'Backend' --dir=engine/collection/rpc --case=underscore --output="engine/collection/rpc/mock" --outpkg="mock" mockery --name 'ProviderEngine' --dir=engine/execution/provider --case=underscore --output="engine/execution/provider/mock" --outpkg="mock" diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 01d22346437..80ca887e727 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -901,11 +901,11 @@ func (exeNode *ExecutionNode) LoadIngestionEngine( // create scripts engine for handling script execution func (exeNode *ExecutionNode) LoadScriptsEngine(node *NodeConfig) (module.ReadyDoneAware, error) { - // for RPC to load it + exeNode.scriptsEng = scripts.New( node.Logger, node.State, - exeNode.computationManager, + exeNode.computationManager.QueryExecutor(), exeNode.executionState, ) diff --git a/engine/execution/computation/manager.go b/engine/execution/computation/manager.go index 907e17cd8e7..ce2c24d76d6 100644 --- a/engine/execution/computation/manager.go +++ b/engine/execution/computation/manager.go @@ -106,7 +106,6 @@ func New( } chainID := vmCtx.Chain.ChainID() - options := []fvm.Option{ fvm.WithReusableCadenceRuntimePool( reusableRuntime.NewReusableCadenceRuntimePool( @@ -121,6 +120,7 @@ func New( }, )), } + if params.ExtensiveTracing { options = append(options, fvm.WithExtensiveTracing()) } @@ -239,3 +239,7 @@ func (e *Manager) GetAccount( blockHeader, snapshot) } + +func (e *Manager) QueryExecutor() query.Executor { + return e.queryExecutor +} diff --git a/engine/execution/computation/query/mock/executor.go b/engine/execution/computation/query/mock/executor.go new file mode 100644 index 00000000000..4ffc343c6e5 --- /dev/null +++ b/engine/execution/computation/query/mock/executor.go @@ -0,0 +1,84 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mock + +import ( + context "context" + + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" + + snapshot "github.com/onflow/flow-go/fvm/storage/snapshot" +) + +// Executor is an autogenerated mock type for the Executor type +type Executor struct { + mock.Mock +} + +// ExecuteScript provides a mock function with given fields: ctx, script, arguments, blockHeader, _a4 +func (_m *Executor) ExecuteScript(ctx context.Context, script []byte, arguments [][]byte, blockHeader *flow.Header, _a4 snapshot.StorageSnapshot) ([]byte, error) { + ret := _m.Called(ctx, script, arguments, blockHeader, _a4) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []byte, [][]byte, *flow.Header, snapshot.StorageSnapshot) ([]byte, error)); ok { + return rf(ctx, script, arguments, blockHeader, _a4) + } + if rf, ok := ret.Get(0).(func(context.Context, []byte, [][]byte, *flow.Header, snapshot.StorageSnapshot) []byte); ok { + r0 = rf(ctx, script, arguments, blockHeader, _a4) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []byte, [][]byte, *flow.Header, snapshot.StorageSnapshot) error); ok { + r1 = rf(ctx, script, arguments, blockHeader, _a4) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAccount provides a mock function with given fields: ctx, addr, header, _a3 +func (_m *Executor) GetAccount(ctx context.Context, addr flow.Address, header *flow.Header, _a3 snapshot.StorageSnapshot) (*flow.Account, error) { + ret := _m.Called(ctx, addr, header, _a3) + + var r0 *flow.Account + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, flow.Address, *flow.Header, snapshot.StorageSnapshot) (*flow.Account, error)); ok { + return rf(ctx, addr, header, _a3) + } + if rf, ok := ret.Get(0).(func(context.Context, flow.Address, *flow.Header, snapshot.StorageSnapshot) *flow.Account); ok { + r0 = rf(ctx, addr, header, _a3) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.Account) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, flow.Address, *flow.Header, snapshot.StorageSnapshot) error); ok { + r1 = rf(ctx, addr, header, _a3) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewExecutor interface { + mock.TestingT + Cleanup(func()) +} + +// NewExecutor creates a new instance of Executor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewExecutor(t mockConstructorTestingTNewExecutor) *Executor { + mock := &Executor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/engine/execution/scripts/engine.go b/engine/execution/scripts/engine.go index 35ab0dbec55..31b7ab125cf 100644 --- a/engine/execution/scripts/engine.go +++ b/engine/execution/scripts/engine.go @@ -9,18 +9,31 @@ import ( "github.com/onflow/flow-go/engine" "github.com/onflow/flow-go/engine/execution" - "github.com/onflow/flow-go/engine/execution/computation" - "github.com/onflow/flow-go/engine/execution/state" + "github.com/onflow/flow-go/engine/execution/computation/query" + "github.com/onflow/flow-go/fvm/storage/snapshot" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" ) +// ScriptExecutionState is a subset of the `state.ExecutionState` interface purposed to only access the state +// used for script execution and not mutate the execution state of the blockchain. +type ScriptExecutionState interface { + // NewStorageSnapshot creates a new ready-only view at the given state commitment. + NewStorageSnapshot(flow.StateCommitment) snapshot.StorageSnapshot + + // StateCommitmentByBlockID returns the final state commitment for the provided block ID. + StateCommitmentByBlockID(context.Context, flow.Identifier) (flow.StateCommitment, error) + + // HasState returns true if the state with the given state commitment exists in memory + HasState(flow.StateCommitment) bool +} + type Engine struct { - unit *engine.Unit - log zerolog.Logger - state protocol.State - computationManager computation.ComputationManager - execState state.ExecutionState + unit *engine.Unit + log zerolog.Logger + state protocol.State + queryExecutor query.Executor + execState ScriptExecutionState } var _ execution.ScriptExecutor = (*Engine)(nil) @@ -28,15 +41,15 @@ var _ execution.ScriptExecutor = (*Engine)(nil) func New( logger zerolog.Logger, state protocol.State, - computationManager computation.ComputationManager, - execState state.ExecutionState, + queryExecutor query.Executor, + execState ScriptExecutionState, ) *Engine { return &Engine{ - unit: engine.NewUnit(), - log: logger.With().Str("engine", "scripts").Logger(), - state: state, - execState: execState, - computationManager: computationManager, + unit: engine.NewUnit(), + log: logger.With().Str("engine", "scripts").Logger(), + state: state, + execState: execState, + queryExecutor: queryExecutor, } } @@ -73,7 +86,7 @@ func (e *Engine) ExecuteScriptAtBlockID( blockSnapshot := e.execState.NewStorageSnapshot(stateCommit) - return e.computationManager.ExecuteScript( + return e.queryExecutor.ExecuteScript( ctx, script, arguments, @@ -131,5 +144,5 @@ func (e *Engine) GetAccount( blockSnapshot := e.execState.NewStorageSnapshot(stateCommit) - return e.computationManager.GetAccount(ctx, addr, block, blockSnapshot) + return e.queryExecutor.GetAccount(ctx, addr, block, blockSnapshot) } diff --git a/engine/execution/scripts/engine_test.go b/engine/execution/scripts/engine_test.go index 9f954530f0e..5b5c116830f 100644 --- a/engine/execution/scripts/engine_test.go +++ b/engine/execution/scripts/engine_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - computation "github.com/onflow/flow-go/engine/execution/computation/mock" + queryMock "github.com/onflow/flow-go/engine/execution/computation/query/mock" stateMock "github.com/onflow/flow-go/engine/execution/state/mock" "github.com/onflow/flow-go/model/flow" protocol "github.com/onflow/flow-go/state/protocol/mock" @@ -17,12 +17,12 @@ import ( ) type testingContext struct { - t *testing.T - engine *Engine - state *protocol.State - executionState *stateMock.ExecutionState - computationManager *computation.ComputationManager - mu *sync.Mutex + t *testing.T + engine *Engine + state *protocol.State + executionState *stateMock.ExecutionState + queryExecutor *queryMock.Executor + mu *sync.Mutex } func (ctx *testingContext) stateCommitmentExist(blockID flow.Identifier, commit flow.StateCommitment) { @@ -32,17 +32,17 @@ func (ctx *testingContext) stateCommitmentExist(blockID flow.Identifier, commit func runWithEngine(t *testing.T, fn func(ctx testingContext)) { log := unittest.Logger() - computationManager := new(computation.ComputationManager) + queryExecutor := new(queryMock.Executor) protocolState := new(protocol.State) execState := new(stateMock.ExecutionState) - engine := New(log, protocolState, computationManager, execState) + engine := New(log, protocolState, queryExecutor, execState) fn(testingContext{ - t: t, - engine: engine, - computationManager: computationManager, - executionState: execState, - state: protocolState, + t: t, + engine: engine, + queryExecutor: queryExecutor, + executionState: execState, + state: protocolState, }) } @@ -70,7 +70,7 @@ func TestExecuteScriptAtBlockID(t *testing.T) { ctx.executionState.On("HasState", *blockA.StartState).Return(true) // Successful call to computation manager - ctx.computationManager. + ctx.queryExecutor. On("ExecuteScript", mock.Anything, script, [][]byte(nil), blockA.Block.Header, nil). Return(scriptResult, nil) @@ -80,7 +80,7 @@ func TestExecuteScriptAtBlockID(t *testing.T) { assert.Equal(t, scriptResult, res) // Assert other components were called as expected - ctx.computationManager.AssertExpectations(t) + ctx.queryExecutor.AssertExpectations(t) ctx.executionState.AssertExpectations(t) ctx.state.AssertExpectations(t) })