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