github.com/DxChainNetwork/dxc@v0.8.1-0.20220824085222-1162e304b6e7/core/state/snapshot/snapshot_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 "encoding/binary" 21 "fmt" 22 "math/big" 23 "math/rand" 24 "testing" 25 26 "github.com/DxChainNetwork/dxc/common" 27 "github.com/DxChainNetwork/dxc/core/rawdb" 28 "github.com/DxChainNetwork/dxc/rlp" 29 "github.com/VictoriaMetrics/fastcache" 30 ) 31 32 // randomHash generates a random blob of data and returns it as a hash. 33 func randomHash() common.Hash { 34 var hash common.Hash 35 if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil { 36 panic(err) 37 } 38 return hash 39 } 40 41 // randomAccount generates a random account and returns it RLP encoded. 42 func randomAccount() []byte { 43 root := randomHash() 44 a := Account{ 45 Balance: big.NewInt(rand.Int63()), 46 Nonce: rand.Uint64(), 47 Root: root[:], 48 CodeHash: emptyCode[:], 49 } 50 data, _ := rlp.EncodeToBytes(a) 51 return data 52 } 53 54 // randomAccountSet generates a set of random accounts with the given strings as 55 // the account address hashes. 56 func randomAccountSet(hashes ...string) map[common.Hash][]byte { 57 accounts := make(map[common.Hash][]byte) 58 for _, hash := range hashes { 59 accounts[common.HexToHash(hash)] = randomAccount() 60 } 61 return accounts 62 } 63 64 // randomStorageSet generates a set of random slots with the given strings as 65 // the slot addresses. 66 func randomStorageSet(accounts []string, hashes [][]string, nilStorage [][]string) map[common.Hash]map[common.Hash][]byte { 67 storages := make(map[common.Hash]map[common.Hash][]byte) 68 for index, account := range accounts { 69 storages[common.HexToHash(account)] = make(map[common.Hash][]byte) 70 71 if index < len(hashes) { 72 hashes := hashes[index] 73 for _, hash := range hashes { 74 storages[common.HexToHash(account)][common.HexToHash(hash)] = randomHash().Bytes() 75 } 76 } 77 if index < len(nilStorage) { 78 nils := nilStorage[index] 79 for _, hash := range nils { 80 storages[common.HexToHash(account)][common.HexToHash(hash)] = nil 81 } 82 } 83 } 84 return storages 85 } 86 87 // Tests that if a disk layer becomes stale, no active external references will 88 // be returned with junk data. This version of the test flattens every diff layer 89 // to check internal corner case around the bottom-most memory accumulator. 90 func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) { 91 // Create an empty base layer and a snapshot tree out of it 92 base := &diskLayer{ 93 diskdb: rawdb.NewMemoryDatabase(), 94 root: common.HexToHash("0x01"), 95 cache: fastcache.New(1024 * 500), 96 } 97 snaps := &Tree{ 98 layers: map[common.Hash]snapshot{ 99 base.root: base, 100 }, 101 } 102 // Retrieve a reference to the base and commit a diff on top 103 ref := snaps.Snapshot(base.root) 104 105 accounts := map[common.Hash][]byte{ 106 common.HexToHash("0xa1"): randomAccount(), 107 } 108 if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { 109 t.Fatalf("failed to create a diff layer: %v", err) 110 } 111 if n := len(snaps.layers); n != 2 { 112 t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 2) 113 } 114 // Commit the diff layer onto the disk and ensure it's persisted 115 if err := snaps.Cap(common.HexToHash("0x02"), 0); err != nil { 116 t.Fatalf("failed to merge diff layer onto disk: %v", err) 117 } 118 // Since the base layer was modified, ensure that data retrieval on the external reference fail 119 if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { 120 t.Errorf("stale reference returned account: %#x (err: %v)", acc, err) 121 } 122 if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale { 123 t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err) 124 } 125 if n := len(snaps.layers); n != 1 { 126 t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 1) 127 fmt.Println(snaps.layers) 128 } 129 } 130 131 // Tests that if a disk layer becomes stale, no active external references will 132 // be returned with junk data. This version of the test retains the bottom diff 133 // layer to check the usual mode of operation where the accumulator is retained. 134 func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) { 135 // Create an empty base layer and a snapshot tree out of it 136 base := &diskLayer{ 137 diskdb: rawdb.NewMemoryDatabase(), 138 root: common.HexToHash("0x01"), 139 cache: fastcache.New(1024 * 500), 140 } 141 snaps := &Tree{ 142 layers: map[common.Hash]snapshot{ 143 base.root: base, 144 }, 145 } 146 // Retrieve a reference to the base and commit two diffs on top 147 ref := snaps.Snapshot(base.root) 148 149 accounts := map[common.Hash][]byte{ 150 common.HexToHash("0xa1"): randomAccount(), 151 } 152 if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { 153 t.Fatalf("failed to create a diff layer: %v", err) 154 } 155 if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil { 156 t.Fatalf("failed to create a diff layer: %v", err) 157 } 158 if n := len(snaps.layers); n != 3 { 159 t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 3) 160 } 161 // Commit the diff layer onto the disk and ensure it's persisted 162 defer func(memcap uint64) { aggregatorMemoryLimit = memcap }(aggregatorMemoryLimit) 163 aggregatorMemoryLimit = 0 164 165 if err := snaps.Cap(common.HexToHash("0x03"), 1); err != nil { 166 t.Fatalf("failed to merge accumulator onto disk: %v", err) 167 } 168 // Since the base layer was modified, ensure that data retrievald on the external reference fail 169 if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { 170 t.Errorf("stale reference returned account: %#x (err: %v)", acc, err) 171 } 172 if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale { 173 t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err) 174 } 175 if n := len(snaps.layers); n != 2 { 176 t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 2) 177 fmt.Println(snaps.layers) 178 } 179 } 180 181 // Tests that if a diff layer becomes stale, no active external references will 182 // be returned with junk data. This version of the test retains the bottom diff 183 // layer to check the usual mode of operation where the accumulator is retained. 184 func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) { 185 // Create an empty base layer and a snapshot tree out of it 186 base := &diskLayer{ 187 diskdb: rawdb.NewMemoryDatabase(), 188 root: common.HexToHash("0x01"), 189 cache: fastcache.New(1024 * 500), 190 } 191 snaps := &Tree{ 192 layers: map[common.Hash]snapshot{ 193 base.root: base, 194 }, 195 } 196 // Commit three diffs on top and retrieve a reference to the bottommost 197 accounts := map[common.Hash][]byte{ 198 common.HexToHash("0xa1"): randomAccount(), 199 } 200 if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { 201 t.Fatalf("failed to create a diff layer: %v", err) 202 } 203 if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil { 204 t.Fatalf("failed to create a diff layer: %v", err) 205 } 206 if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, accounts, nil); err != nil { 207 t.Fatalf("failed to create a diff layer: %v", err) 208 } 209 if n := len(snaps.layers); n != 4 { 210 t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 4) 211 } 212 ref := snaps.Snapshot(common.HexToHash("0x02")) 213 214 // Doing a Cap operation with many allowed layers should be a no-op 215 exp := len(snaps.layers) 216 if err := snaps.Cap(common.HexToHash("0x04"), 2000); err != nil { 217 t.Fatalf("failed to flatten diff layer into accumulator: %v", err) 218 } 219 if got := len(snaps.layers); got != exp { 220 t.Errorf("layers modified, got %d exp %d", got, exp) 221 } 222 // Flatten the diff layer into the bottom accumulator 223 if err := snaps.Cap(common.HexToHash("0x04"), 1); err != nil { 224 t.Fatalf("failed to flatten diff layer into accumulator: %v", err) 225 } 226 // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail 227 if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { 228 t.Errorf("stale reference returned account: %#x (err: %v)", acc, err) 229 } 230 if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale { 231 t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err) 232 } 233 if n := len(snaps.layers); n != 3 { 234 t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 3) 235 fmt.Println(snaps.layers) 236 } 237 } 238 239 // TestPostCapBasicDataAccess tests some functionality regarding capping/flattening. 240 func TestPostCapBasicDataAccess(t *testing.T) { 241 // setAccount is a helper to construct a random account entry and assign it to 242 // an account slot in a snapshot 243 setAccount := func(accKey string) map[common.Hash][]byte { 244 return map[common.Hash][]byte{ 245 common.HexToHash(accKey): randomAccount(), 246 } 247 } 248 // Create a starting base layer and a snapshot tree out of it 249 base := &diskLayer{ 250 diskdb: rawdb.NewMemoryDatabase(), 251 root: common.HexToHash("0x01"), 252 cache: fastcache.New(1024 * 500), 253 } 254 snaps := &Tree{ 255 layers: map[common.Hash]snapshot{ 256 base.root: base, 257 }, 258 } 259 // The lowest difflayer 260 snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil) 261 snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil) 262 snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xa1"), nil, setAccount("0xb2"), nil) 263 264 snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil) 265 snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xb2"), nil, setAccount("0xb3"), nil) 266 267 // checkExist verifies if an account exiss in a snapshot 268 checkExist := func(layer *diffLayer, key string) error { 269 if data, _ := layer.Account(common.HexToHash(key)); data == nil { 270 return fmt.Errorf("expected %x to exist, got nil", common.HexToHash(key)) 271 } 272 return nil 273 } 274 // shouldErr checks that an account access errors as expected 275 shouldErr := func(layer *diffLayer, key string) error { 276 if data, err := layer.Account(common.HexToHash(key)); err == nil { 277 return fmt.Errorf("expected error, got data %x", data) 278 } 279 return nil 280 } 281 // check basics 282 snap := snaps.Snapshot(common.HexToHash("0xb3")).(*diffLayer) 283 284 if err := checkExist(snap, "0xa1"); err != nil { 285 t.Error(err) 286 } 287 if err := checkExist(snap, "0xb2"); err != nil { 288 t.Error(err) 289 } 290 if err := checkExist(snap, "0xb3"); err != nil { 291 t.Error(err) 292 } 293 // Cap to a bad root should fail 294 if err := snaps.Cap(common.HexToHash("0x1337"), 0); err == nil { 295 t.Errorf("expected error, got none") 296 } 297 // Now, merge the a-chain 298 snaps.Cap(common.HexToHash("0xa3"), 0) 299 300 // At this point, a2 got merged into a1. Thus, a1 is now modified, and as a1 is 301 // the parent of b2, b2 should no longer be able to iterate into parent. 302 303 // These should still be accessible 304 if err := checkExist(snap, "0xb2"); err != nil { 305 t.Error(err) 306 } 307 if err := checkExist(snap, "0xb3"); err != nil { 308 t.Error(err) 309 } 310 // But these would need iteration into the modified parent 311 if err := shouldErr(snap, "0xa1"); err != nil { 312 t.Error(err) 313 } 314 if err := shouldErr(snap, "0xa2"); err != nil { 315 t.Error(err) 316 } 317 if err := shouldErr(snap, "0xa3"); err != nil { 318 t.Error(err) 319 } 320 // Now, merge it again, just for fun. It should now error, since a3 321 // is a disk layer 322 if err := snaps.Cap(common.HexToHash("0xa3"), 0); err == nil { 323 t.Error("expected error capping the disk layer, got none") 324 } 325 } 326 327 // TestSnaphots tests the functionality for retrieveing the snapshot 328 // with given head root and the desired depth. 329 func TestSnaphots(t *testing.T) { 330 // setAccount is a helper to construct a random account entry and assign it to 331 // an account slot in a snapshot 332 setAccount := func(accKey string) map[common.Hash][]byte { 333 return map[common.Hash][]byte{ 334 common.HexToHash(accKey): randomAccount(), 335 } 336 } 337 makeRoot := func(height uint64) common.Hash { 338 var buffer [8]byte 339 binary.BigEndian.PutUint64(buffer[:], height) 340 return common.BytesToHash(buffer[:]) 341 } 342 // Create a starting base layer and a snapshot tree out of it 343 base := &diskLayer{ 344 diskdb: rawdb.NewMemoryDatabase(), 345 root: makeRoot(1), 346 cache: fastcache.New(1024 * 500), 347 } 348 snaps := &Tree{ 349 layers: map[common.Hash]snapshot{ 350 base.root: base, 351 }, 352 } 353 // Construct the snapshots with 129 layers, flattening whatever's above that 354 var ( 355 last = common.HexToHash("0x01") 356 head common.Hash 357 ) 358 for i := 0; i < 129; i++ { 359 head = makeRoot(uint64(i + 2)) 360 snaps.Update(head, last, nil, setAccount(fmt.Sprintf("%d", i+2)), nil) 361 last = head 362 snaps.Cap(head, 128) // 130 layers (128 diffs + 1 accumulator + 1 disk) 363 } 364 var cases = []struct { 365 headRoot common.Hash 366 limit int 367 nodisk bool 368 expected int 369 expectBottom common.Hash 370 }{ 371 {head, 0, false, 0, common.Hash{}}, 372 {head, 64, false, 64, makeRoot(129 + 2 - 64)}, 373 {head, 128, false, 128, makeRoot(3)}, // Normal diff layers, no accumulator 374 {head, 129, true, 129, makeRoot(2)}, // All diff layers, including accumulator 375 {head, 130, false, 130, makeRoot(1)}, // All diff layers + disk layer 376 } 377 for i, c := range cases { 378 layers := snaps.Snapshots(c.headRoot, c.limit, c.nodisk) 379 if len(layers) != c.expected { 380 t.Errorf("non-overflow test %d: returned snapshot layers are mismatched, want %v, got %v", i, c.expected, len(layers)) 381 } 382 if len(layers) == 0 { 383 continue 384 } 385 bottommost := layers[len(layers)-1] 386 if bottommost.Root() != c.expectBottom { 387 t.Errorf("non-overflow test %d: snapshot mismatch, want %v, get %v", i, c.expectBottom, bottommost.Root()) 388 } 389 } 390 // Above we've tested the normal capping, which leaves the accumulator live. 391 // Test that if the bottommost accumulator diff layer overflows the allowed 392 // memory limit, the snapshot tree gets capped to one less layer. 393 // Commit the diff layer onto the disk and ensure it's persisted 394 defer func(memcap uint64) { aggregatorMemoryLimit = memcap }(aggregatorMemoryLimit) 395 aggregatorMemoryLimit = 0 396 397 snaps.Cap(head, 128) // 129 (128 diffs + 1 overflown accumulator + 1 disk) 398 399 cases = []struct { 400 headRoot common.Hash 401 limit int 402 nodisk bool 403 expected int 404 expectBottom common.Hash 405 }{ 406 {head, 0, false, 0, common.Hash{}}, 407 {head, 64, false, 64, makeRoot(129 + 2 - 64)}, 408 {head, 128, false, 128, makeRoot(3)}, // All diff layers, accumulator was flattened 409 {head, 129, true, 128, makeRoot(3)}, // All diff layers, accumulator was flattened 410 {head, 130, false, 129, makeRoot(2)}, // All diff layers + disk layer 411 } 412 for i, c := range cases { 413 layers := snaps.Snapshots(c.headRoot, c.limit, c.nodisk) 414 if len(layers) != c.expected { 415 t.Errorf("overflow test %d: returned snapshot layers are mismatched, want %v, got %v", i, c.expected, len(layers)) 416 } 417 if len(layers) == 0 { 418 continue 419 } 420 bottommost := layers[len(layers)-1] 421 if bottommost.Root() != c.expectBottom { 422 t.Errorf("overflow test %d: snapshot mismatch, want %v, get %v", i, c.expectBottom, bottommost.Root()) 423 } 424 } 425 }