github.com/iotexproject/iotex-core@v1.14.1-rc1/state/factory/factory.go (about) 1 // Copyright (c) 2022 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 "github.com/prometheus/client_golang/prometheus" 17 "go.uber.org/zap" 18 19 "github.com/iotexproject/go-pkgs/cache" 20 "github.com/iotexproject/go-pkgs/hash" 21 "github.com/iotexproject/iotex-address/address" 22 23 "github.com/iotexproject/iotex-core/action" 24 "github.com/iotexproject/iotex-core/action/protocol" 25 "github.com/iotexproject/iotex-core/action/protocol/execution/evm" 26 "github.com/iotexproject/iotex-core/action/protocol/staking" 27 "github.com/iotexproject/iotex-core/actpool" 28 "github.com/iotexproject/iotex-core/blockchain" 29 "github.com/iotexproject/iotex-core/blockchain/block" 30 "github.com/iotexproject/iotex-core/blockchain/genesis" 31 "github.com/iotexproject/iotex-core/db" 32 "github.com/iotexproject/iotex-core/db/batch" 33 "github.com/iotexproject/iotex-core/db/trie" 34 "github.com/iotexproject/iotex-core/pkg/lifecycle" 35 "github.com/iotexproject/iotex-core/pkg/log" 36 "github.com/iotexproject/iotex-core/pkg/prometheustimer" 37 "github.com/iotexproject/iotex-core/pkg/tracer" 38 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 39 "github.com/iotexproject/iotex-core/state" 40 ) 41 42 const ( 43 // AccountKVNamespace is the bucket name for account 44 AccountKVNamespace = "Account" 45 // ArchiveNamespacePrefix is the prefix of the buckets storing history data 46 ArchiveNamespacePrefix = "Archive" 47 // CurrentHeightKey indicates the key of current factory height in underlying DB 48 CurrentHeightKey = "currentHeight" 49 // ArchiveTrieNamespace is the bucket for the latest state view 50 ArchiveTrieNamespace = "AccountTrie" 51 // ArchiveTrieRootKey indicates the key of accountTrie root hash in underlying DB 52 ArchiveTrieRootKey = "archiveTrieRoot" 53 ) 54 55 var ( 56 // ErrNotSupported is the error that the statedb is not for archive mode 57 ErrNotSupported = errors.New("not supported") 58 // ErrNoArchiveData is the error that the node have no archive data 59 ErrNoArchiveData = errors.New("no archive data") 60 61 _dbBatchSizelMtc = prometheus.NewGaugeVec( 62 prometheus.GaugeOpts{ 63 Name: "iotex_db_batch_size", 64 Help: "DB batch size", 65 }, 66 []string{}, 67 ) 68 69 //DefaultConfig is the default config for state factory 70 DefaultConfig = Config{ 71 Chain: blockchain.DefaultConfig, 72 Genesis: genesis.Default, 73 } 74 ) 75 76 func init() { 77 prometheus.MustRegister(_dbBatchSizelMtc) 78 } 79 80 type ( 81 // Factory defines an interface for managing states 82 Factory interface { 83 lifecycle.StartStopper 84 protocol.StateReader 85 Register(protocol.Protocol) error 86 Validate(context.Context, *block.Block) error 87 // NewBlockBuilder creates block builder 88 NewBlockBuilder(context.Context, actpool.ActPool, func(action.Envelope) (*action.SealedEnvelope, error)) (*block.Builder, error) 89 SimulateExecution(context.Context, address.Address, *action.Execution) ([]byte, *action.Receipt, error) 90 ReadContractStorage(context.Context, address.Address, []byte) ([]byte, error) 91 PutBlock(context.Context, *block.Block) error 92 DeleteTipBlock(context.Context, *block.Block) error 93 StateAtHeight(uint64, interface{}, ...protocol.StateOption) error 94 StatesAtHeight(uint64, ...protocol.StateOption) (state.Iterator, error) 95 } 96 97 // factory implements StateFactory interface, tracks changes to account/contract and batch-commits to DB 98 factory struct { 99 lifecycle lifecycle.Lifecycle 100 mutex sync.RWMutex 101 cfg Config 102 registry *protocol.Registry 103 currentChainHeight uint64 104 saveHistory bool 105 twoLayerTrie trie.TwoLayerTrie // global state trie, this is a read only trie 106 dao db.KVStore // the underlying DB for account/contract storage 107 timerFactory *prometheustimer.TimerFactory 108 workingsets cache.LRUCache // lru cache for workingsets 109 protocolView protocol.View 110 skipBlockValidationOnPut bool 111 ps *patchStore 112 } 113 114 // Config contains the config for factory 115 Config struct { 116 Chain blockchain.Config 117 Genesis genesis.Genesis 118 } 119 ) 120 121 // GenerateConfig generates the factory config 122 func GenerateConfig(chain blockchain.Config, g genesis.Genesis) Config { 123 return Config{ 124 Chain: chain, 125 Genesis: g, 126 } 127 } 128 129 // Option sets Factory construction parameter 130 type Option func(*factory, *Config) error 131 132 // RegistryOption sets the registry in state db 133 func RegistryOption(reg *protocol.Registry) Option { 134 return func(sf *factory, cfg *Config) error { 135 sf.registry = reg 136 return nil 137 } 138 } 139 140 // SkipBlockValidationOption skips block validation on PutBlock 141 func SkipBlockValidationOption() Option { 142 return func(sf *factory, cfg *Config) error { 143 sf.skipBlockValidationOnPut = true 144 return nil 145 } 146 } 147 148 // DefaultTriePatchOption loads patchs 149 func DefaultTriePatchOption() Option { 150 return func(sf *factory, cfg *Config) (err error) { 151 sf.ps, err = newPatchStore(cfg.Chain.TrieDBPatchFile) 152 return 153 } 154 } 155 156 // NewFactory creates a new state factory 157 func NewFactory(cfg Config, dao db.KVStore, opts ...Option) (Factory, error) { 158 sf := &factory{ 159 cfg: cfg, 160 currentChainHeight: 0, 161 registry: protocol.NewRegistry(), 162 saveHistory: cfg.Chain.EnableArchiveMode, 163 protocolView: protocol.View{}, 164 workingsets: cache.NewThreadSafeLruCache(int(cfg.Chain.WorkingSetCacheSize)), 165 dao: dao, 166 } 167 168 for _, opt := range opts { 169 if err := opt(sf, &cfg); err != nil { 170 log.S().Errorf("Failed to execute state factory creation option %p: %v", opt, err) 171 return nil, err 172 } 173 } 174 timerFactory, err := prometheustimer.New( 175 "iotex_statefactory_perf", 176 "Performance of state factory module", 177 []string{"topic", "chainID"}, 178 []string{"default", strconv.FormatUint(uint64(cfg.Chain.ID), 10)}, 179 ) 180 if err != nil { 181 log.L().Error("Failed to generate prometheus timer factory.", zap.Error(err)) 182 } 183 sf.timerFactory = timerFactory 184 185 return sf, nil 186 } 187 188 func (sf *factory) Start(ctx context.Context) error { 189 ctx = protocol.WithRegistry(ctx, sf.registry) 190 err := sf.dao.Start(ctx) 191 if err != nil { 192 return err 193 } 194 if sf.twoLayerTrie, err = newTwoLayerTrie(ArchiveTrieNamespace, sf.dao, ArchiveTrieRootKey, true); err != nil { 195 return errors.Wrap(err, "failed to generate accountTrie from config") 196 } 197 if err := sf.twoLayerTrie.Start(ctx); err != nil { 198 return err 199 } 200 // check factory height 201 h, err := sf.dao.Get(AccountKVNamespace, []byte(CurrentHeightKey)) 202 switch errors.Cause(err) { 203 case nil: 204 sf.currentChainHeight = byteutil.BytesToUint64(h) 205 // start all protocols 206 if sf.protocolView, err = sf.registry.StartAll(ctx, sf); err != nil { 207 return err 208 } 209 case db.ErrNotExist: 210 if err = sf.dao.Put(AccountKVNamespace, []byte(CurrentHeightKey), byteutil.Uint64ToBytes(0)); err != nil { 211 return errors.Wrap(err, "failed to init factory's height") 212 } 213 // start all protocols 214 if sf.protocolView, err = sf.registry.StartAll(ctx, sf); err != nil { 215 return err 216 } 217 ctx = protocol.WithBlockCtx( 218 ctx, 219 protocol.BlockCtx{ 220 BlockHeight: 0, 221 BlockTimeStamp: time.Unix(sf.cfg.Genesis.Timestamp, 0), 222 Producer: sf.cfg.Chain.ProducerAddress(), 223 GasLimit: sf.cfg.Genesis.BlockGasLimitByHeight(0), 224 }) 225 ctx = protocol.WithFeatureCtx(ctx) 226 // init the state factory 227 if err := sf.createGenesisStates(ctx); err != nil { 228 return errors.Wrap(err, "failed to create genesis states") 229 } 230 default: 231 return err 232 } 233 return sf.lifecycle.OnStart(ctx) 234 } 235 236 func (sf *factory) Stop(ctx context.Context) error { 237 sf.mutex.Lock() 238 defer sf.mutex.Unlock() 239 if err := sf.dao.Stop(ctx); err != nil { 240 return err 241 } 242 sf.workingsets.Clear() 243 return sf.lifecycle.OnStop(ctx) 244 } 245 246 // Height returns factory's height 247 func (sf *factory) Height() (uint64, error) { 248 sf.mutex.RLock() 249 defer sf.mutex.RUnlock() 250 height, err := sf.dao.Get(AccountKVNamespace, []byte(CurrentHeightKey)) 251 if err != nil { 252 return 0, errors.Wrap(err, "failed to get factory's height from underlying DB") 253 } 254 return byteutil.BytesToUint64(height), nil 255 } 256 257 func (sf *factory) newWorkingSet(ctx context.Context, height uint64) (*workingSet, error) { 258 span := tracer.SpanFromContext(ctx) 259 span.AddEvent("factory.newWorkingSet") 260 defer span.End() 261 262 g := genesis.MustExtractGenesisContext(ctx) 263 flusher, err := db.NewKVStoreFlusher( 264 sf.dao, 265 batch.NewCachedBatch(), 266 sf.flusherOptions(!g.IsEaster(height))..., 267 ) 268 if err != nil { 269 return nil, err 270 } 271 store, err := newFactoryWorkingSetStore(sf.protocolView, flusher) 272 if err != nil { 273 return nil, err 274 } 275 if err := store.Start(ctx); err != nil { 276 return nil, err 277 } 278 for _, p := range sf.ps.Get(height) { 279 if p.Type == _Delete { 280 if err := store.Delete(p.Namespace, p.Key); err != nil { 281 return nil, err 282 } 283 } else { 284 if err := store.Put(p.Namespace, p.Key, p.Value); err != nil { 285 return nil, err 286 } 287 } 288 } 289 290 return newWorkingSet(height, store), nil 291 } 292 293 func (sf *factory) flusherOptions(preEaster bool) []db.KVStoreFlusherOption { 294 opts := []db.KVStoreFlusherOption{ 295 db.SerializeFilterOption(func(wi *batch.WriteInfo) bool { 296 if wi.Namespace() == ArchiveTrieNamespace { 297 return true 298 } 299 if wi.Namespace() != evm.CodeKVNameSpace && wi.Namespace() != staking.CandsMapNS { 300 return false 301 } 302 return preEaster 303 }), 304 db.SerializeOption(func(wi *batch.WriteInfo) []byte { 305 if preEaster { 306 return wi.SerializeWithoutWriteType() 307 } 308 return wi.Serialize() 309 }), 310 } 311 if sf.saveHistory { 312 opts = append(opts, db.FlushTranslateOption(func(wi *batch.WriteInfo) *batch.WriteInfo { 313 if wi.WriteType() == batch.Delete && wi.Namespace() == ArchiveTrieNamespace { 314 return nil 315 } 316 return wi 317 })) 318 } 319 320 return opts 321 } 322 323 func (sf *factory) Register(p protocol.Protocol) error { 324 return p.Register(sf.registry) 325 } 326 327 func (sf *factory) Validate(ctx context.Context, blk *block.Block) error { 328 ctx = protocol.WithRegistry(ctx, sf.registry) 329 key := generateWorkingSetCacheKey(blk.Header, blk.Header.ProducerAddress()) 330 ws, isExist, err := sf.getFromWorkingSets(ctx, key) 331 if err != nil { 332 return err 333 } 334 if !isExist { 335 if err := ws.ValidateBlock(ctx, blk); err != nil { 336 return errors.Wrap(err, "failed to validate block with workingset in factory") 337 } 338 sf.putIntoWorkingSets(key, ws) 339 } 340 receipts, err := ws.Receipts() 341 if err != nil { 342 return err 343 } 344 blk.Receipts = receipts 345 return nil 346 } 347 348 // NewBlockBuilder returns block builder which hasn't been signed yet 349 func (sf *factory) NewBlockBuilder( 350 ctx context.Context, 351 ap actpool.ActPool, 352 sign func(action.Envelope) (*action.SealedEnvelope, error), 353 ) (*block.Builder, error) { 354 sf.mutex.Lock() 355 ctx = protocol.WithRegistry(ctx, sf.registry) 356 ws, err := sf.newWorkingSet(ctx, sf.currentChainHeight+1) 357 sf.mutex.Unlock() 358 if err != nil { 359 return nil, errors.Wrap(err, "Failed to obtain working set from state factory") 360 } 361 postSystemActions := make([]*action.SealedEnvelope, 0) 362 unsignedSystemActions, err := ws.generateSystemActions(ctx) 363 if err != nil { 364 return nil, err 365 } 366 for _, elp := range unsignedSystemActions { 367 se, err := sign(elp) 368 if err != nil { 369 return nil, err 370 } 371 postSystemActions = append(postSystemActions, se) 372 } 373 blkBuilder, err := ws.CreateBuilder(ctx, ap, postSystemActions, sf.cfg.Chain.AllowedBlockGasResidue) 374 if err != nil { 375 return nil, err 376 } 377 378 blkCtx := protocol.MustGetBlockCtx(ctx) 379 key := generateWorkingSetCacheKey(blkBuilder.GetCurrentBlockHeader(), blkCtx.Producer.String()) 380 sf.putIntoWorkingSets(key, ws) 381 return blkBuilder, nil 382 } 383 384 // SimulateExecution simulates a running of smart contract operation, this is done off the network since it does not 385 // cause any state change 386 func (sf *factory) SimulateExecution( 387 ctx context.Context, 388 caller address.Address, 389 ex *action.Execution, 390 ) ([]byte, *action.Receipt, error) { 391 ctx, span := tracer.NewSpan(ctx, "factory.SimulateExecution") 392 defer span.End() 393 394 sf.mutex.Lock() 395 ws, err := sf.newWorkingSet(ctx, sf.currentChainHeight+1) 396 sf.mutex.Unlock() 397 if err != nil { 398 return nil, nil, errors.Wrap(err, "failed to obtain working set from state factory") 399 } 400 401 return evm.SimulateExecution(ctx, ws, caller, ex) 402 } 403 404 // ReadContractStorage reads contract's storage 405 func (sf *factory) ReadContractStorage(ctx context.Context, contract address.Address, key []byte) ([]byte, error) { 406 sf.mutex.Lock() 407 ws, err := sf.newWorkingSet(ctx, sf.currentChainHeight+1) 408 sf.mutex.Unlock() 409 if err != nil { 410 return nil, errors.Wrap(err, "failed to generate working set from state factory") 411 } 412 return evm.ReadContractStorage(ctx, ws, contract, key) 413 } 414 415 // PutBlock persists all changes in RunActions() into the DB 416 func (sf *factory) PutBlock(ctx context.Context, blk *block.Block) error { 417 sf.mutex.Lock() 418 timer := sf.timerFactory.NewTimer("Commit") 419 sf.mutex.Unlock() 420 defer timer.End() 421 producer := blk.PublicKey().Address() 422 if producer == nil { 423 return errors.New("failed to get address") 424 } 425 g := genesis.MustExtractGenesisContext(ctx) 426 ctx = protocol.WithBlockCtx( 427 protocol.WithRegistry(ctx, sf.registry), 428 protocol.BlockCtx{ 429 BlockHeight: blk.Height(), 430 BlockTimeStamp: blk.Timestamp(), 431 GasLimit: g.BlockGasLimitByHeight(blk.Height()), 432 Producer: producer, 433 }, 434 ) 435 ctx = protocol.WithFeatureCtx(ctx) 436 key := generateWorkingSetCacheKey(blk.Header, blk.Header.ProducerAddress()) 437 ws, isExist, err := sf.getFromWorkingSets(ctx, key) 438 if err != nil { 439 return err 440 } 441 if !isExist { 442 // regenerate workingset 443 if !sf.skipBlockValidationOnPut { 444 err = ws.ValidateBlock(ctx, blk) 445 } else { 446 err = ws.Process(ctx, blk.RunnableActions().Actions()) 447 } 448 if err != nil { 449 log.L().Error("Failed to update state.", zap.Error(err)) 450 return err 451 } 452 } 453 sf.mutex.Lock() 454 defer sf.mutex.Unlock() 455 receipts, err := ws.Receipts() 456 if err != nil { 457 return err 458 } 459 blk.Receipts = receipts 460 h, _ := ws.Height() 461 if sf.currentChainHeight+1 != h { 462 // another working set with correct version already committed, do nothing 463 return fmt.Errorf( 464 "current state height %d + 1 doesn't match working set height %d", 465 sf.currentChainHeight, h, 466 ) 467 } 468 469 if err := ws.Commit(ctx); err != nil { 470 return err 471 } 472 rh, err := sf.dao.Get(ArchiveTrieNamespace, []byte(ArchiveTrieRootKey)) 473 if err != nil { 474 return err 475 } 476 if err := sf.twoLayerTrie.SetRootHash(rh); err != nil { 477 return err 478 } 479 sf.currentChainHeight = h 480 481 return nil 482 } 483 484 func (sf *factory) DeleteTipBlock(_ context.Context, _ *block.Block) error { 485 return errors.Wrap(ErrNotSupported, "cannot delete tip block from factory") 486 } 487 488 // StateAtHeight returns a confirmed state at height -- archive mode 489 func (sf *factory) StateAtHeight(height uint64, s interface{}, opts ...protocol.StateOption) error { 490 sf.mutex.RLock() 491 defer sf.mutex.RUnlock() 492 cfg, err := processOptions(opts...) 493 if err != nil { 494 return err 495 } 496 if cfg.Keys != nil { 497 return errors.Wrap(ErrNotSupported, "Read state with keys option has not been implemented yet") 498 } 499 if height > sf.currentChainHeight { 500 return errors.Errorf("query height %d is higher than tip height %d", height, sf.currentChainHeight) 501 } 502 return sf.stateAtHeight(height, cfg.Namespace, cfg.Key, s) 503 } 504 505 // StatesAtHeight returns a set states in the state factory at height -- archive mode 506 func (sf *factory) StatesAtHeight(height uint64, opts ...protocol.StateOption) (state.Iterator, error) { 507 sf.mutex.RLock() 508 defer sf.mutex.RUnlock() 509 if height > sf.currentChainHeight { 510 return nil, errors.Errorf("query height %d is higher than tip height %d", height, sf.currentChainHeight) 511 } 512 return nil, errors.Wrap(ErrNotSupported, "Read historical states has not been implemented yet") 513 } 514 515 // State returns a confirmed state in the state factory 516 func (sf *factory) State(s interface{}, opts ...protocol.StateOption) (uint64, error) { 517 sf.mutex.RLock() 518 defer sf.mutex.RUnlock() 519 cfg, err := processOptions(opts...) 520 if err != nil { 521 return 0, err 522 } 523 if cfg.Keys != nil { 524 return 0, errors.Wrap(ErrNotSupported, "Read state with keys option has not been implemented yet") 525 } 526 value, err := sf.dao.Get(cfg.Namespace, cfg.Key) 527 if err != nil { 528 if errors.Cause(err) == db.ErrNotExist { 529 return sf.currentChainHeight, errors.Wrapf(state.ErrStateNotExist, "failed to get state of ns = %x and key = %x", cfg.Namespace, cfg.Key) 530 } 531 return sf.currentChainHeight, err 532 } 533 534 return sf.currentChainHeight, state.Deserialize(s, value) 535 } 536 537 // State returns a set states in the state factory 538 func (sf *factory) States(opts ...protocol.StateOption) (uint64, state.Iterator, error) { 539 sf.mutex.RLock() 540 defer sf.mutex.RUnlock() 541 cfg, err := processOptions(opts...) 542 if err != nil { 543 return 0, nil, err 544 } 545 if cfg.Key != nil { 546 return sf.currentChainHeight, nil, errors.Wrap(ErrNotSupported, "Read states with key option has not been implemented yet") 547 } 548 values, err := readStates(sf.dao, cfg.Namespace, cfg.Keys) 549 if err != nil { 550 return 0, nil, err 551 } 552 553 return sf.currentChainHeight, state.NewIterator(values), nil 554 } 555 556 // ReadView reads the view 557 func (sf *factory) ReadView(name string) (interface{}, error) { 558 return sf.protocolView.Read(name) 559 } 560 561 //====================================== 562 // private trie constructor functions 563 //====================================== 564 565 func (sf *factory) rootHash() ([]byte, error) { 566 return sf.twoLayerTrie.RootHash() 567 } 568 569 func namespaceKey(ns string) []byte { 570 h := hash.Hash160b([]byte(ns)) 571 return h[:] 572 } 573 574 func readState(tlt trie.TwoLayerTrie, ns string, key []byte) ([]byte, error) { 575 ltKey := toLegacyKey(key) 576 data, err := tlt.Get(namespaceKey(ns), ltKey) 577 if err != nil { 578 if errors.Cause(err) == trie.ErrNotExist { 579 return nil, errors.Wrapf(state.ErrStateNotExist, "failed to get state of ns = %x and key = %x", ns, key) 580 } 581 return nil, err 582 } 583 584 return data, nil 585 } 586 587 func toLegacyKey(input []byte) []byte { 588 key := hash.Hash160b(input) 589 return key[:] 590 } 591 592 func legacyKeyLen() int { 593 return 20 594 } 595 596 func (sf *factory) stateAtHeight(height uint64, ns string, key []byte, s interface{}) error { 597 if !sf.saveHistory { 598 return ErrNoArchiveData 599 } 600 tlt, err := newTwoLayerTrie(ArchiveTrieNamespace, sf.dao, fmt.Sprintf("%s-%d", ArchiveTrieRootKey, height), false) 601 if err != nil { 602 return errors.Wrapf(err, "failed to generate trie for %d", height) 603 } 604 if err := tlt.Start(context.Background()); err != nil { 605 return err 606 } 607 defer tlt.Stop(context.Background()) 608 609 value, err := readState(tlt, ns, key) 610 if err != nil { 611 return err 612 } 613 return state.Deserialize(s, value) 614 } 615 616 func (sf *factory) createGenesisStates(ctx context.Context) error { 617 ws, err := sf.newWorkingSet(ctx, 0) 618 if err != nil { 619 return errors.Wrap(err, "failed to obtain working set from state factory") 620 } 621 // add Genesis states 622 if err := ws.CreateGenesisStates(ctx); err != nil { 623 return err 624 } 625 626 return ws.Commit(ctx) 627 } 628 629 // getFromWorkingSets returns (workingset, true) if it exists in a cache, otherwise generates new workingset and return (ws, false) 630 func (sf *factory) getFromWorkingSets(ctx context.Context, key hash.Hash256) (*workingSet, bool, error) { 631 sf.mutex.RLock() 632 defer sf.mutex.RUnlock() 633 if data, ok := sf.workingsets.Get(key); ok { 634 if ws, ok := data.(*workingSet); ok { 635 // if it is already validated, return workingset 636 return ws, true, nil 637 } 638 return nil, false, errors.New("type assertion failed to be WorkingSet") 639 } 640 ws, err := sf.newWorkingSet(ctx, sf.currentChainHeight+1) 641 if err != nil { 642 return nil, false, errors.Wrap(err, "failed to obtain working set from state factory") 643 } 644 return ws, false, nil 645 } 646 647 func (sf *factory) putIntoWorkingSets(key hash.Hash256, ws *workingSet) { 648 sf.mutex.Lock() 649 defer sf.mutex.Unlock() 650 sf.workingsets.Add(key, ws) 651 }