@ -63,6 +63,13 @@ var (
emptyCode = crypto . Keccak256 ( nil )
)
// Config includes all the configurations for pruning.
type Config struct {
Datadir string // The directory of the state database
Cachedir string // The directory of state clean cache
BloomSize uint64 // The Megabytes of memory allocated to bloom-filter
}
// Pruner is an offline tool to prune the stale state with the
// help of the snapshot. The workflow of pruner is very simple:
//
@ -75,40 +82,44 @@ var (
// periodically in order to release the disk usage and improve the
// disk read performance to some extent.
type Pruner struct {
db ethdb . Database
stateBloom * stateBloom
datadir string
trieCachePath string
headHeader * types . Header
snaptree * snapshot . Tree
config Config
chainHeader * types . Header
db ethdb . Database
stateBloom * stateBloom
snaptree * snapshot . Tree
}
// NewPruner creates the pruner instance.
func NewPruner ( db ethdb . Database , datadir , trieCachePath string , bloomSize uint64 ) ( * Pruner , error ) {
func NewPruner ( db ethdb . Database , config Config ) ( * Pruner , error ) {
headBlock := rawdb . ReadHeadBlock ( db )
if headBlock == nil {
return nil , errors . New ( "Failed to load head block" )
}
snaptree , err := snapshot . New ( db , trie . NewDatabase ( db ) , 256 , headBlock . Root ( ) , false , false , false )
snapconfig := snapshot . Config {
CacheSize : 256 ,
Recovery : false ,
NoBuild : true ,
AsyncBuild : false ,
}
snaptree , err := snapshot . New ( snapconfig , db , trie . NewDatabase ( db ) , headBlock . Root ( ) )
if err != nil {
return nil , err // The relevant snapshot(s) might not exist
}
// Sanitize the bloom filter size if it's too small.
if bloomSize < 256 {
log . Warn ( "Sanitizing bloomfilter size" , "provided(MB)" , bloomSize , "updated(MB)" , 256 )
b loomSize = 256
if config . B loomSize < 256 {
log . Warn ( "Sanitizing bloomfilter size" , "provided(MB)" , config . B loomSize, "updated(MB)" , 256 )
config . B loomSize = 256
}
stateBloom , err := newStateBloomWithSize ( b loomSize)
stateBloom , err := newStateBloomWithSize ( config . B loomSize)
if err != nil {
return nil , err
}
return & Pruner {
db : db ,
stateBloom : stateBloom ,
datadir : datadir ,
trieCachePath : trieCachePath ,
headHeader : headBlock . Header ( ) ,
snaptree : snaptree ,
config : config ,
chainHeader : headBlock . Header ( ) ,
db : db ,
stateBloom : stateBloom ,
snaptree : snaptree ,
} , nil
}
@ -236,12 +247,12 @@ func (p *Pruner) Prune(root common.Hash) error {
// reuse it for pruning instead of generating a new one. It's
// mandatory because a part of state may already be deleted,
// the recovery procedure is necessary.
_ , stateBloomRoot , err := findBloomFilter ( p . d atadir)
_ , stateBloomRoot , err := findBloomFilter ( p . config . D atadir)
if err != nil {
return err
}
if stateBloomRoot != ( common . Hash { } ) {
return RecoverPruning ( p . d atadir, p . db , p . trieCachePath )
return RecoverPruning ( p . config . D atadir, p . db , p . config . Cachedir )
}
// If the target state root is not specified, use the HEAD-127 as the
// target. The reason for picking it is:
@ -252,7 +263,7 @@ func (p *Pruner) Prune(root common.Hash) error {
// Retrieve all snapshot layers from the current HEAD.
// In theory there are 128 difflayers + 1 disk layer present,
// so 128 diff layers are expected to be returned.
layers = p . snaptree . Snapshots ( p . head Header. Root , 128 , true )
layers = p . snaptree . Snapshots ( p . chain Header. Root , 128 , true )
if len ( layers ) != 128 {
// Reject if the accumulated diff layers are less than 128. It
// means in most of normal cases, there is no associated state
@ -294,7 +305,7 @@ func (p *Pruner) Prune(root common.Hash) error {
}
} else {
if len ( layers ) > 0 {
log . Info ( "Selecting bottom-most difflayer as the pruning target" , "root" , root , "height" , p . head Header. Number . Uint64 ( ) - 127 )
log . Info ( "Selecting bottom-most difflayer as the pruning target" , "root" , root , "height" , p . chain Header. Number . Uint64 ( ) - 127 )
} else {
log . Info ( "Selecting user-specified state as the pruning target" , "root" , root )
}
@ -303,7 +314,7 @@ func (p *Pruner) Prune(root common.Hash) error {
// It's necessary otherwise in the next restart we will hit the
// deleted state root in the "clean cache" so that the incomplete
// state is picked for usage.
deleteCleanTrieCache ( p . trieCachePath )
deleteCleanTrieCache ( p . config . Cachedir )
// All the state roots of the middle layer should be forcibly pruned,
// otherwise the dangling state will be left.
@ -325,7 +336,7 @@ func (p *Pruner) Prune(root common.Hash) error {
if err := extractGenesis ( p . db , p . stateBloom ) ; err != nil {
return err
}
filterName := bloomFilterName ( p . d atadir, root )
filterName := bloomFilterName ( p . config . D atadir, root )
log . Info ( "Writing state bloom to disk" , "name" , filterName )
if err := p . stateBloom . Commit ( filterName , filterName + stateBloomFileTempSuffix ) ; err != nil {
@ -362,7 +373,13 @@ func RecoverPruning(datadir string, db ethdb.Database, trieCachePath string) err
// - The state HEAD is rewound already because of multiple incomplete `prune-state`
// In this case, even the state HEAD is not exactly matched with snapshot, it
// still feasible to recover the pruning correctly.
snaptree , err := snapshot . New ( db , trie . NewDatabase ( db ) , 256 , headBlock . Root ( ) , false , false , true )
snapconfig := snapshot . Config {
CacheSize : 256 ,
Recovery : true ,
NoBuild : true ,
AsyncBuild : false ,
}
snaptree , err := snapshot . New ( snapconfig , db , trie . NewDatabase ( db ) , headBlock . Root ( ) )
if err != nil {
return err // The relevant snapshot(s) might not exist
}