disklayer_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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]struct{}{
  108. accDelNoCache: struct{}{},
  109. accDelCache: struct{}{},
  110. conNukeNoCache: struct{}{},
  111. conNukeCache: struct{}{},
  112. }, map[common.Hash][]byte{
  113. accModNoCache: reverse(accModNoCache[:]),
  114. accModCache: reverse(accModCache[:]),
  115. }, map[common.Hash]map[common.Hash][]byte{
  116. conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
  117. conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])},
  118. conDelNoCache: {conDelNoCacheSlot: nil},
  119. conDelCache: {conDelCacheSlot: nil},
  120. }); err != nil {
  121. t.Fatalf("failed to update snapshot tree: %v", err)
  122. }
  123. if err := snaps.Cap(diffRoot, 0); err != nil {
  124. t.Fatalf("failed to flatten snapshot tree: %v", err)
  125. }
  126. // Retrieve all the data through the disk layer and validate it
  127. base = snaps.Snapshot(diffRoot)
  128. if _, ok := base.(*diskLayer); !ok {
  129. t.Fatalf("update not flattend into the disk layer")
  130. }
  131. // assertAccount ensures that an account matches the given blob.
  132. assertAccount := func(account common.Hash, data []byte) {
  133. t.Helper()
  134. blob, err := base.AccountRLP(account)
  135. if err != nil {
  136. t.Errorf("account access (%x) failed: %v", account, err)
  137. } else if !bytes.Equal(blob, data) {
  138. t.Errorf("account access (%x) mismatch: have %x, want %x", account, blob, data)
  139. }
  140. }
  141. assertAccount(accNoModNoCache, accNoModNoCache[:])
  142. assertAccount(accNoModCache, accNoModCache[:])
  143. assertAccount(accModNoCache, reverse(accModNoCache[:]))
  144. assertAccount(accModCache, reverse(accModCache[:]))
  145. assertAccount(accDelNoCache, nil)
  146. assertAccount(accDelCache, nil)
  147. // assertStorage ensures that a storage slot matches the given blob.
  148. assertStorage := func(account common.Hash, slot common.Hash, data []byte) {
  149. t.Helper()
  150. blob, err := base.Storage(account, slot)
  151. if err != nil {
  152. t.Errorf("storage access (%x:%x) failed: %v", account, slot, err)
  153. } else if !bytes.Equal(blob, data) {
  154. t.Errorf("storage access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data)
  155. }
  156. }
  157. assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  158. assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  159. assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
  160. assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
  161. assertStorage(conDelNoCache, conDelNoCacheSlot, nil)
  162. assertStorage(conDelCache, conDelCacheSlot, nil)
  163. assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
  164. assertStorage(conNukeCache, conNukeCacheSlot, nil)
  165. // Retrieve all the data directly from the database and validate it
  166. // assertDatabaseAccount ensures that an account from the database matches the given blob.
  167. assertDatabaseAccount := func(account common.Hash, data []byte) {
  168. t.Helper()
  169. if blob := rawdb.ReadAccountSnapshot(db, account); !bytes.Equal(blob, data) {
  170. t.Errorf("account database access (%x) mismatch: have %x, want %x", account, blob, data)
  171. }
  172. }
  173. assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:])
  174. assertDatabaseAccount(accNoModCache, accNoModCache[:])
  175. assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:]))
  176. assertDatabaseAccount(accModCache, reverse(accModCache[:]))
  177. assertDatabaseAccount(accDelNoCache, nil)
  178. assertDatabaseAccount(accDelCache, nil)
  179. // assertDatabaseStorage ensures that a storage slot from the database matches the given blob.
  180. assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) {
  181. t.Helper()
  182. if blob := rawdb.ReadStorageSnapshot(db, account, slot); !bytes.Equal(blob, data) {
  183. t.Errorf("storage database access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data)
  184. }
  185. }
  186. assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  187. assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  188. assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
  189. assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
  190. assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil)
  191. assertDatabaseStorage(conDelCache, conDelCacheSlot, nil)
  192. assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
  193. assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil)
  194. }
  195. // Tests that merging something into a disk layer persists it into the database
  196. // and invalidates any previously written and cached values, discarding anything
  197. // after the in-progress generation marker.
  198. func TestDiskPartialMerge(t *testing.T) {
  199. // Iterate the test a few times to ensure we pick various internal orderings
  200. // for the data slots as well as the progress marker.
  201. for i := 0; i < 1024; i++ {
  202. // Create some accounts in the disk layer
  203. db := memorydb.New()
  204. var (
  205. accNoModNoCache = randomHash()
  206. accNoModCache = randomHash()
  207. accModNoCache = randomHash()
  208. accModCache = randomHash()
  209. accDelNoCache = randomHash()
  210. accDelCache = randomHash()
  211. conNoModNoCache = randomHash()
  212. conNoModNoCacheSlot = randomHash()
  213. conNoModCache = randomHash()
  214. conNoModCacheSlot = randomHash()
  215. conModNoCache = randomHash()
  216. conModNoCacheSlot = randomHash()
  217. conModCache = randomHash()
  218. conModCacheSlot = randomHash()
  219. conDelNoCache = randomHash()
  220. conDelNoCacheSlot = randomHash()
  221. conDelCache = randomHash()
  222. conDelCacheSlot = randomHash()
  223. conNukeNoCache = randomHash()
  224. conNukeNoCacheSlot = randomHash()
  225. conNukeCache = randomHash()
  226. conNukeCacheSlot = randomHash()
  227. baseRoot = randomHash()
  228. diffRoot = randomHash()
  229. genMarker = append(randomHash().Bytes(), randomHash().Bytes()...)
  230. )
  231. // insertAccount injects an account into the database if it's after the
  232. // generator marker, drops the op otherwise. This is needed to seed the
  233. // database with a valid starting snapshot.
  234. insertAccount := func(account common.Hash, data []byte) {
  235. if bytes.Compare(account[:], genMarker) <= 0 {
  236. rawdb.WriteAccountSnapshot(db, account, data[:])
  237. }
  238. }
  239. insertAccount(accNoModNoCache, accNoModNoCache[:])
  240. insertAccount(accNoModCache, accNoModCache[:])
  241. insertAccount(accModNoCache, accModNoCache[:])
  242. insertAccount(accModCache, accModCache[:])
  243. insertAccount(accDelNoCache, accDelNoCache[:])
  244. insertAccount(accDelCache, accDelCache[:])
  245. // insertStorage injects a storage slot into the database if it's after
  246. // the generator marker, drops the op otherwise. This is needed to seed
  247. // the database with a valid starting snapshot.
  248. insertStorage := func(account common.Hash, slot common.Hash, data []byte) {
  249. if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 {
  250. rawdb.WriteStorageSnapshot(db, account, slot, data[:])
  251. }
  252. }
  253. insertAccount(conNoModNoCache, conNoModNoCache[:])
  254. insertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  255. insertAccount(conNoModCache, conNoModCache[:])
  256. insertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  257. insertAccount(conModNoCache, conModNoCache[:])
  258. insertStorage(conModNoCache, conModNoCacheSlot, conModNoCacheSlot[:])
  259. insertAccount(conModCache, conModCache[:])
  260. insertStorage(conModCache, conModCacheSlot, conModCacheSlot[:])
  261. insertAccount(conDelNoCache, conDelNoCache[:])
  262. insertStorage(conDelNoCache, conDelNoCacheSlot, conDelNoCacheSlot[:])
  263. insertAccount(conDelCache, conDelCache[:])
  264. insertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:])
  265. insertAccount(conNukeNoCache, conNukeNoCache[:])
  266. insertStorage(conNukeNoCache, conNukeNoCacheSlot, conNukeNoCacheSlot[:])
  267. insertAccount(conNukeCache, conNukeCache[:])
  268. insertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
  269. rawdb.WriteSnapshotRoot(db, baseRoot)
  270. // Create a disk layer based on the above using a random progress marker
  271. // and cache in some data.
  272. snaps := &Tree{
  273. layers: map[common.Hash]snapshot{
  274. baseRoot: &diskLayer{
  275. diskdb: db,
  276. cache: fastcache.New(500 * 1024),
  277. root: baseRoot,
  278. },
  279. },
  280. }
  281. snaps.layers[baseRoot].(*diskLayer).genMarker = genMarker
  282. base := snaps.Snapshot(baseRoot)
  283. // assertAccount ensures that an account matches the given blob if it's
  284. // already covered by the disk snapshot, and errors out otherwise.
  285. assertAccount := func(account common.Hash, data []byte) {
  286. t.Helper()
  287. blob, err := base.AccountRLP(account)
  288. if bytes.Compare(account[:], genMarker) > 0 && err != ErrNotCoveredYet {
  289. t.Fatalf("test %d: post-marker (%x) account access (%x) succeeded: %x", i, genMarker, account, blob)
  290. }
  291. if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) {
  292. t.Fatalf("test %d: pre-marker (%x) account access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data)
  293. }
  294. }
  295. assertAccount(accNoModCache, accNoModCache[:])
  296. assertAccount(accModCache, accModCache[:])
  297. assertAccount(accDelCache, accDelCache[:])
  298. // assertStorage ensures that a storage slot matches the given blob if
  299. // it's already covered by the disk snapshot, and errors out otherwise.
  300. assertStorage := func(account common.Hash, slot common.Hash, data []byte) {
  301. t.Helper()
  302. blob, err := base.Storage(account, slot)
  303. if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && err != ErrNotCoveredYet {
  304. t.Fatalf("test %d: post-marker (%x) storage access (%x:%x) succeeded: %x", i, genMarker, account, slot, blob)
  305. }
  306. if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) {
  307. t.Fatalf("test %d: pre-marker (%x) storage access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data)
  308. }
  309. }
  310. assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  311. assertStorage(conModCache, conModCacheSlot, conModCacheSlot[:])
  312. assertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:])
  313. assertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
  314. // Modify or delete some accounts, flatten everything onto disk
  315. if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
  316. accDelNoCache: struct{}{},
  317. accDelCache: struct{}{},
  318. conNukeNoCache: struct{}{},
  319. conNukeCache: struct{}{},
  320. }, map[common.Hash][]byte{
  321. accModNoCache: reverse(accModNoCache[:]),
  322. accModCache: reverse(accModCache[:]),
  323. }, map[common.Hash]map[common.Hash][]byte{
  324. conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
  325. conModCache: {conModCacheSlot: reverse(conModCacheSlot[:])},
  326. conDelNoCache: {conDelNoCacheSlot: nil},
  327. conDelCache: {conDelCacheSlot: nil},
  328. }); err != nil {
  329. t.Fatalf("test %d: failed to update snapshot tree: %v", i, err)
  330. }
  331. if err := snaps.Cap(diffRoot, 0); err != nil {
  332. t.Fatalf("test %d: failed to flatten snapshot tree: %v", i, err)
  333. }
  334. // Retrieve all the data through the disk layer and validate it
  335. base = snaps.Snapshot(diffRoot)
  336. if _, ok := base.(*diskLayer); !ok {
  337. t.Fatalf("test %d: update not flattend into the disk layer", i)
  338. }
  339. assertAccount(accNoModNoCache, accNoModNoCache[:])
  340. assertAccount(accNoModCache, accNoModCache[:])
  341. assertAccount(accModNoCache, reverse(accModNoCache[:]))
  342. assertAccount(accModCache, reverse(accModCache[:]))
  343. assertAccount(accDelNoCache, nil)
  344. assertAccount(accDelCache, nil)
  345. assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  346. assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  347. assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
  348. assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
  349. assertStorage(conDelNoCache, conDelNoCacheSlot, nil)
  350. assertStorage(conDelCache, conDelCacheSlot, nil)
  351. assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
  352. assertStorage(conNukeCache, conNukeCacheSlot, nil)
  353. // Retrieve all the data directly from the database and validate it
  354. // assertDatabaseAccount ensures that an account inside the database matches
  355. // the given blob if it's already covered by the disk snapshot, and does not
  356. // exist otherwise.
  357. assertDatabaseAccount := func(account common.Hash, data []byte) {
  358. t.Helper()
  359. blob := rawdb.ReadAccountSnapshot(db, account)
  360. if bytes.Compare(account[:], genMarker) > 0 && blob != nil {
  361. t.Fatalf("test %d: post-marker (%x) account database access (%x) succeeded: %x", i, genMarker, account, blob)
  362. }
  363. if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) {
  364. t.Fatalf("test %d: pre-marker (%x) account database access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data)
  365. }
  366. }
  367. assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:])
  368. assertDatabaseAccount(accNoModCache, accNoModCache[:])
  369. assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:]))
  370. assertDatabaseAccount(accModCache, reverse(accModCache[:]))
  371. assertDatabaseAccount(accDelNoCache, nil)
  372. assertDatabaseAccount(accDelCache, nil)
  373. // assertDatabaseStorage ensures that a storage slot inside the database
  374. // matches the given blob if it's already covered by the disk snapshot,
  375. // and does not exist otherwise.
  376. assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) {
  377. t.Helper()
  378. blob := rawdb.ReadStorageSnapshot(db, account, slot)
  379. if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && blob != nil {
  380. t.Fatalf("test %d: post-marker (%x) storage database access (%x:%x) succeeded: %x", i, genMarker, account, slot, blob)
  381. }
  382. if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) {
  383. t.Fatalf("test %d: pre-marker (%x) storage database access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data)
  384. }
  385. }
  386. assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
  387. assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
  388. assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
  389. assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
  390. assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil)
  391. assertDatabaseStorage(conDelCache, conDelCacheSlot, nil)
  392. assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
  393. assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil)
  394. }
  395. }
  396. // Tests that merging something into a disk layer persists it into the database
  397. // and invalidates any previously written and cached values, discarding anything
  398. // after the in-progress generation marker.
  399. //
  400. // This test case is a tiny specialized case of TestDiskPartialMerge, which tests
  401. // some very specific cornercases that random tests won't ever trigger.
  402. func TestDiskMidAccountPartialMerge(t *testing.T) {
  403. }