github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/core/state/snapshot/difflayer_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 "bytes" 21 "math/rand" 22 "testing" 23 24 "github.com/VictoriaMetrics/fastcache" 25 "github.com/tirogen/go-ethereum/common" 26 "github.com/tirogen/go-ethereum/crypto" 27 "github.com/tirogen/go-ethereum/ethdb/memorydb" 28 ) 29 30 func copyDestructs(destructs map[common.Hash]struct{}) map[common.Hash]struct{} { 31 copy := make(map[common.Hash]struct{}) 32 for hash := range destructs { 33 copy[hash] = struct{}{} 34 } 35 return copy 36 } 37 38 func copyAccounts(accounts map[common.Hash][]byte) map[common.Hash][]byte { 39 copy := make(map[common.Hash][]byte) 40 for hash, blob := range accounts { 41 copy[hash] = blob 42 } 43 return copy 44 } 45 46 func copyStorage(storage map[common.Hash]map[common.Hash][]byte) map[common.Hash]map[common.Hash][]byte { 47 copy := make(map[common.Hash]map[common.Hash][]byte) 48 for accHash, slots := range storage { 49 copy[accHash] = make(map[common.Hash][]byte) 50 for slotHash, blob := range slots { 51 copy[accHash][slotHash] = blob 52 } 53 } 54 return copy 55 } 56 57 // TestMergeBasics tests some simple merges 58 func TestMergeBasics(t *testing.T) { 59 var ( 60 destructs = make(map[common.Hash]struct{}) 61 accounts = make(map[common.Hash][]byte) 62 storage = make(map[common.Hash]map[common.Hash][]byte) 63 ) 64 // Fill up a parent 65 for i := 0; i < 100; i++ { 66 h := randomHash() 67 data := randomAccount() 68 69 accounts[h] = data 70 if rand.Intn(4) == 0 { 71 destructs[h] = struct{}{} 72 } 73 if rand.Intn(2) == 0 { 74 accStorage := make(map[common.Hash][]byte) 75 value := make([]byte, 32) 76 rand.Read(value) 77 accStorage[randomHash()] = value 78 storage[h] = accStorage 79 } 80 } 81 // Add some (identical) layers on top 82 parent := newDiffLayer(emptyLayer(), common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) 83 child := newDiffLayer(parent, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) 84 child = newDiffLayer(child, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) 85 child = newDiffLayer(child, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) 86 child = newDiffLayer(child, common.Hash{}, copyDestructs(destructs), copyAccounts(accounts), copyStorage(storage)) 87 // And flatten 88 merged := (child.flatten()).(*diffLayer) 89 90 { // Check account lists 91 if have, want := len(merged.accountList), 0; have != want { 92 t.Errorf("accountList wrong: have %v, want %v", have, want) 93 } 94 if have, want := len(merged.AccountList()), len(accounts); have != want { 95 t.Errorf("AccountList() wrong: have %v, want %v", have, want) 96 } 97 if have, want := len(merged.accountList), len(accounts); have != want { 98 t.Errorf("accountList [2] wrong: have %v, want %v", have, want) 99 } 100 } 101 { // Check account drops 102 if have, want := len(merged.destructSet), len(destructs); have != want { 103 t.Errorf("accountDrop wrong: have %v, want %v", have, want) 104 } 105 } 106 { // Check storage lists 107 i := 0 108 for aHash, sMap := range storage { 109 if have, want := len(merged.storageList), i; have != want { 110 t.Errorf("[1] storageList wrong: have %v, want %v", have, want) 111 } 112 list, _ := merged.StorageList(aHash) 113 if have, want := len(list), len(sMap); have != want { 114 t.Errorf("[2] StorageList() wrong: have %v, want %v", have, want) 115 } 116 if have, want := len(merged.storageList[aHash]), len(sMap); have != want { 117 t.Errorf("storageList wrong: have %v, want %v", have, want) 118 } 119 i++ 120 } 121 } 122 } 123 124 // TestMergeDelete tests some deletion 125 func TestMergeDelete(t *testing.T) { 126 var ( 127 storage = make(map[common.Hash]map[common.Hash][]byte) 128 ) 129 // Fill up a parent 130 h1 := common.HexToHash("0x01") 131 h2 := common.HexToHash("0x02") 132 133 flipDrops := func() map[common.Hash]struct{} { 134 return map[common.Hash]struct{}{ 135 h2: {}, 136 } 137 } 138 flipAccs := func() map[common.Hash][]byte { 139 return map[common.Hash][]byte{ 140 h1: randomAccount(), 141 } 142 } 143 flopDrops := func() map[common.Hash]struct{} { 144 return map[common.Hash]struct{}{ 145 h1: {}, 146 } 147 } 148 flopAccs := func() map[common.Hash][]byte { 149 return map[common.Hash][]byte{ 150 h2: randomAccount(), 151 } 152 } 153 // Add some flipAccs-flopping layers on top 154 parent := newDiffLayer(emptyLayer(), common.Hash{}, flipDrops(), flipAccs(), storage) 155 child := parent.Update(common.Hash{}, flopDrops(), flopAccs(), storage) 156 child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage) 157 child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage) 158 child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage) 159 child = child.Update(common.Hash{}, flopDrops(), flopAccs(), storage) 160 child = child.Update(common.Hash{}, flipDrops(), flipAccs(), storage) 161 162 if data, _ := child.Account(h1); data == nil { 163 t.Errorf("last diff layer: expected %x account to be non-nil", h1) 164 } 165 if data, _ := child.Account(h2); data != nil { 166 t.Errorf("last diff layer: expected %x account to be nil", h2) 167 } 168 if _, ok := child.destructSet[h1]; ok { 169 t.Errorf("last diff layer: expected %x drop to be missing", h1) 170 } 171 if _, ok := child.destructSet[h2]; !ok { 172 t.Errorf("last diff layer: expected %x drop to be present", h1) 173 } 174 // And flatten 175 merged := (child.flatten()).(*diffLayer) 176 177 if data, _ := merged.Account(h1); data == nil { 178 t.Errorf("merged layer: expected %x account to be non-nil", h1) 179 } 180 if data, _ := merged.Account(h2); data != nil { 181 t.Errorf("merged layer: expected %x account to be nil", h2) 182 } 183 if _, ok := merged.destructSet[h1]; !ok { // Note, drops stay alive until persisted to disk! 184 t.Errorf("merged diff layer: expected %x drop to be present", h1) 185 } 186 if _, ok := merged.destructSet[h2]; !ok { // Note, drops stay alive until persisted to disk! 187 t.Errorf("merged diff layer: expected %x drop to be present", h1) 188 } 189 // If we add more granular metering of memory, we can enable this again, 190 // but it's not implemented for now 191 //if have, want := merged.memory, child.memory; have != want { 192 // t.Errorf("mem wrong: have %d, want %d", have, want) 193 //} 194 } 195 196 // This tests that if we create a new account, and set a slot, and then merge 197 // it, the lists will be correct. 198 func TestInsertAndMerge(t *testing.T) { 199 // Fill up a parent 200 var ( 201 acc = common.HexToHash("0x01") 202 slot = common.HexToHash("0x02") 203 parent *diffLayer 204 child *diffLayer 205 ) 206 { 207 var ( 208 destructs = make(map[common.Hash]struct{}) 209 accounts = make(map[common.Hash][]byte) 210 storage = make(map[common.Hash]map[common.Hash][]byte) 211 ) 212 parent = newDiffLayer(emptyLayer(), common.Hash{}, destructs, accounts, storage) 213 } 214 { 215 var ( 216 destructs = make(map[common.Hash]struct{}) 217 accounts = make(map[common.Hash][]byte) 218 storage = make(map[common.Hash]map[common.Hash][]byte) 219 ) 220 accounts[acc] = randomAccount() 221 storage[acc] = make(map[common.Hash][]byte) 222 storage[acc][slot] = []byte{0x01} 223 child = newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) 224 } 225 // And flatten 226 merged := (child.flatten()).(*diffLayer) 227 { // Check that slot value is present 228 have, _ := merged.Storage(acc, slot) 229 if want := []byte{0x01}; !bytes.Equal(have, want) { 230 t.Errorf("merged slot value wrong: have %x, want %x", have, want) 231 } 232 } 233 } 234 235 func emptyLayer() *diskLayer { 236 return &diskLayer{ 237 diskdb: memorydb.New(), 238 cache: fastcache.New(500 * 1024), 239 } 240 } 241 242 // BenchmarkSearch checks how long it takes to find a non-existing key 243 // BenchmarkSearch-6 200000 10481 ns/op (1K per layer) 244 // BenchmarkSearch-6 200000 10760 ns/op (10K per layer) 245 // BenchmarkSearch-6 100000 17866 ns/op 246 // 247 // BenchmarkSearch-6 500000 3723 ns/op (10k per layer, only top-level RLock() 248 func BenchmarkSearch(b *testing.B) { 249 // First, we set up 128 diff layers, with 1K items each 250 fill := func(parent snapshot) *diffLayer { 251 var ( 252 destructs = make(map[common.Hash]struct{}) 253 accounts = make(map[common.Hash][]byte) 254 storage = make(map[common.Hash]map[common.Hash][]byte) 255 ) 256 for i := 0; i < 10000; i++ { 257 accounts[randomHash()] = randomAccount() 258 } 259 return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) 260 } 261 var layer snapshot 262 layer = emptyLayer() 263 for i := 0; i < 128; i++ { 264 layer = fill(layer) 265 } 266 key := crypto.Keccak256Hash([]byte{0x13, 0x38}) 267 b.ResetTimer() 268 for i := 0; i < b.N; i++ { 269 layer.AccountRLP(key) 270 } 271 } 272 273 // BenchmarkSearchSlot checks how long it takes to find a non-existing key 274 // - Number of layers: 128 275 // - Each layers contains the account, with a couple of storage slots 276 // BenchmarkSearchSlot-6 100000 14554 ns/op 277 // BenchmarkSearchSlot-6 100000 22254 ns/op (when checking parent root using mutex) 278 // BenchmarkSearchSlot-6 100000 14551 ns/op (when checking parent number using atomic) 279 // With bloom filter: 280 // BenchmarkSearchSlot-6 3467835 351 ns/op 281 func BenchmarkSearchSlot(b *testing.B) { 282 // First, we set up 128 diff layers, with 1K items each 283 accountKey := crypto.Keccak256Hash([]byte{0x13, 0x37}) 284 storageKey := crypto.Keccak256Hash([]byte{0x13, 0x37}) 285 accountRLP := randomAccount() 286 fill := func(parent snapshot) *diffLayer { 287 var ( 288 destructs = make(map[common.Hash]struct{}) 289 accounts = make(map[common.Hash][]byte) 290 storage = make(map[common.Hash]map[common.Hash][]byte) 291 ) 292 accounts[accountKey] = accountRLP 293 294 accStorage := make(map[common.Hash][]byte) 295 for i := 0; i < 5; i++ { 296 value := make([]byte, 32) 297 rand.Read(value) 298 accStorage[randomHash()] = value 299 storage[accountKey] = accStorage 300 } 301 return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) 302 } 303 var layer snapshot 304 layer = emptyLayer() 305 for i := 0; i < 128; i++ { 306 layer = fill(layer) 307 } 308 b.ResetTimer() 309 for i := 0; i < b.N; i++ { 310 layer.Storage(accountKey, storageKey) 311 } 312 } 313 314 // With accountList and sorting 315 // BenchmarkFlatten-6 50 29890856 ns/op 316 // 317 // Without sorting and tracking accountList 318 // BenchmarkFlatten-6 300 5511511 ns/op 319 func BenchmarkFlatten(b *testing.B) { 320 fill := func(parent snapshot) *diffLayer { 321 var ( 322 destructs = make(map[common.Hash]struct{}) 323 accounts = make(map[common.Hash][]byte) 324 storage = make(map[common.Hash]map[common.Hash][]byte) 325 ) 326 for i := 0; i < 100; i++ { 327 accountKey := randomHash() 328 accounts[accountKey] = randomAccount() 329 330 accStorage := make(map[common.Hash][]byte) 331 for i := 0; i < 20; i++ { 332 value := make([]byte, 32) 333 rand.Read(value) 334 accStorage[randomHash()] = value 335 } 336 storage[accountKey] = accStorage 337 } 338 return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) 339 } 340 b.ResetTimer() 341 for i := 0; i < b.N; i++ { 342 b.StopTimer() 343 var layer snapshot 344 layer = emptyLayer() 345 for i := 1; i < 128; i++ { 346 layer = fill(layer) 347 } 348 b.StartTimer() 349 350 for i := 1; i < 128; i++ { 351 dl, ok := layer.(*diffLayer) 352 if !ok { 353 break 354 } 355 layer = dl.flatten() 356 } 357 b.StopTimer() 358 } 359 } 360 361 // This test writes ~324M of diff layers to disk, spread over 362 // - 128 individual layers, 363 // - each with 200 accounts 364 // - containing 200 slots 365 // 366 // BenchmarkJournal-6 1 1471373923 ns/ops 367 // BenchmarkJournal-6 1 1208083335 ns/op // bufio writer 368 func BenchmarkJournal(b *testing.B) { 369 fill := func(parent snapshot) *diffLayer { 370 var ( 371 destructs = make(map[common.Hash]struct{}) 372 accounts = make(map[common.Hash][]byte) 373 storage = make(map[common.Hash]map[common.Hash][]byte) 374 ) 375 for i := 0; i < 200; i++ { 376 accountKey := randomHash() 377 accounts[accountKey] = randomAccount() 378 379 accStorage := make(map[common.Hash][]byte) 380 for i := 0; i < 200; i++ { 381 value := make([]byte, 32) 382 rand.Read(value) 383 accStorage[randomHash()] = value 384 } 385 storage[accountKey] = accStorage 386 } 387 return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage) 388 } 389 layer := snapshot(emptyLayer()) 390 for i := 1; i < 128; i++ { 391 layer = fill(layer) 392 } 393 b.ResetTimer() 394 395 for i := 0; i < b.N; i++ { 396 layer.Journal(new(bytes.Buffer)) 397 } 398 }