github.com/bcnmy/go-ethereum@v1.10.27/core/state/snapshot/generate_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 "os" 23 "testing" 24 "time" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core/rawdb" 28 "github.com/ethereum/go-ethereum/ethdb" 29 "github.com/ethereum/go-ethereum/log" 30 "github.com/ethereum/go-ethereum/rlp" 31 "github.com/ethereum/go-ethereum/trie" 32 "golang.org/x/crypto/sha3" 33 ) 34 35 func hashData(input []byte) common.Hash { 36 var hasher = sha3.NewLegacyKeccak256() 37 var hash common.Hash 38 hasher.Reset() 39 hasher.Write(input) 40 hasher.Sum(hash[:0]) 41 return hash 42 } 43 44 // Tests that snapshot generation from an empty database. 45 func TestGeneration(t *testing.T) { 46 // We can't use statedb to make a test trie (circular dependency), so make 47 // a fake one manually. We're going with a small account trie of 3 accounts, 48 // two of which also has the same 3-slot storage trie attached. 49 var helper = newHelper() 50 stRoot := helper.makeStorageTrie(common.Hash{}, common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false) 51 52 helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 53 helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) 54 helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) 55 56 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 57 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 58 59 root, snap := helper.CommitAndGenerate() 60 if have, want := root, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"); have != want { 61 t.Fatalf("have %#x want %#x", have, want) 62 } 63 select { 64 case <-snap.genPending: 65 // Snapshot generation succeeded 66 67 case <-time.After(3 * time.Second): 68 t.Errorf("Snapshot generation failed") 69 } 70 checkSnapRoot(t, snap, root) 71 72 // Signal abortion to the generator and wait for it to tear down 73 stop := make(chan *generatorStats) 74 snap.genAbort <- stop 75 <-stop 76 } 77 78 // Tests that snapshot generation with existent flat state. 79 func TestGenerateExistentState(t *testing.T) { 80 // We can't use statedb to make a test trie (circular dependency), so make 81 // a fake one manually. We're going with a small account trie of 3 accounts, 82 // two of which also has the same 3-slot storage trie attached. 83 var helper = newHelper() 84 85 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 86 helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 87 helper.addSnapAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 88 helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 89 90 helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) 91 helper.addSnapAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) 92 93 stRoot = helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 94 helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) 95 helper.addSnapAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) 96 helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 97 98 root, snap := helper.CommitAndGenerate() 99 select { 100 case <-snap.genPending: 101 // Snapshot generation succeeded 102 103 case <-time.After(3 * time.Second): 104 t.Errorf("Snapshot generation failed") 105 } 106 checkSnapRoot(t, snap, root) 107 108 // Signal abortion to the generator and wait for it to tear down 109 stop := make(chan *generatorStats) 110 snap.genAbort <- stop 111 <-stop 112 } 113 114 func checkSnapRoot(t *testing.T, snap *diskLayer, trieRoot common.Hash) { 115 t.Helper() 116 117 accIt := snap.AccountIterator(common.Hash{}) 118 defer accIt.Release() 119 120 snapRoot, err := generateTrieRoot(nil, accIt, common.Hash{}, stackTrieGenerate, 121 func(db ethdb.KeyValueWriter, accountHash, codeHash common.Hash, stat *generateStats) (common.Hash, error) { 122 storageIt, _ := snap.StorageIterator(accountHash, common.Hash{}) 123 defer storageIt.Release() 124 125 hash, err := generateTrieRoot(nil, storageIt, accountHash, stackTrieGenerate, nil, stat, false) 126 if err != nil { 127 return common.Hash{}, err 128 } 129 return hash, nil 130 }, newGenerateStats(), true) 131 if err != nil { 132 t.Fatal(err) 133 } 134 if snapRoot != trieRoot { 135 t.Fatalf("snaproot: %#x != trieroot #%x", snapRoot, trieRoot) 136 } 137 if err := CheckDanglingStorage(snap.diskdb); err != nil { 138 t.Fatalf("Detected dangling storages: %v", err) 139 } 140 } 141 142 type testHelper struct { 143 diskdb ethdb.Database 144 triedb *trie.Database 145 accTrie *trie.StateTrie 146 nodes *trie.MergedNodeSet 147 } 148 149 func newHelper() *testHelper { 150 diskdb := rawdb.NewMemoryDatabase() 151 triedb := trie.NewDatabase(diskdb) 152 accTrie, _ := trie.NewStateTrie(common.Hash{}, common.Hash{}, triedb) 153 return &testHelper{ 154 diskdb: diskdb, 155 triedb: triedb, 156 accTrie: accTrie, 157 nodes: trie.NewMergedNodeSet(), 158 } 159 } 160 161 func (t *testHelper) addTrieAccount(acckey string, acc *Account) { 162 val, _ := rlp.EncodeToBytes(acc) 163 t.accTrie.Update([]byte(acckey), val) 164 } 165 166 func (t *testHelper) addSnapAccount(acckey string, acc *Account) { 167 val, _ := rlp.EncodeToBytes(acc) 168 key := hashData([]byte(acckey)) 169 rawdb.WriteAccountSnapshot(t.diskdb, key, val) 170 } 171 172 func (t *testHelper) addAccount(acckey string, acc *Account) { 173 t.addTrieAccount(acckey, acc) 174 t.addSnapAccount(acckey, acc) 175 } 176 177 func (t *testHelper) addSnapStorage(accKey string, keys []string, vals []string) { 178 accHash := hashData([]byte(accKey)) 179 for i, key := range keys { 180 rawdb.WriteStorageSnapshot(t.diskdb, accHash, hashData([]byte(key)), []byte(vals[i])) 181 } 182 } 183 184 func (t *testHelper) makeStorageTrie(stateRoot, owner common.Hash, keys []string, vals []string, commit bool) []byte { 185 stTrie, _ := trie.NewStateTrie(owner, common.Hash{}, t.triedb) 186 for i, k := range keys { 187 stTrie.Update([]byte(k), []byte(vals[i])) 188 } 189 if !commit { 190 return stTrie.Hash().Bytes() 191 } 192 root, nodes, _ := stTrie.Commit(false) 193 if nodes != nil { 194 t.nodes.Merge(nodes) 195 } 196 return root.Bytes() 197 } 198 199 func (t *testHelper) Commit() common.Hash { 200 root, nodes, _ := t.accTrie.Commit(true) 201 if nodes != nil { 202 t.nodes.Merge(nodes) 203 } 204 t.triedb.Update(t.nodes) 205 t.triedb.Commit(root, false, nil) 206 return root 207 } 208 209 func (t *testHelper) CommitAndGenerate() (common.Hash, *diskLayer) { 210 root := t.Commit() 211 snap := generateSnapshot(t.diskdb, t.triedb, 16, root) 212 return root, snap 213 } 214 215 // Tests that snapshot generation with existent flat state, where the flat state 216 // contains some errors: 217 // - the contract with empty storage root but has storage entries in the disk 218 // - the contract with non empty storage root but empty storage slots 219 // - the contract(non-empty storage) misses some storage slots 220 // - miss in the beginning 221 // - miss in the middle 222 // - miss in the end 223 // - the contract(non-empty storage) has wrong storage slots 224 // - wrong slots in the beginning 225 // - wrong slots in the middle 226 // - wrong slots in the end 227 // - the contract(non-empty storage) has extra storage slots 228 // - extra slots in the beginning 229 // - extra slots in the middle 230 // - extra slots in the end 231 func TestGenerateExistentStateWithWrongStorage(t *testing.T) { 232 helper := newHelper() 233 234 // Account one, empty root but non-empty database 235 helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) 236 helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 237 238 // Account two, non empty root but empty database 239 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 240 helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 241 242 // Miss slots 243 { 244 // Account three, non empty root but misses slots in the beginning 245 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 246 helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 247 helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"}) 248 249 // Account four, non empty root but misses slots in the middle 250 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 251 helper.addAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 252 helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"}) 253 254 // Account five, non empty root but misses slots in the end 255 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-5")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 256 helper.addAccount("acc-5", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 257 helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"}) 258 } 259 260 // Wrong storage slots 261 { 262 // Account six, non empty root but wrong slots in the beginning 263 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 264 helper.addAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 265 helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"}) 266 267 // Account seven, non empty root but wrong slots in the middle 268 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-7")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 269 helper.addAccount("acc-7", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 270 helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"}) 271 272 // Account eight, non empty root but wrong slots in the end 273 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-8")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 274 helper.addAccount("acc-8", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 275 helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"}) 276 277 // Account 9, non empty root but rotated slots 278 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-9")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 279 helper.addAccount("acc-9", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 280 helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"}) 281 } 282 283 // Extra storage slots 284 { 285 // Account 10, non empty root but extra slots in the beginning 286 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-10")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 287 helper.addAccount("acc-10", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 288 helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"}) 289 290 // Account 11, non empty root but extra slots in the middle 291 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-11")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 292 helper.addAccount("acc-11", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 293 helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"}) 294 295 // Account 12, non empty root but extra slots in the end 296 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-12")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 297 helper.addAccount("acc-12", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 298 helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"}) 299 } 300 301 root, snap := helper.CommitAndGenerate() 302 t.Logf("Root: %#x\n", root) // Root = 0x8746cce9fd9c658b2cfd639878ed6584b7a2b3e73bb40f607fcfa156002429a0 303 304 select { 305 case <-snap.genPending: 306 // Snapshot generation succeeded 307 308 case <-time.After(3 * time.Second): 309 t.Errorf("Snapshot generation failed") 310 } 311 checkSnapRoot(t, snap, root) 312 // Signal abortion to the generator and wait for it to tear down 313 stop := make(chan *generatorStats) 314 snap.genAbort <- stop 315 <-stop 316 } 317 318 // Tests that snapshot generation with existent flat state, where the flat state 319 // contains some errors: 320 // - miss accounts 321 // - wrong accounts 322 // - extra accounts 323 func TestGenerateExistentStateWithWrongAccounts(t *testing.T) { 324 helper := newHelper() 325 326 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 327 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 328 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 329 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 330 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 331 332 // Trie accounts [acc-1, acc-2, acc-3, acc-4, acc-6] 333 // Extra accounts [acc-0, acc-5, acc-7] 334 335 // Missing accounts, only in the trie 336 { 337 helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // Beginning 338 helper.addTrieAccount("acc-4", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // Middle 339 helper.addTrieAccount("acc-6", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // End 340 } 341 342 // Wrong accounts 343 { 344 helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 345 helper.addSnapAccount("acc-2", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")}) 346 347 helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 348 helper.addSnapAccount("acc-3", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) 349 } 350 351 // Extra accounts, only in the snap 352 { 353 helper.addSnapAccount("acc-0", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyRoot.Bytes()}) // before the beginning 354 helper.addSnapAccount("acc-5", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: common.Hex2Bytes("0x1234")}) // Middle 355 helper.addSnapAccount("acc-7", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyRoot.Bytes()}) // after the end 356 } 357 358 root, snap := helper.CommitAndGenerate() 359 t.Logf("Root: %#x\n", root) // Root = 0x825891472281463511e7ebcc7f109e4f9200c20fa384754e11fd605cd98464e8 360 361 select { 362 case <-snap.genPending: 363 // Snapshot generation succeeded 364 365 case <-time.After(3 * time.Second): 366 t.Errorf("Snapshot generation failed") 367 } 368 checkSnapRoot(t, snap, root) 369 370 // Signal abortion to the generator and wait for it to tear down 371 stop := make(chan *generatorStats) 372 snap.genAbort <- stop 373 <-stop 374 } 375 376 // Tests that snapshot generation errors out correctly in case of a missing trie 377 // node in the account trie. 378 func TestGenerateCorruptAccountTrie(t *testing.T) { 379 // We can't use statedb to make a test trie (circular dependency), so make 380 // a fake one manually. We're going with a small account trie of 3 accounts, 381 // without any storage slots to keep the test smaller. 382 helper := newHelper() 383 384 helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074 385 helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 386 helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4 387 388 root := helper.Commit() // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978 389 390 // Delete an account trie leaf and ensure the generator chokes 391 helper.triedb.Commit(root, false, nil) 392 helper.diskdb.Delete(common.HexToHash("0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7").Bytes()) 393 394 snap := generateSnapshot(helper.diskdb, helper.triedb, 16, root) 395 select { 396 case <-snap.genPending: 397 // Snapshot generation succeeded 398 t.Errorf("Snapshot generated against corrupt account trie") 399 400 case <-time.After(time.Second): 401 // Not generated fast enough, hopefully blocked inside on missing trie node fail 402 } 403 // Signal abortion to the generator and wait for it to tear down 404 stop := make(chan *generatorStats) 405 snap.genAbort <- stop 406 <-stop 407 } 408 409 // Tests that snapshot generation errors out correctly in case of a missing root 410 // trie node for a storage trie. It's similar to internal corruption but it is 411 // handled differently inside the generator. 412 func TestGenerateMissingStorageTrie(t *testing.T) { 413 // We can't use statedb to make a test trie (circular dependency), so make 414 // a fake one manually. We're going with a small account trie of 3 accounts, 415 // two of which also has the same 3-slot storage trie attached. 416 helper := newHelper() 417 418 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 419 helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e 420 helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 421 stRoot = helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 422 helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 423 424 root := helper.Commit() 425 426 // Delete a storage trie root and ensure the generator chokes 427 helper.diskdb.Delete(stRoot) 428 429 snap := generateSnapshot(helper.diskdb, helper.triedb, 16, root) 430 select { 431 case <-snap.genPending: 432 // Snapshot generation succeeded 433 t.Errorf("Snapshot generated against corrupt storage trie") 434 435 case <-time.After(time.Second): 436 // Not generated fast enough, hopefully blocked inside on missing trie node fail 437 } 438 // Signal abortion to the generator and wait for it to tear down 439 stop := make(chan *generatorStats) 440 snap.genAbort <- stop 441 <-stop 442 } 443 444 // Tests that snapshot generation errors out correctly in case of a missing trie 445 // node in a storage trie. 446 func TestGenerateCorruptStorageTrie(t *testing.T) { 447 // We can't use statedb to make a test trie (circular dependency), so make 448 // a fake one manually. We're going with a small account trie of 3 accounts, 449 // two of which also has the same 3-slot storage trie attached. 450 helper := newHelper() 451 452 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 453 helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e 454 helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 455 stRoot = helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 456 helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 457 458 root := helper.Commit() 459 460 // Delete a storage trie leaf and ensure the generator chokes 461 helper.diskdb.Delete(common.HexToHash("0x18a0f4d79cff4459642dd7604f303886ad9d77c30cf3d7d7cedb3a693ab6d371").Bytes()) 462 463 snap := generateSnapshot(helper.diskdb, helper.triedb, 16, root) 464 select { 465 case <-snap.genPending: 466 // Snapshot generation succeeded 467 t.Errorf("Snapshot generated against corrupt storage trie") 468 469 case <-time.After(time.Second): 470 // Not generated fast enough, hopefully blocked inside on missing trie node fail 471 } 472 // Signal abortion to the generator and wait for it to tear down 473 stop := make(chan *generatorStats) 474 snap.genAbort <- stop 475 <-stop 476 } 477 478 // Tests that snapshot generation when an extra account with storage exists in the snap state. 479 func TestGenerateWithExtraAccounts(t *testing.T) { 480 helper := newHelper() 481 { 482 // Account one in the trie 483 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), 484 []string{"key-1", "key-2", "key-3", "key-4", "key-5"}, 485 []string{"val-1", "val-2", "val-3", "val-4", "val-5"}, 486 true, 487 ) 488 acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()} 489 val, _ := rlp.EncodeToBytes(acc) 490 helper.accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e 491 492 // Identical in the snap 493 key := hashData([]byte("acc-1")) 494 rawdb.WriteAccountSnapshot(helper.triedb.DiskDB(), key, val) 495 rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-1")), []byte("val-1")) 496 rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-2")), []byte("val-2")) 497 rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-3")), []byte("val-3")) 498 rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-4")), []byte("val-4")) 499 rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("key-5")), []byte("val-5")) 500 } 501 { 502 // Account two exists only in the snapshot 503 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-2")), 504 []string{"key-1", "key-2", "key-3", "key-4", "key-5"}, 505 []string{"val-1", "val-2", "val-3", "val-4", "val-5"}, 506 true, 507 ) 508 acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()} 509 val, _ := rlp.EncodeToBytes(acc) 510 key := hashData([]byte("acc-2")) 511 rawdb.WriteAccountSnapshot(helper.triedb.DiskDB(), key, val) 512 rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("b-key-1")), []byte("b-val-1")) 513 rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("b-key-2")), []byte("b-val-2")) 514 rawdb.WriteStorageSnapshot(helper.triedb.DiskDB(), key, hashData([]byte("b-key-3")), []byte("b-val-3")) 515 } 516 root := helper.Commit() 517 518 // To verify the test: If we now inspect the snap db, there should exist extraneous storage items 519 if data := rawdb.ReadStorageSnapshot(helper.triedb.DiskDB(), hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data == nil { 520 t.Fatalf("expected snap storage to exist") 521 } 522 snap := generateSnapshot(helper.diskdb, helper.triedb, 16, root) 523 select { 524 case <-snap.genPending: 525 // Snapshot generation succeeded 526 527 case <-time.After(3 * time.Second): 528 t.Errorf("Snapshot generation failed") 529 } 530 checkSnapRoot(t, snap, root) 531 532 // Signal abortion to the generator and wait for it to tear down 533 stop := make(chan *generatorStats) 534 snap.genAbort <- stop 535 <-stop 536 // If we now inspect the snap db, there should exist no extraneous storage items 537 if data := rawdb.ReadStorageSnapshot(helper.triedb.DiskDB(), hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data != nil { 538 t.Fatalf("expected slot to be removed, got %v", string(data)) 539 } 540 } 541 542 func enableLogging() { 543 log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) 544 } 545 546 // Tests that snapshot generation when an extra account with storage exists in the snap state. 547 func TestGenerateWithManyExtraAccounts(t *testing.T) { 548 if false { 549 enableLogging() 550 } 551 helper := newHelper() 552 { 553 // Account one in the trie 554 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), 555 []string{"key-1", "key-2", "key-3"}, 556 []string{"val-1", "val-2", "val-3"}, 557 true, 558 ) 559 acc := &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()} 560 val, _ := rlp.EncodeToBytes(acc) 561 helper.accTrie.Update([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e 562 563 // Identical in the snap 564 key := hashData([]byte("acc-1")) 565 rawdb.WriteAccountSnapshot(helper.diskdb, key, val) 566 rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-1")), []byte("val-1")) 567 rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-2")), []byte("val-2")) 568 rawdb.WriteStorageSnapshot(helper.diskdb, key, hashData([]byte("key-3")), []byte("val-3")) 569 } 570 { 571 // 100 accounts exist only in snapshot 572 for i := 0; i < 1000; i++ { 573 //acc := &Account{Balance: big.NewInt(int64(i)), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()} 574 acc := &Account{Balance: big.NewInt(int64(i)), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} 575 val, _ := rlp.EncodeToBytes(acc) 576 key := hashData([]byte(fmt.Sprintf("acc-%d", i))) 577 rawdb.WriteAccountSnapshot(helper.diskdb, key, val) 578 } 579 } 580 root, snap := helper.CommitAndGenerate() 581 select { 582 case <-snap.genPending: 583 // Snapshot generation succeeded 584 585 case <-time.After(3 * time.Second): 586 t.Errorf("Snapshot generation failed") 587 } 588 checkSnapRoot(t, snap, root) 589 // Signal abortion to the generator and wait for it to tear down 590 stop := make(chan *generatorStats) 591 snap.genAbort <- stop 592 <-stop 593 } 594 595 // Tests this case 596 // maxAccountRange 3 597 // snapshot-accounts: 01, 02, 03, 04, 05, 06, 07 598 // trie-accounts: 03, 07 599 // 600 // We iterate three snapshot storage slots (max = 3) from the database. They are 0x01, 0x02, 0x03. 601 // The trie has a lot of deletions. 602 // So in trie, we iterate 2 entries 0x03, 0x07. We create the 0x07 in the database and abort the procedure, because the trie is exhausted. 603 // But in the database, we still have the stale storage slots 0x04, 0x05. They are not iterated yet, but the procedure is finished. 604 func TestGenerateWithExtraBeforeAndAfter(t *testing.T) { 605 accountCheckRange = 3 606 if false { 607 enableLogging() 608 } 609 helper := newHelper() 610 { 611 acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} 612 val, _ := rlp.EncodeToBytes(acc) 613 helper.accTrie.Update(common.HexToHash("0x03").Bytes(), val) 614 helper.accTrie.Update(common.HexToHash("0x07").Bytes(), val) 615 616 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x01"), val) 617 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x02"), val) 618 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x03"), val) 619 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x04"), val) 620 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x05"), val) 621 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x06"), val) 622 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x07"), val) 623 } 624 root, snap := helper.CommitAndGenerate() 625 select { 626 case <-snap.genPending: 627 // Snapshot generation succeeded 628 629 case <-time.After(3 * time.Second): 630 t.Errorf("Snapshot generation failed") 631 } 632 checkSnapRoot(t, snap, root) 633 // Signal abortion to the generator and wait for it to tear down 634 stop := make(chan *generatorStats) 635 snap.genAbort <- stop 636 <-stop 637 } 638 639 // TestGenerateWithMalformedSnapdata tests what happes if we have some junk 640 // in the snapshot database, which cannot be parsed back to an account 641 func TestGenerateWithMalformedSnapdata(t *testing.T) { 642 accountCheckRange = 3 643 if false { 644 enableLogging() 645 } 646 helper := newHelper() 647 { 648 acc := &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()} 649 val, _ := rlp.EncodeToBytes(acc) 650 helper.accTrie.Update(common.HexToHash("0x03").Bytes(), val) 651 652 junk := make([]byte, 100) 653 copy(junk, []byte{0xde, 0xad}) 654 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x02"), junk) 655 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x03"), junk) 656 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x04"), junk) 657 rawdb.WriteAccountSnapshot(helper.diskdb, common.HexToHash("0x05"), junk) 658 } 659 root, snap := helper.CommitAndGenerate() 660 select { 661 case <-snap.genPending: 662 // Snapshot generation succeeded 663 664 case <-time.After(3 * time.Second): 665 t.Errorf("Snapshot generation failed") 666 } 667 checkSnapRoot(t, snap, root) 668 // Signal abortion to the generator and wait for it to tear down 669 stop := make(chan *generatorStats) 670 snap.genAbort <- stop 671 <-stop 672 // If we now inspect the snap db, there should exist no extraneous storage items 673 if data := rawdb.ReadStorageSnapshot(helper.diskdb, hashData([]byte("acc-2")), hashData([]byte("b-key-1"))); data != nil { 674 t.Fatalf("expected slot to be removed, got %v", string(data)) 675 } 676 } 677 678 func TestGenerateFromEmptySnap(t *testing.T) { 679 //enableLogging() 680 accountCheckRange = 10 681 storageCheckRange = 20 682 helper := newHelper() 683 // Add 1K accounts to the trie 684 for i := 0; i < 400; i++ { 685 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte(fmt.Sprintf("acc-%d", i))), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 686 helper.addTrieAccount(fmt.Sprintf("acc-%d", i), 687 &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 688 } 689 root, snap := helper.CommitAndGenerate() 690 t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4 691 692 select { 693 case <-snap.genPending: 694 // Snapshot generation succeeded 695 696 case <-time.After(3 * time.Second): 697 t.Errorf("Snapshot generation failed") 698 } 699 checkSnapRoot(t, snap, root) 700 // Signal abortion to the generator and wait for it to tear down 701 stop := make(chan *generatorStats) 702 snap.genAbort <- stop 703 <-stop 704 } 705 706 // Tests that snapshot generation with existent flat state, where the flat state 707 // storage is correct, but incomplete. 708 // The incomplete part is on the second range 709 // snap: [ 0x01, 0x02, 0x03, 0x04] , [ 0x05, 0x06, 0x07, {missing}] (with storageCheck = 4) 710 // trie: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 711 // This hits a case where the snap verification passes, but there are more elements in the trie 712 // which we must also add. 713 func TestGenerateWithIncompleteStorage(t *testing.T) { 714 storageCheckRange = 4 715 helper := newHelper() 716 stKeys := []string{"1", "2", "3", "4", "5", "6", "7", "8"} 717 stVals := []string{"v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8"} 718 // We add 8 accounts, each one is missing exactly one of the storage slots. This means 719 // we don't have to order the keys and figure out exactly which hash-key winds up 720 // on the sensitive spots at the boundaries 721 for i := 0; i < 8; i++ { 722 accKey := fmt.Sprintf("acc-%d", i) 723 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte(accKey)), stKeys, stVals, true) 724 helper.addAccount(accKey, &Account{Balance: big.NewInt(int64(i)), Root: stRoot, CodeHash: emptyCode.Bytes()}) 725 var moddedKeys []string 726 var moddedVals []string 727 for ii := 0; ii < 8; ii++ { 728 if ii != i { 729 moddedKeys = append(moddedKeys, stKeys[ii]) 730 moddedVals = append(moddedVals, stVals[ii]) 731 } 732 } 733 helper.addSnapStorage(accKey, moddedKeys, moddedVals) 734 } 735 root, snap := helper.CommitAndGenerate() 736 t.Logf("Root: %#x\n", root) // Root: 0xca73f6f05ba4ca3024ef340ef3dfca8fdabc1b677ff13f5a9571fd49c16e67ff 737 738 select { 739 case <-snap.genPending: 740 // Snapshot generation succeeded 741 742 case <-time.After(3 * time.Second): 743 t.Errorf("Snapshot generation failed") 744 } 745 checkSnapRoot(t, snap, root) 746 // Signal abortion to the generator and wait for it to tear down 747 stop := make(chan *generatorStats) 748 snap.genAbort <- stop 749 <-stop 750 } 751 752 func incKey(key []byte) []byte { 753 for i := len(key) - 1; i >= 0; i-- { 754 key[i]++ 755 if key[i] != 0x0 { 756 break 757 } 758 } 759 return key 760 } 761 762 func decKey(key []byte) []byte { 763 for i := len(key) - 1; i >= 0; i-- { 764 key[i]-- 765 if key[i] != 0xff { 766 break 767 } 768 } 769 return key 770 } 771 772 func populateDangling(disk ethdb.KeyValueStore) { 773 populate := func(accountHash common.Hash, keys []string, vals []string) { 774 for i, key := range keys { 775 rawdb.WriteStorageSnapshot(disk, accountHash, hashData([]byte(key)), []byte(vals[i])) 776 } 777 } 778 // Dangling storages of the "first" account 779 populate(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 780 781 // Dangling storages of the "last" account 782 populate(common.HexToHash("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 783 784 // Dangling storages around the account 1 785 hash := decKey(hashData([]byte("acc-1")).Bytes()) 786 populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 787 hash = incKey(hashData([]byte("acc-1")).Bytes()) 788 populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 789 790 // Dangling storages around the account 2 791 hash = decKey(hashData([]byte("acc-2")).Bytes()) 792 populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 793 hash = incKey(hashData([]byte("acc-2")).Bytes()) 794 populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 795 796 // Dangling storages around the account 3 797 hash = decKey(hashData([]byte("acc-3")).Bytes()) 798 populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 799 hash = incKey(hashData([]byte("acc-3")).Bytes()) 800 populate(common.BytesToHash(hash), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 801 802 // Dangling storages of the random account 803 populate(randomHash(), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 804 populate(randomHash(), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 805 populate(randomHash(), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 806 } 807 808 // Tests that snapshot generation with dangling storages. Dangling storage means 809 // the storage data is existent while the corresponding account data is missing. 810 // 811 // This test will populate some dangling storages to see if they can be cleaned up. 812 func TestGenerateCompleteSnapshotWithDanglingStorage(t *testing.T) { 813 var helper = newHelper() 814 815 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 816 helper.addAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 817 helper.addAccount("acc-2", &Account{Balance: big.NewInt(1), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) 818 819 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 820 helper.addAccount("acc-3", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 821 822 helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 823 helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) 824 825 populateDangling(helper.diskdb) 826 827 root, snap := helper.CommitAndGenerate() 828 select { 829 case <-snap.genPending: 830 // Snapshot generation succeeded 831 832 case <-time.After(3 * time.Second): 833 t.Errorf("Snapshot generation failed") 834 } 835 checkSnapRoot(t, snap, root) 836 837 // Signal abortion to the generator and wait for it to tear down 838 stop := make(chan *generatorStats) 839 snap.genAbort <- stop 840 <-stop 841 } 842 843 // Tests that snapshot generation with dangling storages. Dangling storage means 844 // the storage data is existent while the corresponding account data is missing. 845 // 846 // This test will populate some dangling storages to see if they can be cleaned up. 847 func TestGenerateBrokenSnapshotWithDanglingStorage(t *testing.T) { 848 var helper = newHelper() 849 850 stRoot := helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 851 helper.addTrieAccount("acc-1", &Account{Balance: big.NewInt(1), Root: stRoot, CodeHash: emptyCode.Bytes()}) 852 helper.addTrieAccount("acc-2", &Account{Balance: big.NewInt(2), Root: emptyRoot.Bytes(), CodeHash: emptyCode.Bytes()}) 853 854 helper.makeStorageTrie(common.Hash{}, hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) 855 helper.addTrieAccount("acc-3", &Account{Balance: big.NewInt(3), Root: stRoot, CodeHash: emptyCode.Bytes()}) 856 857 populateDangling(helper.diskdb) 858 859 root, snap := helper.CommitAndGenerate() 860 select { 861 case <-snap.genPending: 862 // Snapshot generation succeeded 863 864 case <-time.After(3 * time.Second): 865 t.Errorf("Snapshot generation failed") 866 } 867 checkSnapRoot(t, snap, root) 868 869 // Signal abortion to the generator and wait for it to tear down 870 stop := make(chan *generatorStats) 871 snap.genAbort <- stop 872 <-stop 873 }