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