Skip to content

Commit 41b8931

Browse files
committed
implement block process part of light sync
1 parent 03f7b31 commit 41b8931

23 files changed

+697
-80
lines changed

cmd/evm/internal/t8ntool/execution.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
223223
statedb.AddBalance(pre.Env.Coinbase, minerReward)
224224
}
225225
// Commit block
226-
root, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
226+
root, _, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
227227
if err != nil {
228228
fmt.Fprintf(os.Stderr, "Could not commit state: %v", err)
229229
return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err))
@@ -252,7 +252,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB
252252
}
253253
}
254254
// Commit and re-open to start with a clean state.
255-
root, _ := statedb.Commit(false)
255+
root, _, _ := statedb.Commit(false)
256256
statedb, _ = state.New(root, sdb, nil)
257257
return statedb
258258
}

cmd/utils/flags.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ var (
125125
Name: "datadir.ancient",
126126
Usage: "Data directory for ancient chain segments (default = inside chaindata)",
127127
}
128+
DiffFlag = DirectoryFlag{
129+
Name: "datadir.diff",
130+
Usage: "Data directory for difflayer segments (default = inside chaindata)",
131+
}
128132
MinFreeDiskSpaceFlag = DirectoryFlag{
129133
Name: "datadir.minfreedisk",
130134
Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = --cache.gc converted to MB, 0 = disabled)",
@@ -425,6 +429,10 @@ var (
425429
Name: "cache.preimages",
426430
Usage: "Enable recording the SHA3/keccak preimages of trie keys",
427431
}
432+
PersistDiffFlag = cli.BoolFlag{
433+
Name: "persistdiff",
434+
Usage: "Enable persisting the diff layer",
435+
}
428436
// Miner settings
429437
MiningEnabledFlag = cli.BoolFlag{
430438
Name: "mine",
@@ -1564,7 +1572,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
15641572
if ctx.GlobalIsSet(AncientFlag.Name) {
15651573
cfg.DatabaseFreezer = ctx.GlobalString(AncientFlag.Name)
15661574
}
1567-
1575+
if ctx.GlobalIsSet(DiffFlag.Name) {
1576+
cfg.DatabaseDiff = ctx.GlobalString(DiffFlag.Name)
1577+
}
1578+
if ctx.GlobalIsSet(PersistDiffFlag.Name) {
1579+
cfg.PersistDiff = ctx.GlobalBool(PersistDiffFlag.Name)
1580+
}
15681581
if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
15691582
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
15701583
}

core/blockchain.go

+111-11
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,16 @@ var (
8282
const (
8383
bodyCacheLimit = 256
8484
blockCacheLimit = 256
85+
diffLayerCacheLimit = 1024
8586
receiptsCacheLimit = 10000
8687
txLookupCacheLimit = 1024
8788
maxFutureBlocks = 256
8889
maxTimeFutureBlocks = 30
89-
badBlockLimit = 10
9090
maxBeyondBlocks = 2048
9191

92+
diffLayerfreezerRecheckInterval = 3 * time.Second
93+
diffLayerfreezerBlockLimit = 864000 // The number of blocks that should be kept in disk.
94+
9295
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
9396
//
9497
// Changelog:
@@ -188,13 +191,15 @@ type BlockChain struct {
188191
currentBlock atomic.Value // Current head of the block chain
189192
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
190193

191-
stateCache state.Database // State database to reuse between imports (contains state cache)
192-
bodyCache *lru.Cache // Cache for the most recent block bodies
193-
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
194-
receiptsCache *lru.Cache // Cache for the most recent receipts per block
195-
blockCache *lru.Cache // Cache for the most recent entire blocks
196-
txLookupCache *lru.Cache // Cache for the most recent transaction lookup data.
197-
futureBlocks *lru.Cache // future blocks are blocks added for later processing
194+
stateCache state.Database // State database to reuse between imports (contains state cache)
195+
bodyCache *lru.Cache // Cache for the most recent block bodies
196+
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
197+
receiptsCache *lru.Cache // Cache for the most recent receipts per block
198+
blockCache *lru.Cache // Cache for the most recent entire blocks
199+
txLookupCache *lru.Cache // Cache for the most recent transaction lookup data.
200+
futureBlocks *lru.Cache // future blocks are blocks added for later processing
201+
diffLayerCache *lru.Cache // Cache for the diffLayers
202+
diffQueue *prque.Prque // A Priority queue to store recent diff layer
198203

199204
quit chan struct{} // blockchain quit channel
200205
wg sync.WaitGroup // chain processing wait group for shutting down
@@ -226,6 +231,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
226231
blockCache, _ := lru.New(blockCacheLimit)
227232
txLookupCache, _ := lru.New(txLookupCacheLimit)
228233
futureBlocks, _ := lru.New(maxFutureBlocks)
234+
diffLayerCache, _ := lru.New(diffLayerCacheLimit)
229235

230236
bc := &BlockChain{
231237
chainConfig: chainConfig,
@@ -244,10 +250,12 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
244250
bodyRLPCache: bodyRLPCache,
245251
receiptsCache: receiptsCache,
246252
blockCache: blockCache,
253+
diffLayerCache: diffLayerCache,
247254
txLookupCache: txLookupCache,
248255
futureBlocks: futureBlocks,
249256
engine: engine,
250257
vmConfig: vmConfig,
258+
diffQueue: prque.New(nil),
251259
}
252260
bc.validator = NewBlockValidator(chainConfig, bc, engine)
253261
bc.processor = NewStateProcessor(chainConfig, bc, engine)
@@ -396,6 +404,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
396404
triedb.SaveCachePeriodically(bc.cacheConfig.TrieCleanJournal, bc.cacheConfig.TrieCleanRejournal, bc.quit)
397405
}()
398406
}
407+
// Need persist and prune diff layer
408+
if bc.db.DiffStore() != nil {
409+
go bc.diffLayerFreeze()
410+
}
399411
return bc, nil
400412
}
401413

@@ -408,6 +420,14 @@ func (bc *BlockChain) CacheReceipts(hash common.Hash, receipts types.Receipts) {
408420
bc.receiptsCache.Add(hash, receipts)
409421
}
410422

423+
func (bc *BlockChain) CacheDiffLayer(hash common.Hash, num uint64, diffLayer *types.DiffLayer) {
424+
bc.diffLayerCache.Add(hash, diffLayer)
425+
if bc.db.DiffStore() != nil {
426+
// push to priority queue before persisting
427+
bc.diffQueue.Push(diffLayer, -(int64(num)))
428+
}
429+
}
430+
411431
func (bc *BlockChain) CacheBlock(hash common.Hash, block *types.Block) {
412432
bc.blockCache.Add(hash, block)
413433
}
@@ -1506,10 +1526,19 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
15061526
wg.Done()
15071527
}()
15081528
// Commit all cached state changes into underlying memory database.
1509-
root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
1529+
root, diffLayer, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
15101530
if err != nil {
15111531
return NonStatTy, err
15121532
}
1533+
1534+
// Ensure no empty block body
1535+
if diffLayer != nil && block.Header().TxHash != types.EmptyRootHash {
1536+
// Filling necessary field
1537+
diffLayer.Receipts = receipts
1538+
diffLayer.StateRoot = root
1539+
diffLayer.Hash = block.Hash()
1540+
bc.CacheDiffLayer(diffLayer.Hash, block.Number().Uint64(), diffLayer)
1541+
}
15131542
triedb := bc.stateCache.TrieDB()
15141543

15151544
// If we're running an archive node, always flush
@@ -1895,8 +1924,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
18951924
bc.reportBlock(block, receipts, err)
18961925
return it.index, err
18971926
}
1898-
bc.CacheReceipts(block.Hash(), receipts)
1899-
bc.CacheBlock(block.Hash(), block)
19001927
// Update the metrics touched during block processing
19011928
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them
19021929
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them
@@ -1916,6 +1943,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
19161943
log.Error("validate state failed", "error", err)
19171944
return it.index, err
19181945
}
1946+
bc.CacheReceipts(block.Hash(), receipts)
1947+
bc.CacheBlock(block.Hash(), block)
19191948
proctime := time.Since(start)
19201949

19211950
// Update the metrics touched during block validation
@@ -2292,6 +2321,77 @@ func (bc *BlockChain) update() {
22922321
}
22932322
}
22942323

2324+
func (bc *BlockChain) diffLayerFreeze() {
2325+
recheck := time.Tick(diffLayerfreezerRecheckInterval)
2326+
for {
2327+
select {
2328+
case <-bc.quit:
2329+
// Persist all diffLayers when shutdown, it will introduce redundant storage, but it is acceptable.
2330+
// If the client been ungracefully shutdown, it will missing all cached diff layers, it is acceptable as well.
2331+
var batch ethdb.Batch
2332+
for !bc.diffQueue.Empty() {
2333+
diff, _ := bc.diffQueue.Pop()
2334+
diffLayer := diff.(*types.DiffLayer)
2335+
if batch == nil {
2336+
batch = bc.db.DiffStore().NewBatch()
2337+
}
2338+
rawdb.WriteDiffLayer(batch, diffLayer.Hash, diffLayer)
2339+
if batch.ValueSize() > ethdb.IdealBatchSize {
2340+
if err := batch.Write(); err != nil {
2341+
log.Error("Failed to write diff layer", "err", err)
2342+
return
2343+
}
2344+
batch.Reset()
2345+
}
2346+
}
2347+
if batch != nil {
2348+
if err := batch.Write(); err != nil {
2349+
log.Error("Failed to write diff layer", "err", err)
2350+
return
2351+
}
2352+
batch.Reset()
2353+
}
2354+
return
2355+
case <-recheck:
2356+
currentHeight := bc.CurrentBlock().NumberU64()
2357+
var batch ethdb.Batch
2358+
for !bc.diffQueue.Empty() {
2359+
diff, prio := bc.diffQueue.Pop()
2360+
diffLayer := diff.(*types.DiffLayer)
2361+
2362+
// if the block old enough
2363+
if int64(currentHeight)+prio > int64(bc.triesInMemory) {
2364+
canonicalHash := bc.GetCanonicalHash(uint64(-prio))
2365+
// on the canonical chain
2366+
if canonicalHash == diffLayer.Hash {
2367+
if batch == nil {
2368+
batch = bc.db.DiffStore().NewBatch()
2369+
}
2370+
rawdb.WriteDiffLayer(batch, diffLayer.Hash, diffLayer)
2371+
staleHash := bc.GetCanonicalHash(uint64(-prio) - diffLayerfreezerBlockLimit)
2372+
rawdb.DeleteDiffLayer(batch, staleHash)
2373+
}
2374+
} else {
2375+
bc.diffQueue.Push(diffLayer, prio)
2376+
break
2377+
}
2378+
if batch != nil && batch.ValueSize() > ethdb.IdealBatchSize {
2379+
if err := batch.Write(); err != nil {
2380+
panic(fmt.Sprintf("Failed to write diff layer, error %v", err))
2381+
}
2382+
batch.Reset()
2383+
}
2384+
}
2385+
if batch != nil {
2386+
if err := batch.Write(); err != nil {
2387+
panic(fmt.Sprintf("Failed to write diff layer, error %v", err))
2388+
}
2389+
batch.Reset()
2390+
}
2391+
}
2392+
}
2393+
}
2394+
22952395
// maintainTxIndex is responsible for the construction and deletion of the
22962396
// transaction index.
22972397
//

core/chain_makers.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
223223
block, _, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
224224

225225
// Write state changes to db
226-
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
226+
root, _, err := statedb.Commit(config.IsEIP158(b.header.Number))
227227
if err != nil {
228228
panic(fmt.Sprintf("state write error: %v", err))
229229
}

core/rawdb/accessors_chain.go

+38
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,44 @@ func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *t
447447
WriteBodyRLP(db, hash, number, data)
448448
}
449449

450+
func WriteDiffLayer(db ethdb.KeyValueWriter, hash common.Hash, layer *types.DiffLayer) {
451+
data, err := rlp.EncodeToBytes(layer)
452+
if err != nil {
453+
log.Crit("Failed to RLP encode diff layer", "err", err)
454+
}
455+
WriteDiffLayerRLP(db, hash, data)
456+
}
457+
458+
func WriteDiffLayerRLP(db ethdb.KeyValueWriter, hash common.Hash, rlp rlp.RawValue) {
459+
if err := db.Put(diffLayerKey(hash), rlp); err != nil {
460+
log.Crit("Failed to store block body", "err", err)
461+
}
462+
}
463+
464+
func ReadDiffLayer(db ethdb.Reader, hash common.Hash) *types.DiffLayer {
465+
data := ReadDiffLayerRLP(db, hash)
466+
if len(data) == 0 {
467+
return nil
468+
}
469+
diff := new(types.DiffLayer)
470+
if err := rlp.Decode(bytes.NewReader(data), diff); err != nil {
471+
log.Error("Invalid diff layer RLP", "hash", hash, "err", err)
472+
return nil
473+
}
474+
return diff
475+
}
476+
477+
func ReadDiffLayerRLP(db ethdb.Reader, hash common.Hash) rlp.RawValue {
478+
data, _ := db.Get(diffLayerKey(hash))
479+
return data
480+
}
481+
482+
func DeleteDiffLayer(db ethdb.KeyValueWriter, hash common.Hash) {
483+
if err := db.Delete(diffLayerKey(hash)); err != nil {
484+
log.Crit("Failed to delete diffLayer", "err", err)
485+
}
486+
}
487+
450488
// DeleteBody removes all block body data associated with a hash.
451489
func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
452490
if err := db.Delete(blockBodyKey(number, hash)); err != nil {

core/rawdb/database.go

+25
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
type freezerdb struct {
3737
ethdb.KeyValueStore
3838
ethdb.AncientStore
39+
diffStore ethdb.KeyValueStore
3940
}
4041

4142
// Close implements io.Closer, closing both the fast key-value store as well as
@@ -48,12 +49,28 @@ func (frdb *freezerdb) Close() error {
4849
if err := frdb.KeyValueStore.Close(); err != nil {
4950
errs = append(errs, err)
5051
}
52+
if frdb.diffStore != nil {
53+
if err := frdb.diffStore.Close(); err != nil {
54+
errs = append(errs, err)
55+
}
56+
}
5157
if len(errs) != 0 {
5258
return fmt.Errorf("%v", errs)
5359
}
5460
return nil
5561
}
5662

63+
func (frdb *freezerdb) DiffStore() ethdb.KeyValueStore {
64+
return frdb.diffStore
65+
}
66+
67+
func (frdb *freezerdb) SetDiffStore(diff ethdb.KeyValueStore) {
68+
if frdb.diffStore != nil {
69+
frdb.diffStore.Close()
70+
}
71+
frdb.diffStore = diff
72+
}
73+
5774
// Freeze is a helper method used for external testing to trigger and block until
5875
// a freeze cycle completes, without having to sleep for a minute to trigger the
5976
// automatic background run.
@@ -114,6 +131,14 @@ func (db *nofreezedb) Sync() error {
114131
return errNotSupported
115132
}
116133

134+
func (db *nofreezedb) DiffStore() ethdb.KeyValueStore {
135+
return nil
136+
}
137+
138+
func (db *nofreezedb) SetDiffStore(diff ethdb.KeyValueStore) {
139+
panic("not implement")
140+
}
141+
117142
// NewDatabase creates a high level database on top of a given key-value data
118143
// store without a freezer moving immutable chain segments into cold storage.
119144
func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {

core/rawdb/schema.go

+8
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ var (
9090
SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value
9191
CodePrefix = []byte("c") // CodePrefix + code hash -> account code
9292

93+
// difflayer database
94+
diffLayerPrefix = []byte("d") // diffLayerPrefix + hash -> diffLayer
95+
9396
preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
9497
configPrefix = []byte("ethereum-config-") // config prefix for the db
9598

@@ -177,6 +180,11 @@ func blockReceiptsKey(number uint64, hash common.Hash) []byte {
177180
return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
178181
}
179182

183+
// diffLayerKey = diffLayerKeyPrefix + hash
184+
func diffLayerKey(hash common.Hash) []byte {
185+
return append(append(diffLayerPrefix, hash.Bytes()...))
186+
}
187+
180188
// txLookupKey = txLookupPrefix + hash
181189
func txLookupKey(hash common.Hash) []byte {
182190
return append(txLookupPrefix, hash.Bytes()...)

0 commit comments

Comments
 (0)