diff --git a/state/factory/factory.go b/state/factory/factory.go index 37cd5a7b62..274cc41b7f 100644 --- a/state/factory/factory.go +++ b/state/factory/factory.go @@ -87,7 +87,7 @@ type ( NewBlockBuilder(context.Context, actpool.ActPool, func(action.Envelope) (*action.SealedEnvelope, error)) (*block.Builder, error) PutBlock(context.Context, *block.Block) error WorkingSet(context.Context) (protocol.StateManager, error) - WorkingSetAtHeight(context.Context, uint64) (protocol.StateManager, error) + WorkingSetAtHeight(context.Context, uint64, ...*action.SealedEnvelope) (protocol.StateManager, error) } // factory implements StateFactory interface, tracks changes to account/contract and batch-commits to DB @@ -408,16 +408,29 @@ func (sf *factory) WorkingSet(ctx context.Context) (protocol.StateManager, error return sf.newWorkingSet(ctx, sf.currentChainHeight+1) } -func (sf *factory) WorkingSetAtHeight(ctx context.Context, height uint64) (protocol.StateManager, error) { +func (sf *factory) WorkingSetAtHeight(ctx context.Context, height uint64, preacts ...*action.SealedEnvelope) (protocol.StateManager, error) { if !sf.saveHistory { return nil, ErrNoArchiveData } sf.mutex.Lock() - defer sf.mutex.Unlock() if height > sf.currentChainHeight { + sf.mutex.Unlock() return nil, errors.Errorf("query height %d is higher than tip height %d", height, sf.currentChainHeight) } - return sf.newWorkingSetAtHeight(ctx, height) + ws, err := sf.newWorkingSetAtHeight(ctx, height) + sf.mutex.Unlock() + if err != nil { + return nil, errors.Wrap(err, "failed to obtain working set from state factory") + } + if len(preacts) == 0 { + return ws, nil + } + // prepare workingset at height, and run acts + ws.height++ + if err := ws.Process(ctx, preacts); err != nil { + return nil, err + } + return ws, nil } // PutBlock persists all changes in RunActions() into the DB diff --git a/state/factory/statedb.go b/state/factory/statedb.go index 638debf7ab..03634eb060 100644 --- a/state/factory/statedb.go +++ b/state/factory/statedb.go @@ -270,9 +270,20 @@ func (sdb *stateDB) WorkingSet(ctx context.Context) (protocol.StateManager, erro return sdb.newWorkingSet(ctx, height+1) } -func (sdb *stateDB) WorkingSetAtHeight(ctx context.Context, height uint64) (protocol.StateManager, error) { - // TODO: implement archive mode - return sdb.newWorkingSet(ctx, height) +func (sdb *stateDB) WorkingSetAtHeight(ctx context.Context, height uint64, preacts ...*action.SealedEnvelope) (protocol.StateManager, error) { + ws, err := sdb.newWorkingSet(ctx, height) + if err != nil { + return nil, errors.Wrap(err, "failed to obtain working set from state db") + } + if len(preacts) == 0 { + return ws, nil + } + // prepare workingset at height, and run acts + ws.height++ + if err := ws.Process(ctx, preacts); err != nil { + return nil, err + } + return ws, nil } // 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 475f831700..c383989d17 100644 --- a/test/mock/mock_factory/mock_factory.go +++ b/test/mock/mock_factory/mock_factory.go @@ -210,16 +210,21 @@ func (mr *MockFactoryMockRecorder) WorkingSet(arg0 interface{}) *gomock.Call { } // WorkingSetAtHeight mocks base method. -func (m *MockFactory) WorkingSetAtHeight(arg0 context.Context, arg1 uint64) (protocol.StateManager, error) { +func (m *MockFactory) WorkingSetAtHeight(arg0 context.Context, arg1 uint64, arg2 ...*action.SealedEnvelope) (protocol.StateManager, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WorkingSetAtHeight", arg0, arg1) + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "WorkingSetAtHeight", varargs...) 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 { +func (mr *MockFactoryMockRecorder) WorkingSetAtHeight(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WorkingSetAtHeight", reflect.TypeOf((*MockFactory)(nil).WorkingSetAtHeight), arg0, arg1) + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WorkingSetAtHeight", reflect.TypeOf((*MockFactory)(nil).WorkingSetAtHeight), varargs...) }