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