github.com/core-coin/go-core/v2@v2.1.9/core/state/snapshot/snapshot_test.go (about) 1 // Copyright 2017 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core 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 27 "github.com/core-coin/go-core/v2/common" 28 "github.com/core-coin/go-core/v2/core/rawdb" 29 "github.com/core-coin/go-core/v2/rlp" 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"), 2); err != nil { 166 t.Fatalf("failed to merge diff layer 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 flattens every diff layer 183 // to check internal corner case around the bottom-most memory accumulator. 184 func TestDiffLayerExternalInvalidationFullFlatten(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 two 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 n := len(snaps.layers); n != 3 { 207 t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 3) 208 } 209 ref := snaps.Snapshot(common.HexToHash("0x02")) 210 211 // Flatten the diff layer into the bottom accumulator 212 if err := snaps.Cap(common.HexToHash("0x03"), 1); err != nil { 213 t.Fatalf("failed to flatten diff layer into accumulator: %v", err) 214 } 215 // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail 216 if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { 217 t.Errorf("stale reference returned account: %#x (err: %v)", acc, err) 218 } 219 if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale { 220 t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err) 221 } 222 if n := len(snaps.layers); n != 2 { 223 t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 2) 224 fmt.Println(snaps.layers) 225 } 226 } 227 228 // Tests that if a diff layer becomes stale, no active external references will 229 // be returned with junk data. This version of the test retains the bottom diff 230 // layer to check the usual mode of operation where the accumulator is retained. 231 func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) { 232 // Create an empty base layer and a snapshot tree out of it 233 base := &diskLayer{ 234 diskdb: rawdb.NewMemoryDatabase(), 235 root: common.HexToHash("0x01"), 236 cache: fastcache.New(1024 * 500), 237 } 238 snaps := &Tree{ 239 layers: map[common.Hash]snapshot{ 240 base.root: base, 241 }, 242 } 243 // Commit three diffs on top and retrieve a reference to the bottommost 244 accounts := map[common.Hash][]byte{ 245 common.HexToHash("0xa1"): randomAccount(), 246 } 247 if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil { 248 t.Fatalf("failed to create a diff layer: %v", err) 249 } 250 if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil { 251 t.Fatalf("failed to create a diff layer: %v", err) 252 } 253 if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, accounts, nil); err != nil { 254 t.Fatalf("failed to create a diff layer: %v", err) 255 } 256 if n := len(snaps.layers); n != 4 { 257 t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 4) 258 } 259 ref := snaps.Snapshot(common.HexToHash("0x02")) 260 261 // Doing a Cap operation with many allowed layers should be a no-op 262 exp := len(snaps.layers) 263 if err := snaps.Cap(common.HexToHash("0x04"), 2000); err != nil { 264 t.Fatalf("failed to flatten diff layer into accumulator: %v", err) 265 } 266 if got := len(snaps.layers); got != exp { 267 t.Errorf("layers modified, got %d exp %d", got, exp) 268 } 269 // Flatten the diff layer into the bottom accumulator 270 if err := snaps.Cap(common.HexToHash("0x04"), 2); err != nil { 271 t.Fatalf("failed to flatten diff layer into accumulator: %v", err) 272 } 273 // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail 274 if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale { 275 t.Errorf("stale reference returned account: %#x (err: %v)", acc, err) 276 } 277 if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale { 278 t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err) 279 } 280 if n := len(snaps.layers); n != 3 { 281 t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 3) 282 fmt.Println(snaps.layers) 283 } 284 } 285 286 // TestPostCapBasicDataAccess tests some functionality regarding capping/flattening. 287 func TestPostCapBasicDataAccess(t *testing.T) { 288 // setAccount is a helper to construct a random account entry and assign it to 289 // an account slot in a snapshot 290 setAccount := func(accKey string) map[common.Hash][]byte { 291 return map[common.Hash][]byte{ 292 common.HexToHash(accKey): randomAccount(), 293 } 294 } 295 // Create a starting base layer and a snapshot tree out of it 296 base := &diskLayer{ 297 diskdb: rawdb.NewMemoryDatabase(), 298 root: common.HexToHash("0x01"), 299 cache: fastcache.New(1024 * 500), 300 } 301 snaps := &Tree{ 302 layers: map[common.Hash]snapshot{ 303 base.root: base, 304 }, 305 } 306 // The lowest difflayer 307 snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil) 308 snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil) 309 snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xa1"), nil, setAccount("0xb2"), nil) 310 311 snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil) 312 snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xb2"), nil, setAccount("0xb3"), nil) 313 314 // checkExist verifies if an account exiss in a snapshot 315 checkExist := func(layer *diffLayer, key string) error { 316 if data, _ := layer.Account(common.HexToHash(key)); data == nil { 317 return fmt.Errorf("expected %x to exist, got nil", common.HexToHash(key)) 318 } 319 return nil 320 } 321 // shouldErr checks that an account access errors as expected 322 shouldErr := func(layer *diffLayer, key string) error { 323 if data, err := layer.Account(common.HexToHash(key)); err == nil { 324 return fmt.Errorf("expected error, got data %x", data) 325 } 326 return nil 327 } 328 // check basics 329 snap := snaps.Snapshot(common.HexToHash("0xb3")).(*diffLayer) 330 331 if err := checkExist(snap, "0xa1"); err != nil { 332 t.Error(err) 333 } 334 if err := checkExist(snap, "0xb2"); err != nil { 335 t.Error(err) 336 } 337 if err := checkExist(snap, "0xb3"); err != nil { 338 t.Error(err) 339 } 340 // Cap to a bad root should fail 341 if err := snaps.Cap(common.HexToHash("0x1337"), 0); err == nil { 342 t.Errorf("expected error, got none") 343 } 344 // Now, merge the a-chain 345 snaps.Cap(common.HexToHash("0xa3"), 0) 346 347 // At this point, a2 got merged into a1. Thus, a1 is now modified, and as a1 is 348 // the parent of b2, b2 should no longer be able to iterate into parent. 349 350 // These should still be accessible 351 if err := checkExist(snap, "0xb2"); err != nil { 352 t.Error(err) 353 } 354 if err := checkExist(snap, "0xb3"); err != nil { 355 t.Error(err) 356 } 357 // But these would need iteration into the modified parent 358 if err := shouldErr(snap, "0xa1"); err != nil { 359 t.Error(err) 360 } 361 if err := shouldErr(snap, "0xa2"); err != nil { 362 t.Error(err) 363 } 364 if err := shouldErr(snap, "0xa3"); err != nil { 365 t.Error(err) 366 } 367 // Now, merge it again, just for fun. It should now error, since a3 368 // is a disk layer 369 if err := snaps.Cap(common.HexToHash("0xa3"), 0); err == nil { 370 t.Error("expected error capping the disk layer, got none") 371 } 372 }