diff --git a/action/protocol/execution/evm/evm.go b/action/protocol/execution/evm/evm.go index 3c69550469..114ec664f5 100644 --- a/action/protocol/execution/evm/evm.go +++ b/action/protocol/execution/evm/evm.go @@ -613,6 +613,7 @@ func SimulateExecution( sm protocol.StateManager, caller address.Address, ex action.TxDataForSimulation, + opts ...protocol.SimulateOption, ) ([]byte, *action.Receipt, error) { ctx, span := tracer.NewSpan(ctx, "evm.SimulateExecution") defer span.End() @@ -632,7 +633,16 @@ func SimulateExecution( if err != nil { return nil, nil, err } - ctx = protocol.WithBlockCtx( + cfg := &protocol.SimulateOptionConfig{} + for _, opt := range opts { + opt(cfg) + } + if cfg.PreOpt != nil { + if err := cfg.PreOpt(sm); err != nil { + return nil, nil, err + } + } + ctx = protocol.WithFeatureCtx(protocol.WithBlockCtx( ctx, protocol.BlockCtx{ BlockHeight: bcCtx.Tip.Height + 1, @@ -642,8 +652,6 @@ func SimulateExecution( BaseFee: protocol.CalcBaseFee(g.Blockchain, &bcCtx.Tip), ExcessBlobGas: protocol.CalcExcessBlobGas(bcCtx.Tip.ExcessBlobGas, bcCtx.Tip.BlobGasUsed), }, - ) - - ctx = protocol.WithFeatureCtx(ctx) + )) return ExecuteContract(ctx, sm, ex) } diff --git a/action/protocol/execution/protocol_test.go b/action/protocol/execution/protocol_test.go index 13d2c406b9..a33e343e31 100644 --- a/action/protocol/execution/protocol_test.go +++ b/action/protocol/execution/protocol_test.go @@ -293,7 +293,11 @@ func readExecution( GetBlockTime: getBlockTimeForTest, DepositGasFunc: rewarding.DepositGas, }) - return sf.SimulateExecution(ctx, addr, elp) + ws, err := sf.WorkingSet(ctx) + if err != nil { + return nil, nil, err + } + return evm.SimulateExecution(ctx, ws, addr, elp) } func (sct *SmartContractTest) runExecutions( diff --git a/api/coreservice.go b/api/coreservice.go index a6dc20c75a..1625d55519 100644 --- a/api/coreservice.go +++ b/api/coreservice.go @@ -1820,7 +1820,11 @@ func (core *coreService) ReadContractStorage(ctx context.Context, addr address.A if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - return core.sf.ReadContractStorage(ctx, addr, key) + ws, err := core.sf.WorkingSet(ctx) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + return evm.ReadContractStorage(ctx, ws, addr, key) } func (core *coreService) ReceiveBlock(blk *block.Block) error { @@ -1969,7 +1973,11 @@ func (core *coreService) simulateExecution(ctx context.Context, addr address.Add GetBlockTime: core.getBlockTime, DepositGasFunc: rewarding.DepositGas, }) - return core.sf.SimulateExecution(ctx, addr, elp, opts...) + ws, err := core.sf.WorkingSet(ctx) + if err != nil { + return nil, nil, status.Error(codes.Internal, err.Error()) + } + return evm.SimulateExecution(ctx, ws, addr, elp, opts...) } func filterReceipts(receipts []*action.Receipt, actHash hash.Hash256) *action.Receipt { diff --git a/chainservice/builder.go b/chainservice/builder.go index 7b3e2051b8..bf6c589a45 100644 --- a/chainservice/builder.go +++ b/chainservice/builder.go @@ -735,7 +735,11 @@ func (builder *Builder) registerRollDPoSProtocol() error { GetBlockTime: getBlockTime, DepositGasFunc: rewarding.DepositGas, }) - data, _, err := factory.SimulateExecution(ctx, addr, elp) + ws, err := factory.WorkingSet(ctx) + if err != nil { + return nil, err + } + data, _, err := evm.SimulateExecution(ctx, ws, addr, elp) return data, err }, candidatesutil.CandidatesFromDB, diff --git a/e2etest/staking_test.go b/e2etest/staking_test.go index ea6b2e8e09..3fc4680145 100644 --- a/e2etest/staking_test.go +++ b/e2etest/staking_test.go @@ -125,8 +125,11 @@ func TestStakingContract(t *testing.T) { GetBlockTime: fakeGetBlockTime, DepositGasFunc: rewarding.DepositGas, }) - data, _, err := sf.SimulateExecution(ctx, addr, elp) - + ws, err := sf.WorkingSet(ctx) + if err != nil { + return nil, err + } + data, _, err := evm.SimulateExecution(ctx, ws, addr, elp) return data, err }) require.NoError(err) diff --git a/state/factory/factory.go b/state/factory/factory.go index f5dcca0527..edd913e16d 100644 --- a/state/factory/factory.go +++ b/state/factory/factory.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022 IoTeX Foundation +// Copyright (c) 2024 IoTeX Foundation // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. @@ -18,7 +18,6 @@ import ( "github.com/iotexproject/go-pkgs/cache" "github.com/iotexproject/go-pkgs/hash" - "github.com/iotexproject/iotex-address/address" "github.com/iotexproject/iotex-core/v2/action" "github.com/iotexproject/iotex-core/v2/action/protocol" @@ -86,12 +85,12 @@ type ( Validate(context.Context, *block.Block) error // NewBlockBuilder creates block builder NewBlockBuilder(context.Context, actpool.ActPool, func(action.Envelope) (*action.SealedEnvelope, error)) (*block.Builder, error) - SimulateExecution(context.Context, address.Address, action.Envelope, ...protocol.SimulateOption) ([]byte, *action.Receipt, error) - ReadContractStorage(context.Context, address.Address, []byte) ([]byte, error) PutBlock(context.Context, *block.Block) error DeleteTipBlock(context.Context, *block.Block) error StateAtHeight(uint64, interface{}, ...protocol.StateOption) error StatesAtHeight(uint64, ...protocol.StateOption) (state.Iterator, error) + WorkingSet(context.Context) (protocol.StateManager, error) + WorkingSetAtHeight(context.Context, uint64) (protocol.StateManager, error) } // factory implements StateFactory interface, tracks changes to account/contract and batch-commits to DB @@ -382,44 +381,16 @@ func (sf *factory) NewBlockBuilder( return blkBuilder, nil } -// SimulateExecution simulates a running of smart contract operation, this is done off the network since it does not -// cause any state change -func (sf *factory) SimulateExecution( - ctx context.Context, - caller address.Address, - elp action.Envelope, - opts ...protocol.SimulateOption, -) ([]byte, *action.Receipt, error) { - ctx, span := tracer.NewSpan(ctx, "factory.SimulateExecution") - defer span.End() - +func (sf *factory) WorkingSet(ctx context.Context) (protocol.StateManager, error) { sf.mutex.Lock() - ws, err := sf.newWorkingSet(ctx, sf.currentChainHeight+1) - sf.mutex.Unlock() - if err != nil { - return nil, nil, errors.Wrap(err, "failed to obtain working set from state factory") - } - cfg := &protocol.SimulateOptionConfig{} - for _, opt := range opts { - opt(cfg) - } - if cfg.PreOpt != nil { - if err := cfg.PreOpt(ws); err != nil { - return nil, nil, err - } - } - return evm.SimulateExecution(ctx, ws, caller, elp) + defer sf.mutex.Unlock() + return sf.newWorkingSet(ctx, sf.currentChainHeight+1) } -// ReadContractStorage reads contract's storage -func (sf *factory) ReadContractStorage(ctx context.Context, contract address.Address, key []byte) ([]byte, error) { +func (sf *factory) WorkingSetAtHeight(ctx context.Context, height uint64) (protocol.StateManager, error) { sf.mutex.Lock() - ws, err := sf.newWorkingSet(ctx, sf.currentChainHeight+1) - sf.mutex.Unlock() - if err != nil { - return nil, errors.Wrap(err, "failed to generate working set from state factory") - } - return evm.ReadContractStorage(ctx, ws, contract, key) + defer sf.mutex.Unlock() + return sf.newWorkingSet(ctx, height+1) } // PutBlock persists all changes in RunActions() into the DB diff --git a/state/factory/factory_test.go b/state/factory/factory_test.go index ae065537d1..97ebf293fa 100644 --- a/state/factory/factory_test.go +++ b/state/factory/factory_test.go @@ -1224,7 +1224,9 @@ func testSimulateExecution(ctx context.Context, sf Factory, t *testing.T) { }, DepositGasFunc: rewarding.DepositGas, }) - _, _, err = sf.SimulateExecution(ctx, addr, elp) + ws, err := sf.WorkingSet(ctx) + require.NoError(err) + _, _, err = evm.SimulateExecution(ctx, ws, addr, elp) require.NoError(err) } diff --git a/state/factory/statedb.go b/state/factory/statedb.go index 9cf0c62c14..7da193a25f 100644 --- a/state/factory/statedb.go +++ b/state/factory/statedb.go @@ -17,7 +17,6 @@ import ( "github.com/iotexproject/go-pkgs/cache" "github.com/iotexproject/go-pkgs/hash" - "github.com/iotexproject/iotex-address/address" "github.com/iotexproject/iotex-core/v2/action" "github.com/iotexproject/iotex-core/v2/action/protocol" @@ -30,7 +29,6 @@ import ( "github.com/iotexproject/iotex-core/v2/db/batch" "github.com/iotexproject/iotex-core/v2/pkg/log" "github.com/iotexproject/iotex-core/v2/pkg/prometheustimer" - "github.com/iotexproject/iotex-core/v2/pkg/tracer" "github.com/iotexproject/iotex-core/v2/pkg/util/byteutil" "github.com/iotexproject/iotex-core/v2/state" ) @@ -261,46 +259,15 @@ func (sdb *stateDB) NewBlockBuilder( return blkBuilder, nil } -// SimulateExecution simulates a running of smart contract operation, this is done off the network since it does not -// cause any state change -func (sdb *stateDB) SimulateExecution( - ctx context.Context, - caller address.Address, - elp action.Envelope, - opts ...protocol.SimulateOption, -) ([]byte, *action.Receipt, error) { - ctx, span := tracer.NewSpan(ctx, "stateDB.SimulateExecution") - defer span.End() - +func (sdb *stateDB) WorkingSet(ctx context.Context) (protocol.StateManager, error) { sdb.mutex.RLock() - currHeight := sdb.currentChainHeight + height := sdb.currentChainHeight sdb.mutex.RUnlock() - ws, err := sdb.newWorkingSet(ctx, currHeight+1) - if err != nil { - return nil, nil, err - } - cfg := &protocol.SimulateOptionConfig{} - for _, opt := range opts { - opt(cfg) - } - if cfg.PreOpt != nil { - if err := cfg.PreOpt(ws); err != nil { - return nil, nil, err - } - } - return evm.SimulateExecution(ctx, ws, caller, elp) + return sdb.newWorkingSet(ctx, height+1) } -// ReadContractStorage reads contract's storage -func (sdb *stateDB) ReadContractStorage(ctx context.Context, contract address.Address, key []byte) ([]byte, error) { - sdb.mutex.RLock() - currHeight := sdb.currentChainHeight - sdb.mutex.RUnlock() - ws, err := sdb.newWorkingSet(ctx, currHeight+1) - if err != nil { - return nil, errors.Wrap(err, "failed to generate working set from state db") - } - return evm.ReadContractStorage(ctx, ws, contract, key) +func (sdb *stateDB) WorkingSetAtHeight(ctx context.Context, height uint64) (protocol.StateManager, error) { + return sdb.newWorkingSet(ctx, height+1) } // PutBlock persists all changes in RunActions() into the DB diff --git a/test/mock/mock_factory/mock_factory.go b/test/mock/mock_factory/mock_factory.go index c96ca3e24a..4e5f71671e 100644 --- a/test/mock/mock_factory/mock_factory.go +++ b/test/mock/mock_factory/mock_factory.go @@ -9,7 +9,6 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - address "github.com/iotexproject/iotex-address/address" action "github.com/iotexproject/iotex-core/v2/action" protocol "github.com/iotexproject/iotex-core/v2/action/protocol" actpool "github.com/iotexproject/iotex-core/v2/actpool" @@ -98,21 +97,6 @@ func (mr *MockFactoryMockRecorder) PutBlock(arg0, arg1 interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutBlock", reflect.TypeOf((*MockFactory)(nil).PutBlock), arg0, arg1) } -// ReadContractStorage mocks base method. -func (m *MockFactory) ReadContractStorage(arg0 context.Context, arg1 address.Address, arg2 []byte) ([]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReadContractStorage", arg0, arg1, arg2) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ReadContractStorage indicates an expected call of ReadContractStorage. -func (mr *MockFactoryMockRecorder) ReadContractStorage(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadContractStorage", reflect.TypeOf((*MockFactory)(nil).ReadContractStorage), arg0, arg1, arg2) -} - // ReadView mocks base method. func (m *MockFactory) ReadView(arg0 string) (interface{}, error) { m.ctrl.T.Helper() @@ -142,27 +126,6 @@ func (mr *MockFactoryMockRecorder) Register(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockFactory)(nil).Register), arg0) } -// SimulateExecution mocks base method. -func (m *MockFactory) SimulateExecution(arg0 context.Context, arg1 address.Address, arg2 action.Envelope, arg3 ...protocol.SimulateOption) ([]byte, *action.Receipt, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1, arg2} - for _, a := range arg3 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "SimulateExecution", varargs...) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(*action.Receipt) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// SimulateExecution indicates an expected call of SimulateExecution. -func (mr *MockFactoryMockRecorder) SimulateExecution(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SimulateExecution", reflect.TypeOf((*MockFactory)(nil).SimulateExecution), varargs...) -} - // Start mocks base method. func (m *MockFactory) Start(arg0 context.Context) error { m.ctrl.T.Helper() @@ -283,3 +246,33 @@ func (mr *MockFactoryMockRecorder) Validate(arg0, arg1 interface{}) *gomock.Call mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockFactory)(nil).Validate), arg0, arg1) } + +// WorkingSet mocks base method. +func (m *MockFactory) WorkingSet(arg0 context.Context) (protocol.StateManager, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WorkingSet", arg0) + ret0, _ := ret[0].(protocol.StateManager) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WorkingSet indicates an expected call of WorkingSet. +func (mr *MockFactoryMockRecorder) WorkingSet(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WorkingSet", reflect.TypeOf((*MockFactory)(nil).WorkingSet), arg0) +} + +// WorkingSetAtHeight mocks base method. +func (m *MockFactory) WorkingSetAtHeight(arg0 context.Context, arg1 uint64) (protocol.StateManager, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WorkingSetAtHeight", arg0, arg1) + ret0, _ := ret[0].(protocol.StateManager) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WorkingSetAtHeight indicates an expected call of WorkingSetAtHeight. +func (mr *MockFactoryMockRecorder) WorkingSetAtHeight(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WorkingSetAtHeight", reflect.TypeOf((*MockFactory)(nil).WorkingSetAtHeight), arg0, arg1) +}