disklayer_test.go 21 KB

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