-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathblockchain.go
3324 lines (3060 loc) · 126 KB
/
blockchain.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package core implements the Ethereum consensus protocol.
package core
import (
"errors"
"fmt"
"io"
"math/big"
"runtime"
"slices"
"sort"
"sync"
"sync/atomic"
"time"
mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/monitor"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/systemcontracts"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/syncx"
"github.com/ethereum/go-ethereum/internal/version"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb"
"github.com/ethereum/go-ethereum/triedb/pathdb"
exlru "github.com/hashicorp/golang-lru"
"golang.org/x/crypto/sha3"
)
var (
badBlockRecords = mapset.NewSet[common.Hash]()
badBlockRecordslimit = 1000
badBlockGauge = metrics.NewRegisteredGauge("chain/insert/badBlock", nil)
headBlockGauge = metrics.NewRegisteredGauge("chain/head/block", nil)
headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil)
headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil)
justifiedBlockGauge = metrics.NewRegisteredGauge("chain/head/justified", nil)
finalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
blockInsertMgaspsGauge = metrics.NewRegisteredGauge("chain/insert/mgasps", nil)
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil)
accountCommitTimer = metrics.NewRegisteredTimer("chain/account/commits", nil)
storageReadTimer = metrics.NewRegisteredTimer("chain/storage/reads", nil)
storageUpdateTimer = metrics.NewRegisteredTimer("chain/storage/updates", nil)
storageCommitTimer = metrics.NewRegisteredTimer("chain/storage/commits", nil)
accountReadSingleTimer = metrics.NewRegisteredTimer("chain/account/single/reads", nil)
storageReadSingleTimer = metrics.NewRegisteredTimer("chain/storage/single/reads", nil)
snapshotCommitTimer = metrics.NewRegisteredTimer("chain/snapshot/commits", nil)
triedbCommitTimer = metrics.NewRegisteredTimer("chain/triedb/commits", nil)
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
blockCrossValidationTimer = metrics.NewRegisteredTimer("chain/crossvalidation", nil)
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil)
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
blockRecvTimeDiffGauge = metrics.NewRegisteredGauge("chain/block/recvtimediff", nil)
errInsertionInterrupted = errors.New("insertion is interrupted")
errChainStopped = errors.New("blockchain is stopped")
errInvalidOldChain = errors.New("invalid old chain")
errInvalidNewChain = errors.New("invalid new chain")
)
const (
bodyCacheLimit = 256
blockCacheLimit = 256
diffLayerCacheLimit = 1024
receiptsCacheLimit = 10000
sidecarsCacheLimit = 1024
txLookupCacheLimit = 1024
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
maxBeyondBlocks = 2048
prefetchTxNumber = 100
diffLayerFreezerRecheckInterval = 3 * time.Second
maxDiffForkDist = 11 // Maximum allowed backward distance from the chain head
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
//
// Changelog:
//
// - Version 4
// The following incompatible database changes were added:
// * the `BlockNumber`, `TxHash`, `TxIndex`, `BlockHash` and `Index` fields of log are deleted
// * the `Bloom` field of receipt is deleted
// * the `BlockIndex` and `TxIndex` fields of txlookup are deleted
//
// - Version 5
// The following incompatible database changes were added:
// * the `TxHash`, `GasCost`, and `ContractAddress` fields are no longer stored for a receipt
// * the `TxHash`, `GasCost`, and `ContractAddress` fields are computed by looking up the
// receipts' corresponding block
//
// - Version 6
// The following incompatible database changes were added:
// * Transaction lookup information stores the corresponding block number instead of block hash
//
// - Version 7
// The following incompatible database changes were added:
// * Use freezer as the ancient database to maintain all ancient data
//
// - Version 8
// The following incompatible database changes were added:
// * New scheme for contract code in order to separate the codes and trie nodes
//
// - Version 9
// The following incompatible database changes were added:
// * The metadata structure of freezer is changed by adding 'flushOffset'
BlockChainVersion uint64 = 9
)
// CacheConfig contains the configuration values for the trie database
// and state snapshot these are resident in a blockchain.
type CacheConfig struct {
TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory
TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
Preimages bool // Whether to store preimage of trie key to the disk
TriesInMemory uint64 // How many tries keeps in memory
NoTries bool // Insecure settings. Do not have any tries in databases if enabled.
StateHistory uint64 // Number of blocks from head whose state histories are reserved.
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
PathSyncFlush bool // Whether sync flush the trienodebuffer of pathdb to disk.
JournalFilePath string
JournalFile bool
SnapshotNoBuild bool // Whether the background generation is allowed
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
}
// triedbConfig derives the configures for trie database.
func (c *CacheConfig) triedbConfig(isVerkle bool) *triedb.Config {
config := &triedb.Config{
Cache: c.TrieCleanLimit,
Preimages: c.Preimages,
NoTries: c.NoTries,
IsVerkle: isVerkle,
}
if c.StateScheme == rawdb.HashScheme {
config.HashDB = &hashdb.Config{
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
}
}
if c.StateScheme == rawdb.PathScheme {
config.PathDB = &pathdb.Config{
SyncFlush: c.PathSyncFlush,
StateHistory: c.StateHistory,
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
WriteBufferSize: c.TrieDirtyLimit * 1024 * 1024,
JournalFilePath: c.JournalFilePath,
JournalFile: c.JournalFile,
}
}
return config
}
// defaultCacheConfig are the default caching values if none are specified by the
// user (also used during testing).
var defaultCacheConfig = &CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256,
TriesInMemory: 128,
SnapshotWait: true,
StateScheme: rawdb.HashScheme,
}
// DefaultCacheConfigWithScheme returns a deep copied default cache config with
// a provided trie node scheme.
func DefaultCacheConfigWithScheme(scheme string) *CacheConfig {
config := *defaultCacheConfig
config.StateScheme = scheme
return &config
}
type BlockChainOption func(*BlockChain) (*BlockChain, error)
// txLookup is wrapper over transaction lookup along with the corresponding
// transaction object.
type txLookup struct {
lookup *rawdb.LegacyTxLookupEntry
transaction *types.Transaction
}
// BlockChain represents the canonical chain given a database with a genesis
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
//
// Importing blocks in to the block chain happens according to the set of rules
// defined by the two stage Validator. Processing of blocks is done using the
// Processor which processes the included transaction. The validation of the state
// is done in the second part of the Validator. Failing results in aborting of
// the import.
//
// The BlockChain also helps in returning blocks from **any** chain included
// in the database as well as blocks that represents the canonical chain. It's
// important to note that GetBlock can return any block and does not need to be
// included in the canonical one where as GetBlockByNumber always represents the
// canonical chain.
type BlockChain struct {
chainConfig *params.ChainConfig // Chain & network configuration
cacheConfig *CacheConfig // Cache configuration for pruning
db ethdb.Database // Low level persistent database to store final content in
snaps *snapshot.Tree // Snapshot tree for fast trie leaf access
triegc *prque.Prque[int64, common.Hash] // Priority queue mapping block numbers to tries to gc
gcproc time.Duration // Accumulates canonical block processing for trie dumping
lastWrite uint64 // Last block when the state was flushed
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
triedb *triedb.Database // The database handler for maintaining trie nodes.
statedb *state.CachingDB // State database to reuse between imports (contains state cache)
triesInMemory uint64
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
hc *HeaderChain
rmLogsFeed event.Feed
chainFeed event.Feed
chainHeadFeed event.Feed
chainBlockFeed event.Feed
logsFeed event.Feed
blockProcFeed event.Feed
finalizedHeaderFeed event.Feed
highestVerifiedBlockFeed event.Feed
scope event.SubscriptionScope
genesisBlock *types.Block
// This mutex synchronizes chain write operations.
// Readers don't need to take it, they can just read the database.
chainmu *syncx.ClosableMutex
highestVerifiedHeader atomic.Pointer[types.Header]
highestVerifiedBlock atomic.Pointer[types.Header]
currentBlock atomic.Pointer[types.Header] // Current head of the chain
currentSnapBlock atomic.Pointer[types.Header] // Current head of snap-sync
currentFinalBlock atomic.Pointer[types.Header] // Latest (consensus) finalized block
chasingHead atomic.Pointer[types.Header]
bodyCache *lru.Cache[common.Hash, *types.Body]
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
blockCache *lru.Cache[common.Hash, *types.Block]
txLookupLock sync.RWMutex
txLookupCache *lru.Cache[common.Hash, txLookup]
sidecarsCache *lru.Cache[common.Hash, types.BlobSidecars]
// future blocks are blocks added for later processing
futureBlocks *lru.Cache[common.Hash, *types.Block]
// trusted diff layers
diffLayerCache *exlru.Cache // Cache for the diffLayers
diffLayerChanCache *exlru.Cache // Cache for the difflayer channel
diffQueue *prque.Prque[int64, *types.DiffLayer] // A Priority queue to store recent diff layer
diffQueueBuffer chan *types.DiffLayer
diffLayerFreezerBlockLimit uint64
wg sync.WaitGroup
dbWg sync.WaitGroup
quit chan struct{} // shutdown signal, closed in Stop.
stopping atomic.Bool // false if chain is running, true when stopped
procInterrupt atomic.Bool // interrupt signaler for block processing
engine consensus.Engine
prefetcher Prefetcher
validator Validator // Block and state validator interface
processor Processor // Block transaction processor interface
forker *ForkChoice
vmConfig vm.Config
// monitor
doubleSignMonitor *monitor.DoubleSignMonitor
logger *tracing.Hooks
}
// NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialises the default Ethereum Validator and
// Processor.
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis, overrides *ChainOverrides, engine consensus.Engine,
vmConfig vm.Config, shouldPreserve func(block *types.Header) bool, txLookupLimit *uint64,
options ...BlockChainOption) (*BlockChain, error) {
if cacheConfig == nil {
cacheConfig = defaultCacheConfig
}
if cacheConfig.StateScheme == rawdb.HashScheme && cacheConfig.TriesInMemory != 128 {
log.Warn("TriesInMemory isn't the default value (128), you need specify the same TriesInMemory when pruning data",
"triesInMemory", cacheConfig.TriesInMemory, "scheme", cacheConfig.StateScheme)
}
diffLayerCache, _ := exlru.New(diffLayerCacheLimit)
diffLayerChanCache, _ := exlru.New(diffLayerCacheLimit)
// Open trie database with provided config
enableVerkle, err := EnableVerkleAtGenesis(db, genesis)
if err != nil {
return nil, err
}
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(enableVerkle))
// Write the supplied genesis to the database if it has not been initialized
// yet. The corresponding chain config will be returned, either from the
// provided genesis or from the locally stored configuration if the genesis
// has already been initialized.
chainConfig, genesisHash, compatErr, err := SetupGenesisBlockWithOverride(db, triedb, genesis, overrides)
if err != nil {
return nil, err
}
systemcontracts.GenesisHash = genesisHash
log.Info("Initialised chain configuration", "config", chainConfig)
/*
log.Info("")
log.Info(strings.Repeat("-", 153))
for _, line := range strings.Split(chainConfig.Description(), "\n") {
log.Info(line)
}
log.Info(strings.Repeat("-", 153))
log.Info("")
*/
bc := &BlockChain{
chainConfig: chainConfig,
cacheConfig: cacheConfig,
db: db,
triedb: triedb,
triegc: prque.New[int64, common.Hash](nil),
quit: make(chan struct{}),
triesInMemory: cacheConfig.TriesInMemory,
chainmu: syncx.NewClosableMutex(),
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
sidecarsCache: lru.NewCache[common.Hash, types.BlobSidecars](sidecarsCacheLimit),
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit),
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
diffLayerCache: diffLayerCache,
diffLayerChanCache: diffLayerChanCache,
engine: engine,
vmConfig: vmConfig,
diffQueue: prque.New[int64, *types.DiffLayer](nil),
diffQueueBuffer: make(chan *types.DiffLayer),
logger: vmConfig.Tracer,
}
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped)
if err != nil {
return nil, err
}
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
bc.forker = NewForkChoice(bc, shouldPreserve)
bc.statedb = state.NewDatabase(bc.triedb, nil)
bc.validator = NewBlockValidator(chainConfig, bc)
bc.prefetcher = NewStatePrefetcher(chainConfig, bc.hc)
bc.processor = NewStateProcessor(chainConfig, bc.hc)
bc.genesisBlock = bc.GetBlockByNumber(0)
if bc.genesisBlock == nil {
return nil, ErrNoGenesis
}
bc.highestVerifiedHeader.Store(nil)
bc.highestVerifiedBlock.Store(nil)
bc.currentBlock.Store(nil)
bc.currentSnapBlock.Store(nil)
bc.chasingHead.Store(nil)
// Update chain info data metrics
chainInfoGauge.Update(metrics.GaugeInfoValue{"chain_id": bc.chainConfig.ChainID.String()})
// If Geth is initialized with an external ancient store, re-initialize the
// missing chain indexes and chain flags. This procedure can survive crash
// and can be resumed in next restart since chain flags are updated in last step.
if bc.empty() {
rawdb.InitDatabaseFromFreezer(bc.db)
}
// Load blockchain states from disk
if err := bc.loadLastState(); err != nil {
return nil, err
}
// Make sure the state associated with the block is available, or log out
// if there is no available state, waiting for state sync.
head := bc.CurrentBlock()
if !bc.HasState(head.Root) {
if head.Number.Uint64() == 0 {
// The genesis state is missing, which is only possible in the path-based
// scheme. This situation occurs when the initial state sync is not finished
// yet, or the chain head is rewound below the pivot point. In both scenarios,
// there is no possible recovery approach except for rerunning a snap sync.
// Do nothing here until the state syncer picks it up.
log.Info("Genesis state is missing, wait state sync")
} else {
// Head state is missing, before the state recovery, find out the
// disk layer point of snapshot(if it's enabled). Make sure the
// rewound point is lower than disk layer.
var diskRoot common.Hash
if bc.cacheConfig.SnapshotLimit > 0 {
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
log.Debug("Head state missing, ReadSnapshotRoot", "snap root", diskRoot)
}
if bc.triedb.Scheme() == rawdb.PathScheme && !bc.NoTries() {
recoverable, _ := bc.triedb.Recoverable(diskRoot)
if !bc.HasState(diskRoot) && !recoverable {
diskRoot = bc.triedb.Head()
}
log.Debug("Head state missing, check recoverable", "disk root", diskRoot, "recoverable", recoverable)
}
if diskRoot != (common.Hash{}) {
log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash(), "diskRoot", diskRoot)
snapDisk, err := bc.setHeadBeyondRoot(head.Number.Uint64(), 0, diskRoot, true)
if err != nil {
return nil, err
}
// Chain rewound, persist old snapshot number to indicate recovery procedure
if snapDisk != 0 {
rawdb.WriteSnapshotRecoveryNumber(bc.db, snapDisk)
}
} else {
log.Warn("Head state missing, repairing", "number", head.Number, "hash", head.Hash())
if _, err := bc.setHeadBeyondRoot(head.Number.Uint64(), 0, common.Hash{}, true); err != nil {
return nil, err
}
}
}
}
// Ensure that a previous crash in SetHead doesn't leave extra ancients
if frozen, err := bc.db.BlockStore().ItemAmountInAncient(); err == nil && frozen > 0 {
frozen, err = bc.db.BlockStore().Ancients()
if err != nil {
return nil, err
}
var (
needRewind bool
low uint64
)
// The head full block may be rolled back to a very low height due to
// blockchain repair. If the head full block is even lower than the ancient
// chain, truncate the ancient store.
fullBlock := bc.CurrentBlock()
if fullBlock != nil && fullBlock.Hash() != bc.genesisBlock.Hash() && fullBlock.Number.Uint64() < frozen-1 {
needRewind = true
low = fullBlock.Number.Uint64()
}
// In snap sync, it may happen that ancient data has been written to the
// ancient store, but the LastFastBlock has not been updated, truncate the
// extra data here.
snapBlock := bc.CurrentSnapBlock()
if snapBlock != nil && snapBlock.Number.Uint64() < frozen-1 {
needRewind = true
if snapBlock.Number.Uint64() < low || low == 0 {
low = snapBlock.Number.Uint64()
}
}
if needRewind {
log.Error("Truncating ancient chain", "from", bc.CurrentHeader().Number.Uint64(), "to", low)
if err := bc.SetHead(low); err != nil {
return nil, err
}
}
}
// The first thing the node will do is reconstruct the verification data for
// the head block (ethash cache or clique voting snapshot). Might as well do
// it in advance.
bc.engine.VerifyHeader(bc, bc.CurrentHeader())
if bc.logger != nil && bc.logger.OnBlockchainInit != nil {
bc.logger.OnBlockchainInit(chainConfig)
}
if bc.logger != nil && bc.logger.OnGenesisBlock != nil {
if block := bc.CurrentBlock(); block.Number.Uint64() == 0 {
alloc, err := getGenesisState(bc.db, block.Hash())
if err != nil {
return nil, fmt.Errorf("failed to get genesis state: %w", err)
}
if alloc == nil {
return nil, errors.New("live blockchain tracer requires genesis alloc to be set")
}
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
}
}
// Load any existing snapshot, regenerating it if loading failed
if bc.cacheConfig.SnapshotLimit > 0 {
// If the chain was rewound past the snapshot persistent layer (causing
// a recovery block number to be persisted to disk), check if we're still
// in recovery mode and in that case, don't invalidate the snapshot on a
// head mismatch.
var recover bool
head := bc.CurrentBlock()
if layer := rawdb.ReadSnapshotRecoveryNumber(bc.db); layer != nil && *layer >= head.Number.Uint64() {
log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer)
recover = true
}
snapconfig := snapshot.Config{
CacheSize: bc.cacheConfig.SnapshotLimit,
Recovery: recover,
NoBuild: bc.cacheConfig.SnapshotNoBuild,
AsyncBuild: !bc.cacheConfig.SnapshotWait,
}
bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.triedb, head.Root, int(bc.cacheConfig.TriesInMemory), bc.NoTries())
// Re-initialize the state database with snapshot
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
}
// do options before start any routine
for _, option := range options {
bc, err = option(bc)
if err != nil {
return nil, err
}
}
// Start future block processor.
bc.wg.Add(1)
go bc.updateFutureBlocks()
// Need persist and prune diff layer
if bc.db.DiffStore() != nil {
bc.wg.Add(1)
go bc.trustedDiffLayerLoop()
}
if bc.doubleSignMonitor != nil {
bc.wg.Add(1)
go bc.startDoubleSignMonitor()
}
// Rewind the chain in case of an incompatible config upgrade.
if compatErr != nil {
log.Warn("Rewinding chain to upgrade configuration", "err", compatErr)
if compatErr.RewindToTime > 0 {
bc.SetHeadWithTimestamp(compatErr.RewindToTime)
} else {
bc.SetHead(compatErr.RewindToBlock)
}
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
}
// Start tx indexer if it's enabled.
if txLookupLimit != nil {
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
}
return bc, nil
}
// GetVMConfig returns the block chain VM config.
func (bc *BlockChain) GetVMConfig() *vm.Config {
return &bc.vmConfig
}
func (bc *BlockChain) NoTries() bool {
return bc.statedb.NoTries()
}
func (bc *BlockChain) cacheReceipts(hash common.Hash, receipts types.Receipts, block *types.Block) {
// TODO, This is a hot fix for the block hash of logs is `0x0000000000000000000000000000000000000000000000000000000000000000` for system tx
// Please check details in https://github.com/bnb-chain/bsc/issues/443
// This is a temporary fix, the official fix should be a hard fork.
const possibleSystemReceipts = 3 // One slash tx, two reward distribute txs.
numOfReceipts := len(receipts)
for i := numOfReceipts - 1; i >= 0 && i >= numOfReceipts-possibleSystemReceipts; i-- {
for j := 0; j < len(receipts[i].Logs); j++ {
receipts[i].Logs[j].BlockHash = hash
}
}
txs := block.Transactions()
if len(txs) != len(receipts) {
log.Warn("transaction and receipt count mismatch")
return
}
blockBaseFee := block.BaseFee()
if blockBaseFee == nil {
blockBaseFee = big.NewInt(0)
}
for i, receipt := range receipts {
receipt.EffectiveGasPrice = big.NewInt(0).Add(blockBaseFee, txs[i].EffectiveGasTipValue(blockBaseFee))
if receipt.Logs == nil {
receipt.Logs = []*types.Log{}
}
}
bc.receiptsCache.Add(hash, receipts)
}
func (bc *BlockChain) cacheDiffLayer(diffLayer *types.DiffLayer, diffLayerCh chan struct{}) {
// The difflayer in the system is stored by the map structure,
// so it will be out of order.
// It must be sorted first and then cached,
// otherwise the DiffHash calculated by different nodes will be inconsistent
sort.SliceStable(diffLayer.Codes, func(i, j int) bool {
return diffLayer.Codes[i].Hash.Hex() < diffLayer.Codes[j].Hash.Hex()
})
sort.SliceStable(diffLayer.Destructs, func(i, j int) bool {
return diffLayer.Destructs[i].Hex() < (diffLayer.Destructs[j].Hex())
})
sort.SliceStable(diffLayer.Accounts, func(i, j int) bool {
return diffLayer.Accounts[i].Account.Hex() < diffLayer.Accounts[j].Account.Hex()
})
sort.SliceStable(diffLayer.Storages, func(i, j int) bool {
return diffLayer.Storages[i].Account.Hex() < diffLayer.Storages[j].Account.Hex()
})
for index := range diffLayer.Storages {
// Sort keys and vals by key.
sort.Sort(&diffLayer.Storages[index])
}
if bc.diffLayerCache.Len() >= diffLayerCacheLimit {
bc.diffLayerCache.RemoveOldest()
}
bc.diffLayerCache.Add(diffLayer.BlockHash, diffLayer)
close(diffLayerCh)
if bc.db.DiffStore() != nil {
// push to priority queue before persisting
bc.diffQueueBuffer <- diffLayer
}
}
// empty returns an indicator whether the blockchain is empty.
// Note, it's a special case that we connect a non-empty ancient
// database with an empty node, so that we can plugin the ancient
// into node seamlessly.
func (bc *BlockChain) empty() bool {
genesis := bc.genesisBlock.Hash()
for _, hash := range []common.Hash{rawdb.ReadHeadBlockHash(bc.db), rawdb.ReadHeadHeaderHash(bc.db), rawdb.ReadHeadFastBlockHash(bc.db)} {
if hash != genesis {
return false
}
}
return true
}
// GetJustifiedNumber returns the highest justified blockNumber on the branch including and before `header`.
func (bc *BlockChain) GetJustifiedNumber(header *types.Header) uint64 {
if p, ok := bc.engine.(consensus.PoSA); ok {
justifiedBlockNumber, _, err := p.GetJustifiedNumberAndHash(bc, []*types.Header{header})
if err == nil {
return justifiedBlockNumber
}
}
// return 0 when err!=nil
// so the input `header` will at a disadvantage during reorg
return 0
}
// getFinalizedNumber returns the highest finalized number before the specific block.
func (bc *BlockChain) getFinalizedNumber(header *types.Header) uint64 {
if p, ok := bc.engine.(consensus.PoSA); ok {
if finalizedHeader := p.GetFinalizedHeader(bc, header); finalizedHeader != nil {
return finalizedHeader.Number.Uint64()
}
}
return 0
}
// loadLastState loads the last known chain state from the database. This method
// assumes that the chain manager mutex is held.
func (bc *BlockChain) loadLastState() error {
// Restore the last known head block
head := rawdb.ReadHeadBlockHash(bc.db)
if head == (common.Hash{}) {
// Corrupt or empty database, init from scratch
log.Warn("Empty database, resetting chain")
return bc.Reset()
}
// Make sure the entire head block is available
headBlock := bc.GetBlockByHash(head)
if headBlock == nil {
// Corrupt or empty database, init from scratch
log.Warn("Head block missing, resetting chain", "hash", head)
return bc.Reset()
}
// Everything seems to be fine, set as the head block
bc.currentBlock.Store(headBlock.Header())
headBlockGauge.Update(int64(headBlock.NumberU64()))
justifiedBlockGauge.Update(int64(bc.GetJustifiedNumber(headBlock.Header())))
finalizedBlockGauge.Update(int64(bc.getFinalizedNumber(headBlock.Header())))
// Restore the last known head header
headHeader := headBlock.Header()
if head := rawdb.ReadHeadHeaderHash(bc.db); head != (common.Hash{}) {
if header := bc.GetHeaderByHash(head); header != nil {
headHeader = header
}
}
bc.hc.SetCurrentHeader(headHeader)
// Restore the last known head snap block
bc.currentSnapBlock.Store(headBlock.Header())
headFastBlockGauge.Update(int64(headBlock.NumberU64()))
if head := rawdb.ReadHeadFastBlockHash(bc.db); head != (common.Hash{}) {
if block := bc.GetBlockByHash(head); block != nil {
bc.currentSnapBlock.Store(block.Header())
headFastBlockGauge.Update(int64(block.NumberU64()))
}
}
// Issue a status log for the user
var (
currentSnapBlock = bc.CurrentSnapBlock()
headerTd = bc.GetTd(headHeader.Hash(), headHeader.Number.Uint64())
blockTd = bc.GetTd(headBlock.Hash(), headBlock.NumberU64())
)
if headHeader.Hash() != headBlock.Hash() {
log.Info("Loaded most recent local header", "number", headHeader.Number, "hash", headHeader.Hash(), "hash", headHeader.Root, "td", headerTd, "age", common.PrettyAge(time.Unix(int64(headHeader.Time), 0)))
}
log.Info("Loaded most recent local block", "number", headBlock.Number(), "hash", headBlock.Hash(), "root", headBlock.Root(), "td", blockTd, "age", common.PrettyAge(time.Unix(int64(headBlock.Time()), 0)))
if headBlock.Hash() != currentSnapBlock.Hash() {
snapTd := bc.GetTd(currentSnapBlock.Hash(), currentSnapBlock.Number.Uint64())
log.Info("Loaded most recent local snap block", "number", currentSnapBlock.Number, "hash", currentSnapBlock.Hash(), "root", currentSnapBlock.Root, "td", snapTd, "age", common.PrettyAge(time.Unix(int64(currentSnapBlock.Time), 0)))
}
if posa, ok := bc.engine.(consensus.PoSA); ok {
if currentFinalizedHeader := posa.GetFinalizedHeader(bc, headHeader); currentFinalizedHeader != nil {
if currentFinalizedBlock := bc.GetBlockByHash(currentFinalizedHeader.Hash()); currentFinalizedBlock != nil {
finalTd := bc.GetTd(currentFinalizedBlock.Hash(), currentFinalizedBlock.NumberU64())
log.Info("Loaded most recent local finalized block", "number", currentFinalizedBlock.Number(), "hash", currentFinalizedBlock.Hash(), "root", currentFinalizedBlock.Root(), "td", finalTd, "age", common.PrettyAge(time.Unix(int64(currentFinalizedBlock.Time()), 0)))
}
}
}
if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil {
log.Info("Loaded last snap-sync pivot marker", "number", *pivot)
}
return nil
}
// SetHead rewinds the local chain to a new head. Depending on whether the node
// was snap synced or full synced and in which state, the method will try to
// delete minimal data from disk whilst retaining chain consistency.
func (bc *BlockChain) SetHead(head uint64) error {
if _, err := bc.setHeadBeyondRoot(head, 0, common.Hash{}, false); err != nil {
return err
}
// Send chain head event to update the transaction pool
header := bc.CurrentBlock()
if block := bc.GetBlock(header.Hash(), header.Number.Uint64()); block == nil {
// This should never happen. In practice, previously currentBlock
// contained the entire block whereas now only a "marker", so there
// is an ever so slight chance for a race we should handle.
log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash())
return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4])
}
bc.chainHeadFeed.Send(ChainHeadEvent{Header: header})
return nil
}
// SetHeadWithTimestamp rewinds the local chain to a new head that has at max
// the given timestamp. Depending on whether the node was snap synced or full
// synced and in which state, the method will try to delete minimal data from
// disk whilst retaining chain consistency.
func (bc *BlockChain) SetHeadWithTimestamp(timestamp uint64) error {
if _, err := bc.setHeadBeyondRoot(0, timestamp, common.Hash{}, false); err != nil {
return err
}
// Send chain head event to update the transaction pool
header := bc.CurrentBlock()
if block := bc.GetBlock(header.Hash(), header.Number.Uint64()); block == nil {
// This should never happen. In practice, previously currentBlock
// contained the entire block whereas now only a "marker", so there
// is an ever so slight chance for a race we should handle.
log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash())
return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4])
}
bc.chainHeadFeed.Send(ChainHeadEvent{Header: header})
return nil
}
// rewindHashHead implements the logic of rewindHead in the context of hash scheme.
func (bc *BlockChain) rewindHashHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
var (
limit uint64 // The oldest block that will be searched for this rewinding
beyondRoot = root == common.Hash{} // Flag whether we're beyond the requested root (no root, always true)
pivot = rawdb.ReadLastPivotNumber(bc.db) // Associated block number of pivot point state
rootNumber uint64 // Associated block number of requested root
start = time.Now() // Timestamp the rewinding is restarted
logged = time.Now() // Timestamp last progress log was printed
)
// The oldest block to be searched is determined by the pivot block or a constant
// searching threshold. The rationale behind this is as follows:
//
// - Snap sync is selected if the pivot block is available. The earliest available
// state is the pivot block itself, so there is no sense in going further back.
//
// - Full sync is selected if the pivot block does not exist. The hash database
// periodically flushes the state to disk, and the used searching threshold is
// considered sufficient to find a persistent state, even for the testnet. It
// might be not enough for a chain that is nearly empty. In the worst case,
// the entire chain is reset to genesis, and snap sync is re-enabled on top,
// which is still acceptable.
if pivot != nil {
limit = *pivot
} else if head.Number.Uint64() > params.FullImmutabilityThreshold {
limit = head.Number.Uint64() - params.FullImmutabilityThreshold
}
for {
logger := log.Trace
if time.Since(logged) > time.Second*8 {
logged = time.Now()
logger = log.Info
}
logger("Block state missing, rewinding further", "number", head.Number, "hash", head.Hash(), "elapsed", common.PrettyDuration(time.Since(start)))
// If a root threshold was requested but not yet crossed, check
if !beyondRoot && head.Root == root {
beyondRoot, rootNumber = true, head.Number.Uint64()
}
// If search limit is reached, return the genesis block as the
// new chain head.
if head.Number.Uint64() < limit {
log.Info("Rewinding limit reached, resetting to genesis", "number", head.Number, "hash", head.Hash(), "limit", limit)
return bc.genesisBlock.Header(), rootNumber
}
// If the associated state is not reachable, continue searching
// backwards until an available state is found.
if !bc.HasState(head.Root) {
// If the chain is gapped in the middle, return the genesis
// block as the new chain head.
parent := bc.GetHeader(head.ParentHash, head.Number.Uint64()-1)
if parent == nil {
log.Error("Missing block in the middle, resetting to genesis", "number", head.Number.Uint64()-1, "hash", head.ParentHash)
return bc.genesisBlock.Header(), rootNumber
}
head = parent
// If the genesis block is reached, stop searching.
if head.Number.Uint64() == 0 {
log.Info("Genesis block reached", "number", head.Number, "hash", head.Hash())
return head, rootNumber
}
continue // keep rewinding
}
// Once the available state is found, ensure that the requested root
// has already been crossed. If not, continue rewinding.
if beyondRoot || head.Number.Uint64() == 0 {
log.Info("Rewound to block with state", "number", head.Number, "hash", head.Hash())
return head, rootNumber
}
log.Debug("Skipping block with threshold state", "number", head.Number, "hash", head.Hash(), "root", head.Root)
head = bc.GetHeader(head.ParentHash, head.Number.Uint64()-1) // Keep rewinding
}
}
// rewindPathHead implements the logic of rewindHead in the context of path scheme.
func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
var (
pivot = rawdb.ReadLastPivotNumber(bc.db) // Associated block number of pivot block
rootNumber uint64 // Associated block number of requested root
// BeyondRoot represents whether the requested root is already
// crossed. The flag value is set to true if the root is empty.
beyondRoot = root == common.Hash{}
// noState represents if the target state requested for search
// is unavailable and impossible to be recovered.
noState = !bc.HasState(root) && !bc.stateRecoverable(root)
start = time.Now() // Timestamp the rewinding is restarted
logged = time.Now() // Timestamp last progress log was printed
)
// Rewind the head block tag until an available state is found.
for {
logger := log.Trace
if time.Since(logged) > time.Second*8 {
logged = time.Now()
logger = log.Info
}
logger("Block state missing, rewinding further", "number", head.Number, "hash", head.Hash(), "elapsed", common.PrettyDuration(time.Since(start)))
// If a root threshold was requested but not yet crossed, check
if !beyondRoot && head.Root == root {
beyondRoot, rootNumber = true, head.Number.Uint64()
}
// If the root threshold hasn't been crossed but the available
// state is reached, quickly determine if the target state is
// possible to be reached or not.
if !beyondRoot && noState && bc.HasState(head.Root) {
beyondRoot = true
log.Info("Disable the search for unattainable state", "root", root)
}
// Check if the associated state is available or recoverable if
// the requested root has already been crossed.
if beyondRoot && (bc.HasState(head.Root) || bc.stateRecoverable(head.Root)) {
break
}
// If pivot block is reached, return the genesis block as the
// new chain head. Theoretically there must be a persistent
// state before or at the pivot block, prevent endless rewinding
// towards the genesis just in case.
if pivot != nil && *pivot >= head.Number.Uint64() {
log.Info("Pivot block reached, resetting to genesis", "number", head.Number, "hash", head.Hash())
return bc.genesisBlock.Header(), rootNumber
}
// If the chain is gapped in the middle, return the genesis
// block as the new chain head
parent := bc.GetHeader(head.ParentHash, head.Number.Uint64()-1) // Keep rewinding
if parent == nil {
log.Error("Missing block in the middle, resetting to genesis", "number", head.Number.Uint64()-1, "hash", head.ParentHash)
return bc.genesisBlock.Header(), rootNumber
}
head = parent
// If the genesis block is reached, stop searching.
if head.Number.Uint64() == 0 {
log.Info("Genesis block reached", "number", head.Number, "hash", head.Hash())
return head, rootNumber
}
}
// Recover if the target state if it's not available yet.
if !bc.HasState(head.Root) {
if err := bc.triedb.Recover(head.Root); err != nil {
log.Crit("Failed to rollback state", "err", err)
}
}
log.Info("Rewound to block with state", "number", head.Number, "hash", head.Hash())
return head, rootNumber
}
// rewindHead searches the available states in the database and returns the associated
// block as the new head block.
//
// If the given root is not empty, then the rewind should attempt to pass the specified
// state root and return the associated block number as well. If the root, typically
// representing the state corresponding to snapshot disk layer, is deemed impassable,
// then block number zero is returned, indicating that snapshot recovery is disabled
// and the whole snapshot should be auto-generated in case of head mismatch.
func (bc *BlockChain) rewindHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
if bc.triedb.Scheme() == rawdb.PathScheme && !bc.NoTries() {
return bc.rewindPathHead(head, root)
}
return bc.rewindHashHead(head, root)
}
// SetFinalized sets the finalized block.
// This function differs slightly from Ethereum; we fine-tune it through the outer-layer setting finalizedBlockGauge.
func (bc *BlockChain) SetFinalized(header *types.Header) {
bc.currentFinalBlock.Store(header)
if header != nil {
rawdb.WriteFinalizedBlockHash(bc.db.BlockStore(), header.Hash())
} else {
rawdb.WriteFinalizedBlockHash(bc.db.BlockStore(), common.Hash{})
}
}