journal.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // Copyright 2019 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 snapshot
  17. import (
  18. "bytes"
  19. "encoding/binary"
  20. "errors"
  21. "fmt"
  22. "io"
  23. "time"
  24. "github.com/VictoriaMetrics/fastcache"
  25. "github.com/ethereum/go-ethereum/common"
  26. "github.com/ethereum/go-ethereum/core/rawdb"
  27. "github.com/ethereum/go-ethereum/ethdb"
  28. "github.com/ethereum/go-ethereum/log"
  29. "github.com/ethereum/go-ethereum/rlp"
  30. "github.com/ethereum/go-ethereum/trie"
  31. )
  32. const journalVersion uint64 = 0
  33. // journalGenerator is a disk layer entry containing the generator progress marker.
  34. type journalGenerator struct {
  35. // Indicator that whether the database was in progress of being wiped.
  36. // It's deprecated but keep it here for background compatibility.
  37. Wiping bool
  38. Done bool // Whether the generator finished creating the snapshot
  39. Marker []byte
  40. Accounts uint64
  41. Slots uint64
  42. Storage uint64
  43. }
  44. // journalDestruct is an account deletion entry in a diffLayer's disk journal.
  45. type journalDestruct struct {
  46. Hash common.Hash
  47. }
  48. // journalAccount is an account entry in a diffLayer's disk journal.
  49. type journalAccount struct {
  50. Hash common.Hash
  51. Blob []byte
  52. }
  53. // journalStorage is an account's storage map in a diffLayer's disk journal.
  54. type journalStorage struct {
  55. Hash common.Hash
  56. Keys []common.Hash
  57. Vals [][]byte
  58. }
  59. // loadAndParseJournal tries to parse the snapshot journal in latest format.
  60. func loadAndParseJournal(db ethdb.KeyValueStore, base *diskLayer) (snapshot, journalGenerator, error) {
  61. // Retrieve the disk layer generator. It must exist, no matter the
  62. // snapshot is fully generated or not. Otherwise the entire disk
  63. // layer is invalid.
  64. generatorBlob := rawdb.ReadSnapshotGenerator(db)
  65. if len(generatorBlob) == 0 {
  66. return nil, journalGenerator{}, errors.New("missing snapshot generator")
  67. }
  68. var generator journalGenerator
  69. if err := rlp.DecodeBytes(generatorBlob, &generator); err != nil {
  70. return nil, journalGenerator{}, fmt.Errorf("failed to decode snapshot generator: %v", err)
  71. }
  72. // Retrieve the diff layer journal. It's possible that the journal is
  73. // not existent, e.g. the disk layer is generating while that the Geth
  74. // crashes without persisting the diff journal.
  75. // So if there is no journal, or the journal is invalid(e.g. the journal
  76. // is not matched with disk layer; or the it's the legacy-format journal,
  77. // etc.), we just discard all diffs and try to recover them later.
  78. journal := rawdb.ReadSnapshotJournal(db)
  79. if len(journal) == 0 {
  80. log.Warn("Loaded snapshot journal", "diskroot", base.root, "diffs", "missing")
  81. return base, generator, nil
  82. }
  83. r := rlp.NewStream(bytes.NewReader(journal), 0)
  84. // Firstly, resolve the first element as the journal version
  85. version, err := r.Uint()
  86. if err != nil {
  87. log.Warn("Failed to resolve the journal version", "error", err)
  88. return base, generator, nil
  89. }
  90. if version != journalVersion {
  91. log.Warn("Discarded the snapshot journal with wrong version", "required", journalVersion, "got", version)
  92. return base, generator, nil
  93. }
  94. // Secondly, resolve the disk layer root, ensure it's continuous
  95. // with disk layer. Note now we can ensure it's the snapshot journal
  96. // correct version, so we expect everything can be resolved properly.
  97. var root common.Hash
  98. if err := r.Decode(&root); err != nil {
  99. return nil, journalGenerator{}, errors.New("missing disk layer root")
  100. }
  101. // The diff journal is not matched with disk, discard them.
  102. // It can happen that Geth crashes without persisting the latest
  103. // diff journal.
  104. if !bytes.Equal(root.Bytes(), base.root.Bytes()) {
  105. log.Warn("Loaded snapshot journal", "diskroot", base.root, "diffs", "unmatched")
  106. return base, generator, nil
  107. }
  108. // Load all the snapshot diffs from the journal
  109. snapshot, err := loadDiffLayer(base, r)
  110. if err != nil {
  111. return nil, journalGenerator{}, err
  112. }
  113. log.Debug("Loaded snapshot journal", "diskroot", base.root, "diffhead", snapshot.Root())
  114. return snapshot, generator, nil
  115. }
  116. // loadSnapshot loads a pre-existing state snapshot backed by a key-value store.
  117. func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash, recovery bool) (snapshot, bool, error) {
  118. // If snapshotting is disabled (initial sync in progress), don't do anything,
  119. // wait for the chain to permit us to do something meaningful
  120. if rawdb.ReadSnapshotDisabled(diskdb) {
  121. return nil, true, nil
  122. }
  123. // Retrieve the block number and hash of the snapshot, failing if no snapshot
  124. // is present in the database (or crashed mid-update).
  125. baseRoot := rawdb.ReadSnapshotRoot(diskdb)
  126. if baseRoot == (common.Hash{}) {
  127. return nil, false, errors.New("missing or corrupted snapshot")
  128. }
  129. base := &diskLayer{
  130. diskdb: diskdb,
  131. triedb: triedb,
  132. cache: fastcache.New(cache * 1024 * 1024),
  133. root: baseRoot,
  134. }
  135. snapshot, generator, err := loadAndParseJournal(diskdb, base)
  136. if err != nil {
  137. log.Warn("Failed to load new-format journal", "error", err)
  138. return nil, false, err
  139. }
  140. // Entire snapshot journal loaded, sanity check the head. If the loaded
  141. // snapshot is not matched with current state root, print a warning log
  142. // or discard the entire snapshot it's legacy snapshot.
  143. //
  144. // Possible scenario: Geth was crashed without persisting journal and then
  145. // restart, the head is rewound to the point with available state(trie)
  146. // which is below the snapshot. In this case the snapshot can be recovered
  147. // by re-executing blocks but right now it's unavailable.
  148. if head := snapshot.Root(); head != root {
  149. // If it's legacy snapshot, or it's new-format snapshot but
  150. // it's not in recovery mode, returns the error here for
  151. // rebuilding the entire snapshot forcibly.
  152. if !recovery {
  153. return nil, false, fmt.Errorf("head doesn't match snapshot: have %#x, want %#x", head, root)
  154. }
  155. // It's in snapshot recovery, the assumption is held that
  156. // the disk layer is always higher than chain head. It can
  157. // be eventually recovered when the chain head beyonds the
  158. // disk layer.
  159. log.Warn("Snapshot is not continuous with chain", "snaproot", head, "chainroot", root)
  160. }
  161. // Everything loaded correctly, resume any suspended operations
  162. if !generator.Done {
  163. // Whether or not wiping was in progress, load any generator progress too
  164. base.genMarker = generator.Marker
  165. if base.genMarker == nil {
  166. base.genMarker = []byte{}
  167. }
  168. base.genPending = make(chan struct{})
  169. base.genAbort = make(chan chan *generatorStats)
  170. var origin uint64
  171. if len(generator.Marker) >= 8 {
  172. origin = binary.BigEndian.Uint64(generator.Marker)
  173. }
  174. go base.generate(&generatorStats{
  175. origin: origin,
  176. start: time.Now(),
  177. accounts: generator.Accounts,
  178. slots: generator.Slots,
  179. storage: common.StorageSize(generator.Storage),
  180. })
  181. }
  182. return snapshot, false, nil
  183. }
  184. // loadDiffLayer reads the next sections of a snapshot journal, reconstructing a new
  185. // diff and verifying that it can be linked to the requested parent.
  186. func loadDiffLayer(parent snapshot, r *rlp.Stream) (snapshot, error) {
  187. // Read the next diff journal entry
  188. var root common.Hash
  189. if err := r.Decode(&root); err != nil {
  190. // The first read may fail with EOF, marking the end of the journal
  191. if err == io.EOF {
  192. return parent, nil
  193. }
  194. return nil, fmt.Errorf("load diff root: %v", err)
  195. }
  196. var destructs []journalDestruct
  197. if err := r.Decode(&destructs); err != nil {
  198. return nil, fmt.Errorf("load diff destructs: %v", err)
  199. }
  200. destructSet := make(map[common.Hash]struct{})
  201. for _, entry := range destructs {
  202. destructSet[entry.Hash] = struct{}{}
  203. }
  204. var accounts []journalAccount
  205. if err := r.Decode(&accounts); err != nil {
  206. return nil, fmt.Errorf("load diff accounts: %v", err)
  207. }
  208. accountData := make(map[common.Hash][]byte)
  209. for _, entry := range accounts {
  210. if len(entry.Blob) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that
  211. accountData[entry.Hash] = entry.Blob
  212. } else {
  213. accountData[entry.Hash] = nil
  214. }
  215. }
  216. var storage []journalStorage
  217. if err := r.Decode(&storage); err != nil {
  218. return nil, fmt.Errorf("load diff storage: %v", err)
  219. }
  220. storageData := make(map[common.Hash]map[common.Hash][]byte)
  221. for _, entry := range storage {
  222. slots := make(map[common.Hash][]byte)
  223. for i, key := range entry.Keys {
  224. if len(entry.Vals[i]) > 0 { // RLP loses nil-ness, but `[]byte{}` is not a valid item, so reinterpret that
  225. slots[key] = entry.Vals[i]
  226. } else {
  227. slots[key] = nil
  228. }
  229. }
  230. storageData[entry.Hash] = slots
  231. }
  232. return loadDiffLayer(newDiffLayer(parent, root, destructSet, accountData, storageData), r)
  233. }
  234. // Journal terminates any in-progress snapshot generation, also implicitly pushing
  235. // the progress into the database.
  236. func (dl *diskLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) {
  237. // If the snapshot is currently being generated, abort it
  238. var stats *generatorStats
  239. if dl.genAbort != nil {
  240. abort := make(chan *generatorStats)
  241. dl.genAbort <- abort
  242. if stats = <-abort; stats != nil {
  243. stats.Log("Journalling in-progress snapshot", dl.root, dl.genMarker)
  244. }
  245. }
  246. // Ensure the layer didn't get stale
  247. dl.lock.RLock()
  248. defer dl.lock.RUnlock()
  249. if dl.stale {
  250. return common.Hash{}, ErrSnapshotStale
  251. }
  252. // Ensure the generator stats is written even if none was ran this cycle
  253. journalProgress(dl.diskdb, dl.genMarker, stats)
  254. log.Debug("Journalled disk layer", "root", dl.root)
  255. return dl.root, nil
  256. }
  257. // Journal writes the memory layer contents into a buffer to be stored in the
  258. // database as the snapshot journal.
  259. func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) {
  260. // Journal the parent first
  261. base, err := dl.parent.Journal(buffer)
  262. if err != nil {
  263. return common.Hash{}, err
  264. }
  265. // Ensure the layer didn't get stale
  266. dl.lock.RLock()
  267. defer dl.lock.RUnlock()
  268. if dl.Stale() {
  269. return common.Hash{}, ErrSnapshotStale
  270. }
  271. // Everything below was journalled, persist this layer too
  272. if err := rlp.Encode(buffer, dl.root); err != nil {
  273. return common.Hash{}, err
  274. }
  275. destructs := make([]journalDestruct, 0, len(dl.destructSet))
  276. for hash := range dl.destructSet {
  277. destructs = append(destructs, journalDestruct{Hash: hash})
  278. }
  279. if err := rlp.Encode(buffer, destructs); err != nil {
  280. return common.Hash{}, err
  281. }
  282. accounts := make([]journalAccount, 0, len(dl.accountData))
  283. for hash, blob := range dl.accountData {
  284. accounts = append(accounts, journalAccount{Hash: hash, Blob: blob})
  285. }
  286. if err := rlp.Encode(buffer, accounts); err != nil {
  287. return common.Hash{}, err
  288. }
  289. storage := make([]journalStorage, 0, len(dl.storageData))
  290. for hash, slots := range dl.storageData {
  291. keys := make([]common.Hash, 0, len(slots))
  292. vals := make([][]byte, 0, len(slots))
  293. for key, val := range slots {
  294. keys = append(keys, key)
  295. vals = append(vals, val)
  296. }
  297. storage = append(storage, journalStorage{Hash: hash, Keys: keys, Vals: vals})
  298. }
  299. if err := rlp.Encode(buffer, storage); err != nil {
  300. return common.Hash{}, err
  301. }
  302. log.Debug("Journalled diff layer", "root", dl.root, "parent", dl.parent.Root())
  303. return base, nil
  304. }