github.com/iotexproject/iotex-core@v1.14.1-rc1/state/factory/statedb.go (about) 1 // Copyright (c) 2020 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package factory 7 8 import ( 9 "context" 10 "fmt" 11 "strconv" 12 "sync" 13 "time" 14 15 "github.com/pkg/errors" 16 "go.uber.org/zap" 17 18 "github.com/iotexproject/go-pkgs/cache" 19 "github.com/iotexproject/go-pkgs/hash" 20 "github.com/iotexproject/iotex-address/address" 21 22 "github.com/iotexproject/iotex-core/action" 23 "github.com/iotexproject/iotex-core/action/protocol" 24 "github.com/iotexproject/iotex-core/action/protocol/execution/evm" 25 "github.com/iotexproject/iotex-core/action/protocol/staking" 26 "github.com/iotexproject/iotex-core/actpool" 27 "github.com/iotexproject/iotex-core/blockchain/block" 28 "github.com/iotexproject/iotex-core/blockchain/genesis" 29 "github.com/iotexproject/iotex-core/db" 30 "github.com/iotexproject/iotex-core/db/batch" 31 "github.com/iotexproject/iotex-core/pkg/log" 32 "github.com/iotexproject/iotex-core/pkg/prometheustimer" 33 "github.com/iotexproject/iotex-core/pkg/tracer" 34 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 35 "github.com/iotexproject/iotex-core/state" 36 ) 37 38 // stateDB implements StateFactory interface, tracks changes to account/contract and batch-commits to DB 39 type stateDB struct { 40 mutex sync.RWMutex 41 currentChainHeight uint64 42 cfg Config 43 registry *protocol.Registry 44 dao db.KVStore // the underlying DB for account/contract storage 45 timerFactory *prometheustimer.TimerFactory 46 workingsets cache.LRUCache // lru cache for workingsets 47 protocolView protocol.View 48 skipBlockValidationOnPut bool 49 ps *patchStore 50 } 51 52 // StateDBOption sets stateDB construction parameter 53 type StateDBOption func(*stateDB, *Config) error 54 55 // DefaultPatchOption loads patchs 56 func DefaultPatchOption() StateDBOption { 57 return func(sdb *stateDB, cfg *Config) (err error) { 58 sdb.ps, err = newPatchStore(cfg.Chain.TrieDBPatchFile) 59 return 60 } 61 } 62 63 // RegistryStateDBOption sets the registry in state db 64 func RegistryStateDBOption(reg *protocol.Registry) StateDBOption { 65 return func(sdb *stateDB, cfg *Config) error { 66 sdb.registry = reg 67 return nil 68 } 69 } 70 71 // SkipBlockValidationStateDBOption skips block validation on PutBlock 72 func SkipBlockValidationStateDBOption() StateDBOption { 73 return func(sdb *stateDB, cfg *Config) error { 74 sdb.skipBlockValidationOnPut = true 75 return nil 76 } 77 } 78 79 // DisableWorkingSetCacheOption disable workingset cache 80 func DisableWorkingSetCacheOption() StateDBOption { 81 return func(sdb *stateDB, cfg *Config) error { 82 sdb.workingsets = cache.NewDummyLruCache() 83 return nil 84 } 85 } 86 87 // NewStateDB creates a new state db 88 func NewStateDB(cfg Config, dao db.KVStore, opts ...StateDBOption) (Factory, error) { 89 sdb := stateDB{ 90 cfg: cfg, 91 currentChainHeight: 0, 92 registry: protocol.NewRegistry(), 93 protocolView: protocol.View{}, 94 workingsets: cache.NewThreadSafeLruCache(int(cfg.Chain.WorkingSetCacheSize)), 95 dao: dao, 96 } 97 for _, opt := range opts { 98 if err := opt(&sdb, &cfg); err != nil { 99 log.S().Errorf("Failed to execute state factory creation option %p: %v", opt, err) 100 return nil, err 101 } 102 } 103 timerFactory, err := prometheustimer.New( 104 "iotex_statefactory_perf", 105 "Performance of state factory module", 106 []string{"topic", "chainID"}, 107 []string{"default", strconv.FormatUint(uint64(cfg.Chain.ID), 10)}, 108 ) 109 if err != nil { 110 log.L().Error("Failed to generate prometheus timer factory.", zap.Error(err)) 111 } 112 sdb.timerFactory = timerFactory 113 return &sdb, nil 114 } 115 116 func (sdb *stateDB) Start(ctx context.Context) error { 117 ctx = protocol.WithRegistry(ctx, sdb.registry) 118 if err := sdb.dao.Start(ctx); err != nil { 119 return err 120 } 121 // check factory height 122 h, err := sdb.dao.Get(AccountKVNamespace, []byte(CurrentHeightKey)) 123 switch errors.Cause(err) { 124 case nil: 125 sdb.currentChainHeight = byteutil.BytesToUint64(h) 126 // start all protocols 127 if sdb.protocolView, err = sdb.registry.StartAll(ctx, sdb); err != nil { 128 return err 129 } 130 case db.ErrNotExist: 131 sdb.currentChainHeight = 0 132 if err = sdb.dao.Put(AccountKVNamespace, []byte(CurrentHeightKey), byteutil.Uint64ToBytes(0)); err != nil { 133 return errors.Wrap(err, "failed to init statedb's height") 134 } 135 // start all protocols 136 if sdb.protocolView, err = sdb.registry.StartAll(ctx, sdb); err != nil { 137 return err 138 } 139 ctx = protocol.WithBlockCtx( 140 ctx, 141 protocol.BlockCtx{ 142 BlockHeight: 0, 143 BlockTimeStamp: time.Unix(sdb.cfg.Genesis.Timestamp, 0), 144 Producer: sdb.cfg.Chain.ProducerAddress(), 145 GasLimit: sdb.cfg.Genesis.BlockGasLimitByHeight(0), 146 }) 147 ctx = protocol.WithFeatureCtx(ctx) 148 // init the state factory 149 if err = sdb.createGenesisStates(ctx); err != nil { 150 return errors.Wrap(err, "failed to create genesis states") 151 } 152 default: 153 return err 154 } 155 156 return nil 157 } 158 159 func (sdb *stateDB) Stop(ctx context.Context) error { 160 sdb.mutex.Lock() 161 defer sdb.mutex.Unlock() 162 sdb.workingsets.Clear() 163 return sdb.dao.Stop(ctx) 164 } 165 166 // Height returns factory's height 167 func (sdb *stateDB) Height() (uint64, error) { 168 sdb.mutex.RLock() 169 defer sdb.mutex.RUnlock() 170 height, err := sdb.dao.Get(AccountKVNamespace, []byte(CurrentHeightKey)) 171 if err != nil { 172 return 0, errors.Wrap(err, "failed to get factory's height from underlying DB") 173 } 174 return byteutil.BytesToUint64(height), nil 175 } 176 177 func (sdb *stateDB) newWorkingSet(ctx context.Context, height uint64) (*workingSet, error) { 178 g := genesis.MustExtractGenesisContext(ctx) 179 flusher, err := db.NewKVStoreFlusher( 180 sdb.dao, 181 batch.NewCachedBatch(), 182 sdb.flusherOptions(!g.IsEaster(height))..., 183 ) 184 if err != nil { 185 return nil, err 186 } 187 for _, p := range sdb.ps.Get(height) { 188 if p.Type == _Delete { 189 flusher.KVStoreWithBuffer().MustDelete(p.Namespace, p.Key) 190 } else { 191 flusher.KVStoreWithBuffer().MustPut(p.Namespace, p.Key, p.Value) 192 } 193 } 194 store := newStateDBWorkingSetStore(sdb.protocolView, flusher, g.IsNewfoundland(height)) 195 if err := store.Start(ctx); err != nil { 196 return nil, err 197 } 198 199 return newWorkingSet(height, store), nil 200 } 201 202 func (sdb *stateDB) Register(p protocol.Protocol) error { 203 return p.Register(sdb.registry) 204 } 205 206 func (sdb *stateDB) Validate(ctx context.Context, blk *block.Block) error { 207 ctx = protocol.WithRegistry(ctx, sdb.registry) 208 key := generateWorkingSetCacheKey(blk.Header, blk.Header.ProducerAddress()) 209 ws, isExist, err := sdb.getFromWorkingSets(ctx, key) 210 if err != nil { 211 return err 212 } 213 if !isExist { 214 if err = ws.ValidateBlock(ctx, blk); err != nil { 215 return errors.Wrap(err, "failed to validate block with workingset in statedb") 216 } 217 sdb.workingsets.Add(key, ws) 218 } 219 receipts, err := ws.Receipts() 220 if err != nil { 221 return err 222 } 223 blk.Receipts = receipts 224 return nil 225 } 226 227 // NewBlockBuilder returns block builder which hasn't been signed yet 228 func (sdb *stateDB) NewBlockBuilder( 229 ctx context.Context, 230 ap actpool.ActPool, 231 sign func(action.Envelope) (*action.SealedEnvelope, error), 232 ) (*block.Builder, error) { 233 ctx = protocol.WithRegistry(ctx, sdb.registry) 234 sdb.mutex.RLock() 235 currHeight := sdb.currentChainHeight 236 sdb.mutex.RUnlock() 237 ws, err := sdb.newWorkingSet(ctx, currHeight+1) 238 if err != nil { 239 return nil, err 240 } 241 postSystemActions := make([]*action.SealedEnvelope, 0) 242 unsignedSystemActions, err := ws.generateSystemActions(ctx) 243 if err != nil { 244 return nil, err 245 } 246 for _, elp := range unsignedSystemActions { 247 se, err := sign(elp) 248 if err != nil { 249 return nil, err 250 } 251 postSystemActions = append(postSystemActions, se) 252 } 253 blkBuilder, err := ws.CreateBuilder(ctx, ap, postSystemActions, sdb.cfg.Chain.AllowedBlockGasResidue) 254 if err != nil { 255 return nil, err 256 } 257 258 blkCtx := protocol.MustGetBlockCtx(ctx) 259 key := generateWorkingSetCacheKey(blkBuilder.GetCurrentBlockHeader(), blkCtx.Producer.String()) 260 sdb.workingsets.Add(key, ws) 261 return blkBuilder, nil 262 } 263 264 // SimulateExecution simulates a running of smart contract operation, this is done off the network since it does not 265 // cause any state change 266 func (sdb *stateDB) SimulateExecution( 267 ctx context.Context, 268 caller address.Address, 269 ex *action.Execution, 270 ) ([]byte, *action.Receipt, error) { 271 ctx, span := tracer.NewSpan(ctx, "stateDB.SimulateExecution") 272 defer span.End() 273 274 sdb.mutex.RLock() 275 currHeight := sdb.currentChainHeight 276 sdb.mutex.RUnlock() 277 ws, err := sdb.newWorkingSet(ctx, currHeight+1) 278 if err != nil { 279 return nil, nil, err 280 } 281 282 return evm.SimulateExecution(ctx, ws, caller, ex) 283 } 284 285 // ReadContractStorage reads contract's storage 286 func (sdb *stateDB) ReadContractStorage(ctx context.Context, contract address.Address, key []byte) ([]byte, error) { 287 sdb.mutex.RLock() 288 currHeight := sdb.currentChainHeight 289 sdb.mutex.RUnlock() 290 ws, err := sdb.newWorkingSet(ctx, currHeight+1) 291 if err != nil { 292 return nil, errors.Wrap(err, "failed to generate working set from state db") 293 } 294 return evm.ReadContractStorage(ctx, ws, contract, key) 295 } 296 297 // PutBlock persists all changes in RunActions() into the DB 298 func (sdb *stateDB) PutBlock(ctx context.Context, blk *block.Block) error { 299 sdb.mutex.Lock() 300 timer := sdb.timerFactory.NewTimer("Commit") 301 sdb.mutex.Unlock() 302 defer timer.End() 303 producer := blk.PublicKey().Address() 304 if producer == nil { 305 return errors.New("failed to get address") 306 } 307 g := genesis.MustExtractGenesisContext(ctx) 308 ctx = protocol.WithBlockCtx( 309 protocol.WithRegistry(ctx, sdb.registry), 310 protocol.BlockCtx{ 311 BlockHeight: blk.Height(), 312 BlockTimeStamp: blk.Timestamp(), 313 GasLimit: g.BlockGasLimitByHeight(blk.Height()), 314 Producer: producer, 315 }, 316 ) 317 ctx = protocol.WithFeatureCtx(ctx) 318 key := generateWorkingSetCacheKey(blk.Header, blk.Header.ProducerAddress()) 319 ws, isExist, err := sdb.getFromWorkingSets(ctx, key) 320 if err != nil { 321 return err 322 } 323 if !isExist { 324 if !sdb.skipBlockValidationOnPut { 325 err = ws.ValidateBlock(ctx, blk) 326 } else { 327 err = ws.Process(ctx, blk.RunnableActions().Actions()) 328 } 329 if err != nil { 330 log.L().Error("Failed to update state.", zap.Error(err)) 331 return err 332 } 333 } 334 sdb.mutex.Lock() 335 defer sdb.mutex.Unlock() 336 receipts, err := ws.Receipts() 337 if err != nil { 338 return err 339 } 340 blk.Receipts = receipts 341 h, _ := ws.Height() 342 if sdb.currentChainHeight+1 != h { 343 // another working set with correct version already committed, do nothing 344 return fmt.Errorf( 345 "current state height %d + 1 doesn't match working set height %d", 346 sdb.currentChainHeight, h, 347 ) 348 } 349 350 if err := ws.Commit(ctx); err != nil { 351 return err 352 } 353 sdb.currentChainHeight = h 354 return nil 355 } 356 357 func (sdb *stateDB) DeleteTipBlock(_ context.Context, _ *block.Block) error { 358 return errors.Wrap(ErrNotSupported, "cannot delete tip block from state db") 359 } 360 361 // State returns a confirmed state in the state factory 362 func (sdb *stateDB) State(s interface{}, opts ...protocol.StateOption) (uint64, error) { 363 cfg, err := processOptions(opts...) 364 if err != nil { 365 return 0, err 366 } 367 sdb.mutex.RLock() 368 defer sdb.mutex.RUnlock() 369 if cfg.Keys != nil { 370 return 0, errors.Wrap(ErrNotSupported, "Read state with keys option has not been implemented yet") 371 } 372 return sdb.currentChainHeight, sdb.state(cfg.Namespace, cfg.Key, s) 373 } 374 375 // State returns a set of states in the state factory 376 func (sdb *stateDB) States(opts ...protocol.StateOption) (uint64, state.Iterator, error) { 377 cfg, err := processOptions(opts...) 378 if err != nil { 379 return 0, nil, err 380 } 381 sdb.mutex.RLock() 382 defer sdb.mutex.RUnlock() 383 if cfg.Key != nil { 384 return sdb.currentChainHeight, nil, errors.Wrap(ErrNotSupported, "Read states with key option has not been implemented yet") 385 } 386 values, err := readStates(sdb.dao, cfg.Namespace, cfg.Keys) 387 if err != nil { 388 return 0, nil, err 389 } 390 391 return sdb.currentChainHeight, state.NewIterator(values), nil 392 } 393 394 // StateAtHeight returns a confirmed state at height -- archive mode 395 func (sdb *stateDB) StateAtHeight(height uint64, s interface{}, opts ...protocol.StateOption) error { 396 return ErrNotSupported 397 } 398 399 // StatesAtHeight returns a set states in the state factory at height -- archive mode 400 func (sdb *stateDB) StatesAtHeight(height uint64, opts ...protocol.StateOption) (state.Iterator, error) { 401 return nil, errors.Wrap(ErrNotSupported, "state db does not support archive mode") 402 } 403 404 // ReadView reads the view 405 func (sdb *stateDB) ReadView(name string) (interface{}, error) { 406 return sdb.protocolView.Read(name) 407 } 408 409 //====================================== 410 // private trie constructor functions 411 //====================================== 412 413 func (sdb *stateDB) flusherOptions(preEaster bool) []db.KVStoreFlusherOption { 414 opts := []db.KVStoreFlusherOption{ 415 db.SerializeOption(func(wi *batch.WriteInfo) []byte { 416 if preEaster { 417 return wi.SerializeWithoutWriteType() 418 } 419 return wi.Serialize() 420 }), 421 } 422 if !preEaster { 423 return opts 424 } 425 return append( 426 opts, 427 db.SerializeFilterOption(func(wi *batch.WriteInfo) bool { 428 return wi.Namespace() == evm.CodeKVNameSpace || wi.Namespace() == staking.CandsMapNS 429 }), 430 ) 431 } 432 433 func (sdb *stateDB) state(ns string, addr []byte, s interface{}) error { 434 data, err := sdb.dao.Get(ns, addr) 435 if err != nil { 436 if errors.Cause(err) == db.ErrNotExist { 437 return errors.Wrapf(state.ErrStateNotExist, "state of %x doesn't exist", addr) 438 } 439 return errors.Wrapf(err, "error when getting the state of %x", addr) 440 } 441 if err := state.Deserialize(s, data); err != nil { 442 return errors.Wrapf(err, "error when deserializing state data into %T", s) 443 } 444 return nil 445 } 446 447 func (sdb *stateDB) createGenesisStates(ctx context.Context) error { 448 ws, err := sdb.newWorkingSet(ctx, 0) 449 if err != nil { 450 return err 451 } 452 if err := ws.CreateGenesisStates(ctx); err != nil { 453 return err 454 } 455 456 return ws.Commit(ctx) 457 } 458 459 // getFromWorkingSets returns (workingset, true) if it exists in a cache, otherwise generates new workingset and return (ws, false) 460 func (sdb *stateDB) getFromWorkingSets(ctx context.Context, key hash.Hash256) (*workingSet, bool, error) { 461 if data, ok := sdb.workingsets.Get(key); ok { 462 if ws, ok := data.(*workingSet); ok { 463 // if it is already validated, return workingset 464 return ws, true, nil 465 } 466 return nil, false, errors.New("type assertion failed to be WorkingSet") 467 } 468 sdb.mutex.RLock() 469 currHeight := sdb.currentChainHeight 470 sdb.mutex.RUnlock() 471 tx, err := sdb.newWorkingSet(ctx, currHeight+1) 472 return tx, false, err 473 }