pruner.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. // Copyright 2020 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package pruner
  17. import (
  18. "bytes"
  19. "encoding/binary"
  20. "errors"
  21. "fmt"
  22. "math"
  23. "os"
  24. "path/filepath"
  25. "strings"
  26. "time"
  27. "github.com/prometheus/tsdb/fileutil"
  28. "github.com/ethereum/go-ethereum/common"
  29. "github.com/ethereum/go-ethereum/consensus"
  30. "github.com/ethereum/go-ethereum/core/rawdb"
  31. "github.com/ethereum/go-ethereum/core/state"
  32. "github.com/ethereum/go-ethereum/core/state/snapshot"
  33. "github.com/ethereum/go-ethereum/core/types"
  34. "github.com/ethereum/go-ethereum/crypto"
  35. "github.com/ethereum/go-ethereum/ethdb"
  36. "github.com/ethereum/go-ethereum/log"
  37. "github.com/ethereum/go-ethereum/node"
  38. "github.com/ethereum/go-ethereum/rlp"
  39. "github.com/ethereum/go-ethereum/trie"
  40. )
  41. const (
  42. // stateBloomFilePrefix is the filename prefix of state bloom filter.
  43. stateBloomFilePrefix = "statebloom"
  44. // stateBloomFilePrefix is the filename suffix of state bloom filter.
  45. stateBloomFileSuffix = "bf.gz"
  46. // stateBloomFileTempSuffix is the filename suffix of state bloom filter
  47. // while it is being written out to detect write aborts.
  48. stateBloomFileTempSuffix = ".tmp"
  49. // rangeCompactionThreshold is the minimal deleted entry number for
  50. // triggering range compaction. It's a quite arbitrary number but just
  51. // to avoid triggering range compaction because of small deletion.
  52. rangeCompactionThreshold = 100000
  53. )
  54. var (
  55. // emptyRoot is the known root hash of an empty trie.
  56. emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
  57. // emptyCode is the known hash of the empty EVM bytecode.
  58. emptyCode = crypto.Keccak256(nil)
  59. )
  60. // Pruner is an offline tool to prune the stale state with the
  61. // help of the snapshot. The workflow of pruner is very simple:
  62. //
  63. // - iterate the snapshot, reconstruct the relevant state
  64. // - iterate the database, delete all other state entries which
  65. // don't belong to the target state and the genesis state
  66. //
  67. // It can take several hours(around 2 hours for mainnet) to finish
  68. // the whole pruning work. It's recommended to run this offline tool
  69. // periodically in order to release the disk usage and improve the
  70. // disk read performance to some extent.
  71. type Pruner struct {
  72. db ethdb.Database
  73. stateBloom *stateBloom
  74. datadir string
  75. trieCachePath string
  76. headHeader *types.Header
  77. snaptree *snapshot.Tree
  78. triesInMemory uint64
  79. }
  80. type BlockPruner struct {
  81. db ethdb.Database
  82. oldAncientPath string
  83. newAncientPath string
  84. node *node.Node
  85. BlockAmountReserved uint64
  86. }
  87. // NewPruner creates the pruner instance.
  88. func NewPruner(db ethdb.Database, datadir, trieCachePath string, bloomSize, triesInMemory uint64) (*Pruner, error) {
  89. headBlock := rawdb.ReadHeadBlock(db)
  90. if headBlock == nil {
  91. return nil, errors.New("Failed to load head block")
  92. }
  93. snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, int(triesInMemory), headBlock.Root(), false, false, false)
  94. if err != nil {
  95. return nil, err // The relevant snapshot(s) might not exist
  96. }
  97. // Sanitize the bloom filter size if it's too small.
  98. if bloomSize < 256 {
  99. log.Warn("Sanitizing bloomfilter size", "provided(MB)", bloomSize, "updated(MB)", 256)
  100. bloomSize = 256
  101. }
  102. stateBloom, err := newStateBloomWithSize(bloomSize)
  103. if err != nil {
  104. return nil, err
  105. }
  106. return &Pruner{
  107. db: db,
  108. stateBloom: stateBloom,
  109. datadir: datadir,
  110. trieCachePath: trieCachePath,
  111. triesInMemory: triesInMemory,
  112. headHeader: headBlock.Header(),
  113. snaptree: snaptree,
  114. }, nil
  115. }
  116. func NewBlockPruner(db ethdb.Database, n *node.Node, oldAncientPath, newAncientPath string, BlockAmountReserved uint64) *BlockPruner {
  117. return &BlockPruner{
  118. db: db,
  119. oldAncientPath: oldAncientPath,
  120. newAncientPath: newAncientPath,
  121. node: n,
  122. BlockAmountReserved: BlockAmountReserved,
  123. }
  124. }
  125. func prune(snaptree *snapshot.Tree, root common.Hash, maindb ethdb.Database, stateBloom *stateBloom, bloomPath string, middleStateRoots map[common.Hash]struct{}, start time.Time) error {
  126. // Delete all stale trie nodes in the disk. With the help of state bloom
  127. // the trie nodes(and codes) belong to the active state will be filtered
  128. // out. A very small part of stale tries will also be filtered because of
  129. // the false-positive rate of bloom filter. But the assumption is held here
  130. // that the false-positive is low enough(~0.05%). The probablity of the
  131. // dangling node is the state root is super low. So the dangling nodes in
  132. // theory will never ever be visited again.
  133. var (
  134. count int
  135. size common.StorageSize
  136. pstart = time.Now()
  137. logged = time.Now()
  138. batch = maindb.NewBatch()
  139. iter = maindb.NewIterator(nil, nil)
  140. )
  141. for iter.Next() {
  142. key := iter.Key()
  143. // All state entries don't belong to specific state and genesis are deleted here
  144. // - trie node
  145. // - legacy contract code
  146. // - new-scheme contract code
  147. isCode, codeKey := rawdb.IsCodeKey(key)
  148. if len(key) == common.HashLength || isCode {
  149. checkKey := key
  150. if isCode {
  151. checkKey = codeKey
  152. }
  153. if _, exist := middleStateRoots[common.BytesToHash(checkKey)]; exist {
  154. log.Debug("Forcibly delete the middle state roots", "hash", common.BytesToHash(checkKey))
  155. } else {
  156. if ok, err := stateBloom.Contain(checkKey); err != nil {
  157. return err
  158. } else if ok {
  159. continue
  160. }
  161. }
  162. count += 1
  163. size += common.StorageSize(len(key) + len(iter.Value()))
  164. batch.Delete(key)
  165. var eta time.Duration // Realistically will never remain uninited
  166. if done := binary.BigEndian.Uint64(key[:8]); done > 0 {
  167. var (
  168. left = math.MaxUint64 - binary.BigEndian.Uint64(key[:8])
  169. speed = done/uint64(time.Since(pstart)/time.Millisecond+1) + 1 // +1s to avoid division by zero
  170. )
  171. eta = time.Duration(left/speed) * time.Millisecond
  172. }
  173. if time.Since(logged) > 8*time.Second {
  174. log.Info("Pruning state data", "nodes", count, "size", size,
  175. "elapsed", common.PrettyDuration(time.Since(pstart)), "eta", common.PrettyDuration(eta))
  176. logged = time.Now()
  177. }
  178. // Recreate the iterator after every batch commit in order
  179. // to allow the underlying compactor to delete the entries.
  180. if batch.ValueSize() >= ethdb.IdealBatchSize {
  181. batch.Write()
  182. batch.Reset()
  183. iter.Release()
  184. iter = maindb.NewIterator(nil, key)
  185. }
  186. }
  187. }
  188. if batch.ValueSize() > 0 {
  189. batch.Write()
  190. batch.Reset()
  191. }
  192. iter.Release()
  193. log.Info("Pruned state data", "nodes", count, "size", size, "elapsed", common.PrettyDuration(time.Since(pstart)))
  194. // Pruning is done, now drop the "useless" layers from the snapshot.
  195. // Firstly, flushing the target layer into the disk. After that all
  196. // diff layers below the target will all be merged into the disk.
  197. if root != snaptree.DiskRoot() {
  198. if err := snaptree.Cap(root, 0); err != nil {
  199. return err
  200. }
  201. }
  202. // Secondly, flushing the snapshot journal into the disk. All diff
  203. // layers upon are dropped silently. Eventually the entire snapshot
  204. // tree is converted into a single disk layer with the pruning target
  205. // as the root.
  206. if _, err := snaptree.Journal(root); err != nil {
  207. return err
  208. }
  209. // Delete the state bloom, it marks the entire pruning procedure is
  210. // finished. If any crashes or manual exit happens before this,
  211. // `RecoverPruning` will pick it up in the next restarts to redo all
  212. // the things.
  213. os.RemoveAll(bloomPath)
  214. // Start compactions, will remove the deleted data from the disk immediately.
  215. // Note for small pruning, the compaction is skipped.
  216. if count >= rangeCompactionThreshold {
  217. cstart := time.Now()
  218. for b := 0x00; b <= 0xf0; b += 0x10 {
  219. var (
  220. start = []byte{byte(b)}
  221. end = []byte{byte(b + 0x10)}
  222. )
  223. if b == 0xf0 {
  224. end = nil
  225. }
  226. log.Info("Compacting database", "range", fmt.Sprintf("%#x-%#x", start, end), "elapsed", common.PrettyDuration(time.Since(cstart)))
  227. if err := maindb.Compact(start, end); err != nil {
  228. log.Error("Database compaction failed", "error", err)
  229. return err
  230. }
  231. }
  232. log.Info("Database compaction finished", "elapsed", common.PrettyDuration(time.Since(cstart)))
  233. }
  234. log.Info("State pruning successful", "pruned", size, "elapsed", common.PrettyDuration(time.Since(start)))
  235. return nil
  236. }
  237. func (p *BlockPruner) backUpOldDb(name string, cache, handles int, namespace string, readonly, interrupt bool) error {
  238. // Open old db wrapper.
  239. chainDb, err := p.node.OpenDatabaseWithFreezer(name, cache, handles, p.oldAncientPath, namespace, readonly, true, interrupt)
  240. if err != nil {
  241. log.Error("Failed to open ancient database", "err=", err)
  242. return err
  243. }
  244. defer chainDb.Close()
  245. log.Info("chainDB opened successfully")
  246. // Get the number of items in old ancient db.
  247. itemsOfAncient, err := chainDb.ItemAmountInAncient()
  248. log.Info("the number of items in ancientDB is ", "itemsOfAncient", itemsOfAncient)
  249. // If we can't access the freezer or it's empty, abort.
  250. if err != nil || itemsOfAncient == 0 {
  251. log.Error("can't access the freezer or it's empty, abort")
  252. return errors.New("can't access the freezer or it's empty, abort")
  253. }
  254. // If the items in freezer is less than the block amount that we want to reserve, it is not enough, should stop.
  255. if itemsOfAncient < p.BlockAmountReserved {
  256. log.Error("the number of old blocks is not enough to reserve,", "ancient items", itemsOfAncient, "the amount specified", p.BlockAmountReserved)
  257. return errors.New("the number of old blocks is not enough to reserve")
  258. }
  259. var oldOffSet uint64
  260. if interrupt {
  261. // The interrupt scecario within this function is specific for old and new ancientDB exsisted concurrently,
  262. // should use last version of offset for oldAncientDB, because current offset is
  263. // actually of the new ancientDB_Backup, but what we want is the offset of ancientDB being backup.
  264. oldOffSet = rawdb.ReadOffSetOfLastAncientFreezer(chainDb)
  265. } else {
  266. // Using current version of ancientDB for oldOffSet because the db for backup is current version.
  267. oldOffSet = rawdb.ReadOffSetOfCurrentAncientFreezer(chainDb)
  268. }
  269. log.Info("the oldOffSet is ", "oldOffSet", oldOffSet)
  270. // Get the start BlockNumber for pruning.
  271. startBlockNumber := oldOffSet + itemsOfAncient - p.BlockAmountReserved
  272. log.Info("new offset/new startBlockNumber is ", "new offset", startBlockNumber)
  273. // Create new ancientdb backup and record the new and last version of offset in kvDB as well.
  274. // For every round, newoffset actually equals to the startBlockNumber in ancient backup db.
  275. frdbBack, err := rawdb.NewFreezerDb(chainDb, p.newAncientPath, namespace, readonly, startBlockNumber)
  276. if err != nil {
  277. log.Error("Failed to create ancient freezer backup", "err=", err)
  278. return err
  279. }
  280. defer frdbBack.Close()
  281. offsetBatch := chainDb.NewBatch()
  282. rawdb.WriteOffSetOfCurrentAncientFreezer(offsetBatch, startBlockNumber)
  283. rawdb.WriteOffSetOfLastAncientFreezer(offsetBatch, oldOffSet)
  284. if err := offsetBatch.Write(); err != nil {
  285. log.Crit("Failed to write offset into disk", "err", err)
  286. }
  287. // It's guaranteed that the old/new offsets are updated as well as the new ancientDB are created if this flock exist.
  288. lock, _, err := fileutil.Flock(filepath.Join(p.newAncientPath, "PRUNEFLOCKBACK"))
  289. if err != nil {
  290. log.Error("file lock error", "err", err)
  291. return err
  292. }
  293. log.Info("prune info", "old offset", oldOffSet, "number of items in ancientDB", itemsOfAncient, "amount to reserve", p.BlockAmountReserved)
  294. log.Info("new offset/new startBlockNumber recorded successfully ", "new offset", startBlockNumber)
  295. start := time.Now()
  296. // All ancient data after and including startBlockNumber should write into new ancientDB ancient_back.
  297. for blockNumber := startBlockNumber; blockNumber < itemsOfAncient+oldOffSet; blockNumber++ {
  298. blockHash := rawdb.ReadCanonicalHash(chainDb, blockNumber)
  299. block := rawdb.ReadBlock(chainDb, blockHash, blockNumber)
  300. receipts := rawdb.ReadRawReceipts(chainDb, blockHash, blockNumber)
  301. // Calculate the total difficulty of the block
  302. td := rawdb.ReadTd(chainDb, blockHash, blockNumber)
  303. if td == nil {
  304. return consensus.ErrUnknownAncestor
  305. }
  306. // Write into new ancient_back db.
  307. rawdb.WriteAncientBlock(frdbBack, block, receipts, td)
  308. // Print the log every 5s for better trace.
  309. if common.PrettyDuration(time.Since(start)) > common.PrettyDuration(5*time.Second) {
  310. log.Info("block backup process running successfully", "current blockNumber for backup", blockNumber)
  311. start = time.Now()
  312. }
  313. }
  314. lock.Release()
  315. log.Info("block back up done", "current start blockNumber in ancientDB", startBlockNumber)
  316. return nil
  317. }
  318. // Backup the ancient data for the old ancient db, i.e. the most recent 128 blocks in ancient db.
  319. func (p *BlockPruner) BlockPruneBackUp(name string, cache, handles int, namespace string, readonly, interrupt bool) error {
  320. start := time.Now()
  321. if err := p.backUpOldDb(name, cache, handles, namespace, readonly, interrupt); err != nil {
  322. return err
  323. }
  324. log.Info("Block pruning BackUp successfully", "time duration since start is", common.PrettyDuration(time.Since(start)))
  325. return nil
  326. }
  327. func (p *BlockPruner) RecoverInterruption(name string, cache, handles int, namespace string, readonly bool) error {
  328. log.Info("RecoverInterruption for block prune")
  329. newExist, err := CheckFileExist(p.newAncientPath)
  330. if err != nil {
  331. log.Error("newAncientDb path error")
  332. return err
  333. }
  334. if newExist {
  335. log.Info("New ancientDB_backup existed in interruption scenario")
  336. flockOfAncientBack, err := CheckFileExist(filepath.Join(p.newAncientPath, "PRUNEFLOCKBACK"))
  337. if err != nil {
  338. log.Error("Failed to check flock of ancientDB_Back %v", err)
  339. return err
  340. }
  341. // Indicating both old and new ancientDB existed concurrently.
  342. // Delete directly for the new ancientdb to prune from start, e.g.: path ../chaindb/ancient_backup
  343. if err := os.RemoveAll(p.newAncientPath); err != nil {
  344. log.Error("Failed to remove old ancient directory %v", err)
  345. return err
  346. }
  347. if flockOfAncientBack {
  348. // Indicating the oldOffset/newOffset have already been updated.
  349. if err := p.BlockPruneBackUp(name, cache, handles, namespace, readonly, true); err != nil {
  350. log.Error("Failed to prune")
  351. return err
  352. }
  353. } else {
  354. // Indicating the flock did not exist and the new offset did not be updated, so just handle this case as usual.
  355. if err := p.BlockPruneBackUp(name, cache, handles, namespace, readonly, false); err != nil {
  356. log.Error("Failed to prune")
  357. return err
  358. }
  359. }
  360. if err := p.AncientDbReplacer(); err != nil {
  361. log.Error("Failed to replace ancientDB")
  362. return err
  363. }
  364. } else {
  365. log.Info("New ancientDB_backup did not exist in interruption scenario")
  366. // Indicating new ancientDB even did not be created, just prune starting at backup from startBlockNumber as usual,
  367. // in this case, the new offset have not been written into kvDB.
  368. if err := p.BlockPruneBackUp(name, cache, handles, namespace, readonly, false); err != nil {
  369. log.Error("Failed to prune")
  370. return err
  371. }
  372. if err := p.AncientDbReplacer(); err != nil {
  373. log.Error("Failed to replace ancientDB")
  374. return err
  375. }
  376. }
  377. return nil
  378. }
  379. func CheckFileExist(path string) (bool, error) {
  380. if _, err := os.Stat(path); err != nil {
  381. if os.IsNotExist(err) {
  382. // Indicating the file didn't exist.
  383. return false, nil
  384. }
  385. return true, err
  386. }
  387. return true, nil
  388. }
  389. func (p *BlockPruner) AncientDbReplacer() error {
  390. // Delete directly for the old ancientdb, e.g.: path ../chaindb/ancient
  391. if err := os.RemoveAll(p.oldAncientPath); err != nil {
  392. log.Error("Failed to remove old ancient directory %v", err)
  393. return err
  394. }
  395. // Rename the new ancientdb path same to the old
  396. if err := os.Rename(p.newAncientPath, p.oldAncientPath); err != nil {
  397. log.Error("Failed to rename new ancient directory")
  398. return err
  399. }
  400. return nil
  401. }
  402. // Prune deletes all historical state nodes except the nodes belong to the
  403. // specified state version. If user doesn't specify the state version, use
  404. // the bottom-most snapshot diff layer as the target.
  405. func (p *Pruner) Prune(root common.Hash) error {
  406. // If the state bloom filter is already committed previously,
  407. // reuse it for pruning instead of generating a new one. It's
  408. // mandatory because a part of state may already be deleted,
  409. // the recovery procedure is necessary.
  410. _, stateBloomRoot, err := findBloomFilter(p.datadir)
  411. if err != nil {
  412. return err
  413. }
  414. if stateBloomRoot != (common.Hash{}) {
  415. return RecoverPruning(p.datadir, p.db, p.trieCachePath, p.triesInMemory)
  416. }
  417. // If the target state root is not specified, use the HEAD-(n-1) as the
  418. // target. The reason for picking it is:
  419. // - in most of the normal cases, the related state is available
  420. // - the probability of this layer being reorg is very low
  421. var layers []snapshot.Snapshot
  422. if root == (common.Hash{}) {
  423. // Retrieve all snapshot layers from the current HEAD.
  424. // In theory there are n difflayers + 1 disk layer present,
  425. // so n diff layers are expected to be returned.
  426. layers = p.snaptree.Snapshots(p.headHeader.Root, int(p.triesInMemory), true)
  427. if len(layers) != int(p.triesInMemory) {
  428. // Reject if the accumulated diff layers are less than n. It
  429. // means in most of normal cases, there is no associated state
  430. // with bottom-most diff layer.
  431. return fmt.Errorf("snapshot not old enough yet: need %d more blocks", int(p.triesInMemory)-len(layers))
  432. }
  433. // Use the bottom-most diff layer as the target
  434. root = layers[len(layers)-1].Root()
  435. }
  436. // Ensure the root is really present. The weak assumption
  437. // is the presence of root can indicate the presence of the
  438. // entire trie.
  439. if blob := rawdb.ReadTrieNode(p.db, root); len(blob) == 0 {
  440. // The special case is for clique based networks(rinkeby, goerli
  441. // and some other private networks), it's possible that two
  442. // consecutive blocks will have same root. In this case snapshot
  443. // difflayer won't be created. So HEAD-(n-1) may not paired with
  444. // head-(n-1) layer. Instead the paired layer is higher than the
  445. // bottom-most diff layer. Try to find the bottom-most snapshot
  446. // layer with state available.
  447. //
  448. // Note HEAD is ignored. Usually there is the associated
  449. // state available, but we don't want to use the topmost state
  450. // as the pruning target.
  451. var found bool
  452. for i := len(layers) - 2; i >= 1; i-- {
  453. if blob := rawdb.ReadTrieNode(p.db, layers[i].Root()); len(blob) != 0 {
  454. root = layers[i].Root()
  455. found = true
  456. log.Info("Selecting middle-layer as the pruning target", "root", root, "depth", i)
  457. break
  458. }
  459. }
  460. if !found {
  461. if blob := rawdb.ReadTrieNode(p.db, p.snaptree.DiskRoot()); len(blob) != 0 {
  462. root = p.snaptree.DiskRoot()
  463. found = true
  464. log.Info("Selecting disk-layer as the pruning target", "root", root)
  465. }
  466. }
  467. if !found {
  468. if len(layers) > 0 {
  469. return errors.New("no snapshot paired state")
  470. }
  471. return fmt.Errorf("associated state[%x] is not present", root)
  472. }
  473. } else {
  474. if len(layers) > 0 {
  475. log.Info("Selecting bottom-most difflayer as the pruning target", "root", root, "height", p.headHeader.Number.Uint64()-127)
  476. } else {
  477. log.Info("Selecting user-specified state as the pruning target", "root", root)
  478. }
  479. }
  480. // Before start the pruning, delete the clean trie cache first.
  481. // It's necessary otherwise in the next restart we will hit the
  482. // deleted state root in the "clean cache" so that the incomplete
  483. // state is picked for usage.
  484. deleteCleanTrieCache(p.trieCachePath)
  485. // All the state roots of the middle layer should be forcibly pruned,
  486. // otherwise the dangling state will be left.
  487. middleRoots := make(map[common.Hash]struct{})
  488. for _, layer := range layers {
  489. if layer.Root() == root {
  490. break
  491. }
  492. middleRoots[layer.Root()] = struct{}{}
  493. }
  494. // Traverse the target state, re-construct the whole state trie and
  495. // commit to the given bloom filter.
  496. start := time.Now()
  497. if err := snapshot.GenerateTrie(p.snaptree, root, p.db, p.stateBloom); err != nil {
  498. return err
  499. }
  500. // Traverse the genesis, put all genesis state entries into the
  501. // bloom filter too.
  502. if err := extractGenesis(p.db, p.stateBloom); err != nil {
  503. return err
  504. }
  505. filterName := bloomFilterName(p.datadir, root)
  506. log.Info("Writing state bloom to disk", "name", filterName)
  507. if err := p.stateBloom.Commit(filterName, filterName+stateBloomFileTempSuffix); err != nil {
  508. return err
  509. }
  510. log.Info("State bloom filter committed", "name", filterName)
  511. return prune(p.snaptree, root, p.db, p.stateBloom, filterName, middleRoots, start)
  512. }
  513. // RecoverPruning will resume the pruning procedure during the system restart.
  514. // This function is used in this case: user tries to prune state data, but the
  515. // system was interrupted midway because of crash or manual-kill. In this case
  516. // if the bloom filter for filtering active state is already constructed, the
  517. // pruning can be resumed. What's more if the bloom filter is constructed, the
  518. // pruning **has to be resumed**. Otherwise a lot of dangling nodes may be left
  519. // in the disk.
  520. func RecoverPruning(datadir string, db ethdb.Database, trieCachePath string, triesInMemory uint64) error {
  521. stateBloomPath, stateBloomRoot, err := findBloomFilter(datadir)
  522. if err != nil {
  523. return err
  524. }
  525. if stateBloomPath == "" {
  526. return nil // nothing to recover
  527. }
  528. headBlock := rawdb.ReadHeadBlock(db)
  529. if headBlock == nil {
  530. return errors.New("Failed to load head block")
  531. }
  532. // Initialize the snapshot tree in recovery mode to handle this special case:
  533. // - Users run the `prune-state` command multiple times
  534. // - Neither these `prune-state` running is finished(e.g. interrupted manually)
  535. // - The state bloom filter is already generated, a part of state is deleted,
  536. // so that resuming the pruning here is mandatory
  537. // - The state HEAD is rewound already because of multiple incomplete `prune-state`
  538. // In this case, even the state HEAD is not exactly matched with snapshot, it
  539. // still feasible to recover the pruning correctly.
  540. snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, int(triesInMemory), headBlock.Root(), false, false, true)
  541. if err != nil {
  542. return err // The relevant snapshot(s) might not exist
  543. }
  544. stateBloom, err := NewStateBloomFromDisk(stateBloomPath)
  545. if err != nil {
  546. return err
  547. }
  548. log.Info("Loaded state bloom filter", "path", stateBloomPath)
  549. // Before start the pruning, delete the clean trie cache first.
  550. // It's necessary otherwise in the next restart we will hit the
  551. // deleted state root in the "clean cache" so that the incomplete
  552. // state is picked for usage.
  553. deleteCleanTrieCache(trieCachePath)
  554. // All the state roots of the middle layers should be forcibly pruned,
  555. // otherwise the dangling state will be left.
  556. var (
  557. found bool
  558. layers = snaptree.Snapshots(headBlock.Root(), int(triesInMemory), true)
  559. middleRoots = make(map[common.Hash]struct{})
  560. )
  561. for _, layer := range layers {
  562. if layer.Root() == stateBloomRoot {
  563. found = true
  564. break
  565. }
  566. middleRoots[layer.Root()] = struct{}{}
  567. }
  568. if !found {
  569. log.Error("Pruning target state is not existent")
  570. return errors.New("non-existent target state")
  571. }
  572. return prune(snaptree, stateBloomRoot, db, stateBloom, stateBloomPath, middleRoots, time.Now())
  573. }
  574. // extractGenesis loads the genesis state and commits all the state entries
  575. // into the given bloomfilter.
  576. func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
  577. genesisHash := rawdb.ReadCanonicalHash(db, 0)
  578. if genesisHash == (common.Hash{}) {
  579. return errors.New("missing genesis hash")
  580. }
  581. genesis := rawdb.ReadBlock(db, genesisHash, 0)
  582. if genesis == nil {
  583. return errors.New("missing genesis block")
  584. }
  585. t, err := trie.NewSecure(genesis.Root(), trie.NewDatabase(db))
  586. if err != nil {
  587. return err
  588. }
  589. accIter := t.NodeIterator(nil)
  590. for accIter.Next(true) {
  591. hash := accIter.Hash()
  592. // Embedded nodes don't have hash.
  593. if hash != (common.Hash{}) {
  594. stateBloom.Put(hash.Bytes(), nil)
  595. }
  596. // If it's a leaf node, yes we are touching an account,
  597. // dig into the storage trie further.
  598. if accIter.Leaf() {
  599. var acc state.Account
  600. if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
  601. return err
  602. }
  603. if acc.Root != emptyRoot {
  604. storageTrie, err := trie.NewSecure(acc.Root, trie.NewDatabase(db))
  605. if err != nil {
  606. return err
  607. }
  608. storageIter := storageTrie.NodeIterator(nil)
  609. for storageIter.Next(true) {
  610. hash := storageIter.Hash()
  611. if hash != (common.Hash{}) {
  612. stateBloom.Put(hash.Bytes(), nil)
  613. }
  614. }
  615. if storageIter.Error() != nil {
  616. return storageIter.Error()
  617. }
  618. }
  619. if !bytes.Equal(acc.CodeHash, emptyCode) {
  620. stateBloom.Put(acc.CodeHash, nil)
  621. }
  622. }
  623. }
  624. return accIter.Error()
  625. }
  626. func bloomFilterName(datadir string, hash common.Hash) string {
  627. return filepath.Join(datadir, fmt.Sprintf("%s.%s.%s", stateBloomFilePrefix, hash.Hex(), stateBloomFileSuffix))
  628. }
  629. func isBloomFilter(filename string) (bool, common.Hash) {
  630. filename = filepath.Base(filename)
  631. if strings.HasPrefix(filename, stateBloomFilePrefix) && strings.HasSuffix(filename, stateBloomFileSuffix) {
  632. return true, common.HexToHash(filename[len(stateBloomFilePrefix)+1 : len(filename)-len(stateBloomFileSuffix)-1])
  633. }
  634. return false, common.Hash{}
  635. }
  636. func findBloomFilter(datadir string) (string, common.Hash, error) {
  637. var (
  638. stateBloomPath string
  639. stateBloomRoot common.Hash
  640. )
  641. if err := filepath.Walk(datadir, func(path string, info os.FileInfo, err error) error {
  642. if info != nil && !info.IsDir() {
  643. ok, root := isBloomFilter(path)
  644. if ok {
  645. stateBloomPath = path
  646. stateBloomRoot = root
  647. }
  648. }
  649. return nil
  650. }); err != nil {
  651. return "", common.Hash{}, err
  652. }
  653. return stateBloomPath, stateBloomRoot, nil
  654. }
  655. const warningLog = `
  656. WARNING!
  657. The clean trie cache is not found. Please delete it by yourself after the
  658. pruning. Remember don't start the Geth without deleting the clean trie cache
  659. otherwise the entire database may be damaged!
  660. Check the command description "geth snapshot prune-state --help" for more details.
  661. `
  662. func deleteCleanTrieCache(path string) {
  663. if _, err := os.Stat(path); os.IsNotExist(err) {
  664. log.Warn(warningLog)
  665. return
  666. }
  667. os.RemoveAll(path)
  668. log.Info("Deleted trie clean cache", "path", path)
  669. }