disklayer_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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. "testing"
  20. "github.com/VictoriaMetrics/fastcache"
  21. "github.com/ethereum/go-ethereum/common"
  22. "github.com/ethereum/go-ethereum/core/rawdb"
  23. "github.com/ethereum/go-ethereum/ethdb/memorydb"
  24. )
  25. // reverse reverses the contents of a byte slice. It's used to update random accs
  26. // with deterministic changes.
  27. func reverse(blob []byte) []byte {
  28. res := make([]byte, len(blob))
  29. for i, b := range blob {
  30. res[len(blob)-1-i] = b
  31. }
  32. return res
  33. }
  34. // Tests that merging something into a disk layer persists it into the database
  35. // and invalidates any previously written and cached values.
  36. func TestDiskMerge(t *testing.T) {
  37. // Create some accounts in the disk layer
  38. db := memorydb.New()
  39. var (
  40. accNoModNoCache = common.Hash{0x1}
  41. accNoModCache = common.Hash{0x2}
  42. accModNoCache = common.Hash{0x3}
  43. accModCache = common.Hash{0x4}
  44. accDelNoCache = common.Hash{0x5}
  45. accDelCache = common.Hash{0x6}
  46. conNoModNoCache = common.Hash{0x7}
  47. conNoModNoCacheSlot = common.Hash{0x70}
  48. conNoModCache = common.Hash{0x8}
  49. conNoModCacheSlot = common.Hash{0x80}
  50. conModNoCache = common.Hash{0x9}
  51. conModNoCacheSlot = common.Hash{0x90}
  52. conModCache = common.Hash{0xa}
  53. conModCacheSlot = common.Hash{0xa0}
  54. conDelNoCache = common.Hash{0xb}
  55. conDelNoCacheSlot = common.Hash{0xb0}
  56. conDelCache = common.Hash{0xc}
  57. conDelCacheSlot = common.Hash{0xc0}
  58. conNukeNoCache = common.Hash{0xd}
  59. conNukeNoCacheSlot = common.Hash{0xd0}
  60. conNukeCache = common.Hash{0xe}
  61. conNukeCacheSlot = common.Hash{0xe0}
  62. baseRoot = randomHash()
  63. diffRoot = randomHash()
  64. )
  65. rawdb.WriteAccountSnapshot(db, accNoModNoCache, accNoModNoCache[:])
  66. rawdb.WriteAccountSnapshot(db, accNoModCache, accNoModCache[:])
  67. rawdb.WriteAccountSnapshot(db, accModNoCache, accModNoCache[:])
  68. rawdb.WriteAccountSnapshot(db, accModCache, accModCache[:])
  69. rawdb.WriteAccountSnapshot(db, accDelNoCache, accDelNoCache[:])
  70. rawdb.WriteAccountSnapshot(db, accDelCache, accDelCache[:])
  71. rawdb.WriteAccountSnapshot(db, conNoModNoCache, conNoModNoCache[:])
  72. rawdb.WriteStorageSnapshot(db, conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  73. rawdb.WriteAccountSnapshot(db, conNoModCache, conNoModCache[:])
  74. rawdb.WriteStorageSnapshot(db, conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  75. rawdb.WriteAccountSnapshot(db, conModNoCache, conModNoCache[:])
  76. rawdb.WriteStorageSnapshot(db, conModNoCache, conModNoCacheSlot, conModNoCacheSlot[:])
  77. rawdb.WriteAccountSnapshot(db, conModCache, conModCache[:])
  78. rawdb.WriteStorageSnapshot(db, conModCache, conModCacheSlot, conModCacheSlot[:])
  79. rawdb.WriteAccountSnapshot(db, conDelNoCache, conDelNoCache[:])
  80. rawdb.WriteStorageSnapshot(db, conDelNoCache, conDelNoCacheSlot, conDelNoCacheSlot[:])
  81. rawdb.WriteAccountSnapshot(db, conDelCache, conDelCache[:])
  82. rawdb.WriteStorageSnapshot(db, conDelCache, conDelCacheSlot, conDelCacheSlot[:])
  83. rawdb.WriteAccountSnapshot(db, conNukeNoCache, conNukeNoCache[:])
  84. rawdb.WriteStorageSnapshot(db, conNukeNoCache, conNukeNoCacheSlot, conNukeNoCacheSlot[:])
  85. rawdb.WriteAccountSnapshot(db, conNukeCache, conNukeCache[:])
  86. rawdb.WriteStorageSnapshot(db, conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
  87. rawdb.WriteSnapshotRoot(db, baseRoot)
  88. // Create a disk layer based on the above and cache in some data
  89. snaps := &Tree{
  90. layers: map[common.Hash]snapshot{
  91. baseRoot: &diskLayer{
  92. diskdb: db,
  93. cache: fastcache.New(500 * 1024),
  94. root: baseRoot,
  95. },
  96. },
  97. }
  98. base := snaps.Snapshot(baseRoot)
  99. base.AccountRLP(accNoModCache)
  100. base.AccountRLP(accModCache)
  101. base.AccountRLP(accDelCache)
  102. base.Storage(conNoModCache, conNoModCacheSlot)
  103. base.Storage(conModCache, conModCacheSlot)
  104. base.Storage(conDelCache, conDelCacheSlot)
  105. base.Storage(conNukeCache, conNukeCacheSlot)
  106. // Modify or delete some accounts, flatten everything onto disk
  107. if err := snaps.Update(diffRoot, baseRoot, map[common.Hash][]byte{
  108. accModNoCache: reverse(accModNoCache[:]),
  109. accModCache: reverse(accModCache[:]),
  110. accDelNoCache: nil,
  111. accDelCache: nil,
  112. conNukeNoCache: nil,
  113. conNukeCache: nil,
  114. }, map[common.Hash]map[common.Hash][]byte{
  115. conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
  116. conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])},
  117. conDelNoCache: {conDelNoCacheSlot: nil},
  118. conDelCache: {conDelCacheSlot: nil},
  119. }); err != nil {
  120. t.Fatalf("failed to update snapshot tree: %v", err)
  121. }
  122. if err := snaps.Cap(diffRoot, 0); err != nil {
  123. t.Fatalf("failed to flatten snapshot tree: %v", err)
  124. }
  125. // Retrieve all the data through the disk layer and validate it
  126. base = snaps.Snapshot(diffRoot)
  127. if _, ok := base.(*diskLayer); !ok {
  128. t.Fatalf("update not flattend into the disk layer")
  129. }
  130. // assertAccount ensures that an account matches the given blob.
  131. assertAccount := func(account common.Hash, data []byte) {
  132. t.Helper()
  133. blob, err := base.AccountRLP(account)
  134. if err != nil {
  135. t.Errorf("account access (%x) failed: %v", account, err)
  136. } else if !bytes.Equal(blob, data) {
  137. t.Errorf("account access (%x) mismatch: have %x, want %x", account, blob, data)
  138. }
  139. }
  140. assertAccount(accNoModNoCache, accNoModNoCache[:])
  141. assertAccount(accNoModCache, accNoModCache[:])
  142. assertAccount(accModNoCache, reverse(accModNoCache[:]))
  143. assertAccount(accModCache, reverse(accModCache[:]))
  144. assertAccount(accDelNoCache, nil)
  145. assertAccount(accDelCache, nil)
  146. // assertStorage ensures that a storage slot matches the given blob.
  147. assertStorage := func(account common.Hash, slot common.Hash, data []byte) {
  148. t.Helper()
  149. blob, err := base.Storage(account, slot)
  150. if err != nil {
  151. t.Errorf("storage access (%x:%x) failed: %v", account, slot, err)
  152. } else if !bytes.Equal(blob, data) {
  153. t.Errorf("storage access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data)
  154. }
  155. }
  156. assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  157. assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  158. assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
  159. assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
  160. assertStorage(conDelNoCache, conDelNoCacheSlot, nil)
  161. assertStorage(conDelCache, conDelCacheSlot, nil)
  162. assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
  163. assertStorage(conNukeCache, conNukeCacheSlot, nil)
  164. // Retrieve all the data directly from the database and validate it
  165. // assertDatabaseAccount ensures that an account from the database matches the given blob.
  166. assertDatabaseAccount := func(account common.Hash, data []byte) {
  167. t.Helper()
  168. if blob := rawdb.ReadAccountSnapshot(db, account); !bytes.Equal(blob, data) {
  169. t.Errorf("account database access (%x) mismatch: have %x, want %x", account, blob, data)
  170. }
  171. }
  172. assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:])
  173. assertDatabaseAccount(accNoModCache, accNoModCache[:])
  174. assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:]))
  175. assertDatabaseAccount(accModCache, reverse(accModCache[:]))
  176. assertDatabaseAccount(accDelNoCache, nil)
  177. assertDatabaseAccount(accDelCache, nil)
  178. // assertDatabaseStorage ensures that a storage slot from the database matches the given blob.
  179. assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) {
  180. t.Helper()
  181. if blob := rawdb.ReadStorageSnapshot(db, account, slot); !bytes.Equal(blob, data) {
  182. t.Errorf("storage database access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data)
  183. }
  184. }
  185. assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  186. assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  187. assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
  188. assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
  189. assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil)
  190. assertDatabaseStorage(conDelCache, conDelCacheSlot, nil)
  191. assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
  192. assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil)
  193. }
  194. // Tests that merging something into a disk layer persists it into the database
  195. // and invalidates any previously written and cached values, discarding anything
  196. // after the in-progress generation marker.
  197. func TestDiskPartialMerge(t *testing.T) {
  198. // Iterate the test a few times to ensure we pick various internal orderings
  199. // for the data slots as well as the progress marker.
  200. for i := 0; i < 1024; i++ {
  201. // Create some accounts in the disk layer
  202. db := memorydb.New()
  203. var (
  204. accNoModNoCache = randomHash()
  205. accNoModCache = randomHash()
  206. accModNoCache = randomHash()
  207. accModCache = randomHash()
  208. accDelNoCache = randomHash()
  209. accDelCache = randomHash()
  210. conNoModNoCache = randomHash()
  211. conNoModNoCacheSlot = randomHash()
  212. conNoModCache = randomHash()
  213. conNoModCacheSlot = randomHash()
  214. conModNoCache = randomHash()
  215. conModNoCacheSlot = randomHash()
  216. conModCache = randomHash()
  217. conModCacheSlot = randomHash()
  218. conDelNoCache = randomHash()
  219. conDelNoCacheSlot = randomHash()
  220. conDelCache = randomHash()
  221. conDelCacheSlot = randomHash()
  222. conNukeNoCache = randomHash()
  223. conNukeNoCacheSlot = randomHash()
  224. conNukeCache = randomHash()
  225. conNukeCacheSlot = randomHash()
  226. baseRoot = randomHash()
  227. diffRoot = randomHash()
  228. genMarker = append(randomHash().Bytes(), randomHash().Bytes()...)
  229. )
  230. // insertAccount injects an account into the database if it's after the
  231. // generator marker, drops the op otherwise. This is needed to seed the
  232. // database with a valid starting snapshot.
  233. insertAccount := func(account common.Hash, data []byte) {
  234. if bytes.Compare(account[:], genMarker) <= 0 {
  235. rawdb.WriteAccountSnapshot(db, account, data[:])
  236. }
  237. }
  238. insertAccount(accNoModNoCache, accNoModNoCache[:])
  239. insertAccount(accNoModCache, accNoModCache[:])
  240. insertAccount(accModNoCache, accModNoCache[:])
  241. insertAccount(accModCache, accModCache[:])
  242. insertAccount(accDelNoCache, accDelNoCache[:])
  243. insertAccount(accDelCache, accDelCache[:])
  244. // insertStorage injects a storage slot into the database if it's after
  245. // the generator marker, drops the op otherwise. This is needed to seed
  246. // the database with a valid starting snapshot.
  247. insertStorage := func(account common.Hash, slot common.Hash, data []byte) {
  248. if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 {
  249. rawdb.WriteStorageSnapshot(db, account, slot, data[:])
  250. }
  251. }
  252. insertAccount(conNoModNoCache, conNoModNoCache[:])
  253. insertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  254. insertAccount(conNoModCache, conNoModCache[:])
  255. insertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  256. insertAccount(conModNoCache, conModNoCache[:])
  257. insertStorage(conModNoCache, conModNoCacheSlot, conModNoCacheSlot[:])
  258. insertAccount(conModCache, conModCache[:])
  259. insertStorage(conModCache, conModCacheSlot, conModCacheSlot[:])
  260. insertAccount(conDelNoCache, conDelNoCache[:])
  261. insertStorage(conDelNoCache, conDelNoCacheSlot, conDelNoCacheSlot[:])
  262. insertAccount(conDelCache, conDelCache[:])
  263. insertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:])
  264. insertAccount(conNukeNoCache, conNukeNoCache[:])
  265. insertStorage(conNukeNoCache, conNukeNoCacheSlot, conNukeNoCacheSlot[:])
  266. insertAccount(conNukeCache, conNukeCache[:])
  267. insertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
  268. rawdb.WriteSnapshotRoot(db, baseRoot)
  269. // Create a disk layer based on the above using a random progress marker
  270. // and cache in some data.
  271. snaps := &Tree{
  272. layers: map[common.Hash]snapshot{
  273. baseRoot: &diskLayer{
  274. diskdb: db,
  275. cache: fastcache.New(500 * 1024),
  276. root: baseRoot,
  277. },
  278. },
  279. }
  280. snaps.layers[baseRoot].(*diskLayer).genMarker = genMarker
  281. base := snaps.Snapshot(baseRoot)
  282. // assertAccount ensures that an account matches the given blob if it's
  283. // already covered by the disk snapshot, and errors out otherwise.
  284. assertAccount := func(account common.Hash, data []byte) {
  285. t.Helper()
  286. blob, err := base.AccountRLP(account)
  287. if bytes.Compare(account[:], genMarker) > 0 && err != ErrNotCoveredYet {
  288. t.Fatalf("test %d: post-marker (%x) account access (%x) succeded: %x", i, genMarker, account, blob)
  289. }
  290. if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) {
  291. t.Fatalf("test %d: pre-marker (%x) account access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data)
  292. }
  293. }
  294. assertAccount(accNoModCache, accNoModCache[:])
  295. assertAccount(accModCache, accModCache[:])
  296. assertAccount(accDelCache, accDelCache[:])
  297. // assertStorage ensures that a storage slot matches the given blob if
  298. // it's already covered by the disk snapshot, and errors out otherwise.
  299. assertStorage := func(account common.Hash, slot common.Hash, data []byte) {
  300. t.Helper()
  301. blob, err := base.Storage(account, slot)
  302. if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && err != ErrNotCoveredYet {
  303. t.Fatalf("test %d: post-marker (%x) storage access (%x:%x) succeded: %x", i, genMarker, account, slot, blob)
  304. }
  305. if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) {
  306. t.Fatalf("test %d: pre-marker (%x) storage access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data)
  307. }
  308. }
  309. assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  310. assertStorage(conModCache, conModCacheSlot, conModCacheSlot[:])
  311. assertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:])
  312. assertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
  313. // Modify or delete some accounts, flatten everything onto disk
  314. if err := snaps.Update(diffRoot, baseRoot, map[common.Hash][]byte{
  315. accModNoCache: reverse(accModNoCache[:]),
  316. accModCache: reverse(accModCache[:]),
  317. accDelNoCache: nil,
  318. accDelCache: nil,
  319. conNukeNoCache: nil,
  320. conNukeCache: nil,
  321. }, map[common.Hash]map[common.Hash][]byte{
  322. conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
  323. conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])},
  324. conDelNoCache: {conDelNoCacheSlot: nil},
  325. conDelCache: {conDelCacheSlot: nil},
  326. }); err != nil {
  327. t.Fatalf("test %d: failed to update snapshot tree: %v", i, err)
  328. }
  329. if err := snaps.Cap(diffRoot, 0); err != nil {
  330. t.Fatalf("test %d: failed to flatten snapshot tree: %v", i, err)
  331. }
  332. // Retrieve all the data through the disk layer and validate it
  333. base = snaps.Snapshot(diffRoot)
  334. if _, ok := base.(*diskLayer); !ok {
  335. t.Fatalf("test %d: update not flattend into the disk layer", i)
  336. }
  337. assertAccount(accNoModNoCache, accNoModNoCache[:])
  338. assertAccount(accNoModCache, accNoModCache[:])
  339. assertAccount(accModNoCache, reverse(accModNoCache[:]))
  340. assertAccount(accModCache, reverse(accModCache[:]))
  341. assertAccount(accDelNoCache, nil)
  342. assertAccount(accDelCache, nil)
  343. assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  344. assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  345. assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
  346. assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
  347. assertStorage(conDelNoCache, conDelNoCacheSlot, nil)
  348. assertStorage(conDelCache, conDelCacheSlot, nil)
  349. assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
  350. assertStorage(conNukeCache, conNukeCacheSlot, nil)
  351. // Retrieve all the data directly from the database and validate it
  352. // assertDatabaseAccount ensures that an account inside the database matches
  353. // the given blob if it's already covered by the disk snapshot, and does not
  354. // exist otherwise.
  355. assertDatabaseAccount := func(account common.Hash, data []byte) {
  356. t.Helper()
  357. blob := rawdb.ReadAccountSnapshot(db, account)
  358. if bytes.Compare(account[:], genMarker) > 0 && blob != nil {
  359. t.Fatalf("test %d: post-marker (%x) account database access (%x) succeded: %x", i, genMarker, account, blob)
  360. }
  361. if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) {
  362. t.Fatalf("test %d: pre-marker (%x) account database access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data)
  363. }
  364. }
  365. assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:])
  366. assertDatabaseAccount(accNoModCache, accNoModCache[:])
  367. assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:]))
  368. assertDatabaseAccount(accModCache, reverse(accModCache[:]))
  369. assertDatabaseAccount(accDelNoCache, nil)
  370. assertDatabaseAccount(accDelCache, nil)
  371. // assertDatabaseStorage ensures that a storage slot inside the database
  372. // matches the given blob if it's already covered by the disk snapshot,
  373. // and does not exist otherwise.
  374. assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) {
  375. t.Helper()
  376. blob := rawdb.ReadStorageSnapshot(db, account, slot)
  377. if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && blob != nil {
  378. t.Fatalf("test %d: post-marker (%x) storage database access (%x:%x) succeded: %x", i, genMarker, account, slot, blob)
  379. }
  380. if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) {
  381. t.Fatalf("test %d: pre-marker (%x) storage database access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data)
  382. }
  383. }
  384. assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  385. assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  386. assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
  387. assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
  388. assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil)
  389. assertDatabaseStorage(conDelCache, conDelCacheSlot, nil)
  390. assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
  391. assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil)
  392. }
  393. }
  394. // Tests that merging something into a disk layer persists it into the database
  395. // and invalidates any previously written and cached values, discarding anything
  396. // after the in-progress generation marker.
  397. //
  398. // This test case is a tiny specialized case of TestDiskPartialMerge, which tests
  399. // some very specific cornercases that random tests won't ever trigger.
  400. func TestDiskMidAccountPartialMerge(t *testing.T) {
  401. }