@@ -40,10 +40,13 @@ type stateDB struct {
40
40
cfg Config
41
41
registry * protocol.Registry
42
42
dao db.KVStore // the underlying DB for account/contract storage
43
+ daoVersioned db.KvVersioned
43
44
timerFactory * prometheustimer.TimerFactory
44
45
workingsets cache.LRUCache // lru cache for workingsets
45
46
protocolView protocol.View
46
47
skipBlockValidationOnPut bool
48
+ versioned bool
49
+ metaNS string // metadata namespace for versioned DB
47
50
ps * patchStore
48
51
}
49
52
@@ -82,22 +85,39 @@ func DisableWorkingSetCacheOption() StateDBOption {
82
85
}
83
86
}
84
87
88
+ // MetadataNamespaceOption specifies the metadat namespace for versioned DB
89
+ func MetadataNamespaceOption (ns string ) StateDBOption {
90
+ return func (sdb * stateDB , cfg * Config ) error {
91
+ sdb .metaNS = ns
92
+ return nil
93
+ }
94
+ }
95
+
85
96
// NewStateDB creates a new state db
86
97
func NewStateDB (cfg Config , dao db.KVStore , opts ... StateDBOption ) (Factory , error ) {
87
98
sdb := stateDB {
88
99
cfg : cfg ,
89
100
currentChainHeight : 0 ,
101
+ versioned : cfg .Chain .EnableArchiveMode ,
90
102
registry : protocol .NewRegistry (),
91
103
protocolView : protocol.View {},
92
104
workingsets : cache .NewThreadSafeLruCache (int (cfg .Chain .WorkingSetCacheSize )),
93
- dao : dao ,
94
105
}
95
106
for _ , opt := range opts {
96
107
if err := opt (& sdb , & cfg ); err != nil {
97
108
log .S ().Errorf ("Failed to execute state factory creation option %p: %v" , opt , err )
98
109
return nil , err
99
110
}
100
111
}
112
+ if sdb .versioned {
113
+ daoVersioned , ok := dao .(db.KvVersioned )
114
+ if ! ok {
115
+ return nil , errors .Wrap (ErrNotSupported , "cannot enable archive mode StateDB with non-versioned DB" )
116
+ }
117
+ sdb .daoVersioned = daoVersioned
118
+ } else {
119
+ sdb .dao = dao
120
+ }
101
121
timerFactory , err := prometheustimer .New (
102
122
"iotex_statefactory_perf" ,
103
123
"Performance of state factory module" ,
@@ -111,23 +131,30 @@ func NewStateDB(cfg Config, dao db.KVStore, opts ...StateDBOption) (Factory, err
111
131
return & sdb , nil
112
132
}
113
133
134
+ func (sdb * stateDB ) DAO (height uint64 ) db.KVStore {
135
+ if sdb .versioned {
136
+ return sdb .daoVersioned .SetVersion (height )
137
+ }
138
+ return sdb .dao
139
+ }
140
+
114
141
func (sdb * stateDB ) Start (ctx context.Context ) error {
115
142
ctx = protocol .WithRegistry (ctx , sdb .registry )
116
- if err := sdb .dao .Start (ctx ); err != nil {
143
+ if err := sdb .DAO ( 0 ) .Start (ctx ); err != nil {
117
144
return err
118
145
}
119
146
// check factory height
120
- h , err := sdb .dao . Get ( AccountKVNamespace , [] byte ( CurrentHeightKey ) )
147
+ h , err := sdb .getHeight ( )
121
148
switch errors .Cause (err ) {
122
149
case nil :
123
- sdb .currentChainHeight = byteutil . BytesToUint64 ( h )
150
+ sdb .currentChainHeight = h
124
151
// start all protocols
125
152
if sdb .protocolView , err = sdb .registry .StartAll (ctx , sdb ); err != nil {
126
153
return err
127
154
}
128
155
case db .ErrNotExist :
129
156
sdb .currentChainHeight = 0
130
- if err = sdb .dao . Put ( AccountKVNamespace , [] byte ( CurrentHeightKey ), byteutil . Uint64ToBytes ( 0 ) ); err != nil {
157
+ if err = sdb .putHeight ( 0 ); err != nil {
131
158
return errors .Wrap (err , "failed to init statedb's height" )
132
159
}
133
160
// start all protocols
@@ -158,24 +185,46 @@ func (sdb *stateDB) Stop(ctx context.Context) error {
158
185
sdb .mutex .Lock ()
159
186
defer sdb .mutex .Unlock ()
160
187
sdb .workingsets .Clear ()
161
- return sdb .dao .Stop (ctx )
188
+ return sdb .DAO ( 0 ) .Stop (ctx )
162
189
}
163
190
164
191
// Height returns factory's height
165
192
func (sdb * stateDB ) Height () (uint64 , error ) {
166
193
sdb .mutex .RLock ()
167
194
defer sdb .mutex .RUnlock ()
168
- height , err := sdb .dao .Get (AccountKVNamespace , []byte (CurrentHeightKey ))
195
+ return sdb .getHeight ()
196
+ }
197
+
198
+ func (sdb * stateDB ) getHeight () (uint64 , error ) {
199
+ height , err := sdb .DAO (0 ).Get (sdb .metadataNS (), []byte (CurrentHeightKey ))
169
200
if err != nil {
170
201
return 0 , errors .Wrap (err , "failed to get factory's height from underlying DB" )
171
202
}
172
203
return byteutil .BytesToUint64 (height ), nil
173
204
}
174
205
206
+ func (sdb * stateDB ) putHeight (h uint64 ) error {
207
+ return sdb .DAO (h ).Put (sdb .metadataNS (), []byte (CurrentHeightKey ), byteutil .Uint64ToBytes (h ))
208
+ }
209
+
210
+ func (sdb * stateDB ) metadataNS () string {
211
+ if sdb .versioned {
212
+ return sdb .metaNS
213
+ }
214
+ return AccountKVNamespace
215
+ }
216
+
217
+ func (sdb * stateDB ) transCurrentHeight (wi * batch.WriteInfo ) * batch.WriteInfo {
218
+ if wi .Namespace () == sdb .metaNS && string (wi .Key ()) == CurrentHeightKey && wi .WriteType () == batch .Put {
219
+ return batch .NewWriteInfo (wi .WriteType (), AccountKVNamespace , wi .Key (), wi .Value (), wi .Error ())
220
+ }
221
+ return wi
222
+ }
223
+
175
224
func (sdb * stateDB ) newWorkingSet (ctx context.Context , height uint64 ) (* workingSet , error ) {
176
225
g := genesis .MustExtractGenesisContext (ctx )
177
226
flusher , err := db .NewKVStoreFlusher (
178
- sdb .dao ,
227
+ sdb .DAO ( height ) ,
179
228
batch .NewCachedBatch (),
180
229
sdb .flusherOptions (! g .IsEaster (height ))... ,
181
230
)
@@ -189,7 +238,7 @@ func (sdb *stateDB) newWorkingSet(ctx context.Context, height uint64) (*workingS
189
238
flusher .KVStoreWithBuffer ().MustPut (p .Namespace , p .Key , p .Value )
190
239
}
191
240
}
192
- store := newStateDBWorkingSetStore (sdb .protocolView , flusher , g .IsNewfoundland (height ))
241
+ store := newStateDBWorkingSetStore (sdb .protocolView , flusher , g .IsNewfoundland (height ), sdb . metadataNS () )
193
242
if err := store .Start (ctx ); err != nil {
194
243
return nil , err
195
244
}
@@ -267,7 +316,6 @@ func (sdb *stateDB) WorkingSet(ctx context.Context) (protocol.StateManager, erro
267
316
}
268
317
269
318
func (sdb * stateDB ) WorkingSetAtHeight (ctx context.Context , height uint64 ) (protocol.StateManager , error ) {
270
- // TODO: implement archive mode
271
319
return sdb .newWorkingSet (ctx , height )
272
320
}
273
321
@@ -327,12 +375,13 @@ func (sdb *stateDB) State(s interface{}, opts ...protocol.StateOption) (uint64,
327
375
if err != nil {
328
376
return 0 , err
329
377
}
330
- sdb .mutex .RLock ()
331
- defer sdb .mutex .RUnlock ()
332
378
if cfg .Keys != nil {
333
379
return 0 , errors .Wrap (ErrNotSupported , "Read state with keys option has not been implemented yet" )
334
380
}
335
- return sdb .currentChainHeight , sdb .state (cfg .Namespace , cfg .Key , s )
381
+ sdb .mutex .RLock ()
382
+ height := sdb .currentChainHeight
383
+ sdb .mutex .RUnlock ()
384
+ return height , sdb .state (height , cfg .Namespace , cfg .Key , s )
336
385
}
337
386
338
387
// State returns a set of states in the state factory
@@ -346,7 +395,7 @@ func (sdb *stateDB) States(opts ...protocol.StateOption) (uint64, state.Iterator
346
395
if cfg .Key != nil {
347
396
return sdb .currentChainHeight , nil , errors .Wrap (ErrNotSupported , "Read states with key option has not been implemented yet" )
348
397
}
349
- keys , values , err := readStates (sdb .dao , cfg .Namespace , cfg .Keys )
398
+ keys , values , err := readStates (sdb .DAO ( sdb . currentChainHeight ) , cfg .Namespace , cfg .Keys )
350
399
if err != nil {
351
400
return 0 , nil , err
352
401
}
@@ -358,28 +407,23 @@ func (sdb *stateDB) States(opts ...protocol.StateOption) (uint64, state.Iterator
358
407
return sdb .currentChainHeight , iter , nil
359
408
}
360
409
361
- // StateAtHeight returns a confirmed state at height -- archive mode
362
- func (sdb * stateDB ) StateAtHeight (height uint64 , s interface {}, opts ... protocol.StateOption ) error {
363
- return ErrNotSupported
364
- }
365
-
366
- // StatesAtHeight returns a set states in the state factory at height -- archive mode
367
- func (sdb * stateDB ) StatesAtHeight (height uint64 , opts ... protocol.StateOption ) (state.Iterator , error ) {
368
- return nil , errors .Wrap (ErrNotSupported , "state db does not support archive mode" )
369
- }
370
-
371
410
// ReadView reads the view
372
411
func (sdb * stateDB ) ReadView (name string ) (interface {}, error ) {
373
412
return sdb .protocolView .Read (name )
374
413
}
375
414
376
415
//======================================
377
- // private trie constructor functions
416
+ // private statedb functions
378
417
//======================================
379
418
380
419
func (sdb * stateDB ) flusherOptions (preEaster bool ) []db.KVStoreFlusherOption {
381
420
opts := []db.KVStoreFlusherOption {
382
421
db .SerializeOption (func (wi * batch.WriteInfo ) []byte {
422
+ if sdb .versioned {
423
+ // current height is moved to another namespace
424
+ // transform it back for the purpose of calculating digest
425
+ wi = sdb .transCurrentHeight (wi )
426
+ }
383
427
if preEaster {
384
428
return wi .SerializeWithoutWriteType ()
385
429
}
@@ -397,8 +441,8 @@ func (sdb *stateDB) flusherOptions(preEaster bool) []db.KVStoreFlusherOption {
397
441
)
398
442
}
399
443
400
- func (sdb * stateDB ) state (ns string , addr []byte , s interface {}) error {
401
- data , err := sdb .dao .Get (ns , addr )
444
+ func (sdb * stateDB ) state (h uint64 , ns string , addr []byte , s interface {}) error {
445
+ data , err := sdb .DAO ( h ) .Get (ns , addr )
402
446
if err != nil {
403
447
if errors .Cause (err ) == db .ErrNotExist {
404
448
return errors .Wrapf (state .ErrStateNotExist , "state of %x doesn't exist" , addr )
0 commit comments