github.com/FusionFoundation/efsn/v4@v4.2.0/trie/trie_test.go (about) 1 // Copyright 2014 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 trie 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "errors" 23 "fmt" 24 "io/ioutil" 25 "math/big" 26 "math/rand" 27 "os" 28 "reflect" 29 "testing" 30 "testing/quick" 31 32 "github.com/FusionFoundation/efsn/v4/common" 33 "github.com/FusionFoundation/efsn/v4/crypto" 34 "github.com/FusionFoundation/efsn/v4/ethdb" 35 "github.com/FusionFoundation/efsn/v4/rlp" 36 "github.com/davecgh/go-spew/spew" 37 ) 38 39 func init() { 40 spew.Config.Indent = " " 41 spew.Config.DisableMethods = false 42 } 43 44 // Used for testing 45 func newEmpty() *Trie { 46 trie, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) 47 return trie 48 } 49 50 func TestEmptyTrie(t *testing.T) { 51 var trie Trie 52 res := trie.Hash() 53 exp := emptyRoot 54 if res != common.Hash(exp) { 55 t.Errorf("expected %x got %x", exp, res) 56 } 57 } 58 59 func TestNull(t *testing.T) { 60 var trie Trie 61 key := make([]byte, 32) 62 value := []byte("test") 63 trie.Update(key, value) 64 if !bytes.Equal(trie.Get(key), value) { 65 t.Fatal("wrong value") 66 } 67 } 68 69 func TestMissingRoot(t *testing.T) { 70 trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), NewDatabase(ethdb.NewMemDatabase())) 71 if trie != nil { 72 t.Error("New returned non-nil trie for invalid root") 73 } 74 if _, ok := err.(*MissingNodeError); !ok { 75 t.Errorf("New returned wrong error: %v", err) 76 } 77 } 78 79 func TestMissingNodeDisk(t *testing.T) { testMissingNode(t, false) } 80 func TestMissingNodeMemonly(t *testing.T) { testMissingNode(t, true) } 81 82 func testMissingNode(t *testing.T, memonly bool) { 83 diskdb := ethdb.NewMemDatabase() 84 triedb := NewDatabase(diskdb) 85 86 trie, _ := New(common.Hash{}, triedb) 87 updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer") 88 updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf") 89 root, _ := trie.Commit(nil) 90 if !memonly { 91 triedb.Commit(root, true) 92 } 93 94 trie, _ = New(root, triedb) 95 _, err := trie.TryGet([]byte("120000")) 96 if err != nil { 97 t.Errorf("Unexpected error: %v", err) 98 } 99 trie, _ = New(root, triedb) 100 _, err = trie.TryGet([]byte("120099")) 101 if err != nil { 102 t.Errorf("Unexpected error: %v", err) 103 } 104 trie, _ = New(root, triedb) 105 _, err = trie.TryGet([]byte("123456")) 106 if err != nil { 107 t.Errorf("Unexpected error: %v", err) 108 } 109 trie, _ = New(root, triedb) 110 err = trie.TryUpdate([]byte("120099"), []byte("zxcvzxcvzxcvzxcvzxcvzxcvzxcvzxcv")) 111 if err != nil { 112 t.Errorf("Unexpected error: %v", err) 113 } 114 trie, _ = New(root, triedb) 115 err = trie.TryDelete([]byte("123456")) 116 if err != nil { 117 t.Errorf("Unexpected error: %v", err) 118 } 119 120 hash := common.HexToHash("0xe1d943cc8f061a0c0b98162830b970395ac9315654824bf21b73b891365262f9") 121 if memonly { 122 delete(triedb.nodes, hash) 123 } else { 124 diskdb.Delete(hash[:]) 125 } 126 127 trie, _ = New(root, triedb) 128 _, err = trie.TryGet([]byte("120000")) 129 if _, ok := err.(*MissingNodeError); !ok { 130 t.Errorf("Wrong error: %v", err) 131 } 132 trie, _ = New(root, triedb) 133 _, err = trie.TryGet([]byte("120099")) 134 if _, ok := err.(*MissingNodeError); !ok { 135 t.Errorf("Wrong error: %v", err) 136 } 137 trie, _ = New(root, triedb) 138 _, err = trie.TryGet([]byte("123456")) 139 if err != nil { 140 t.Errorf("Unexpected error: %v", err) 141 } 142 trie, _ = New(root, triedb) 143 err = trie.TryUpdate([]byte("120099"), []byte("zxcv")) 144 if _, ok := err.(*MissingNodeError); !ok { 145 t.Errorf("Wrong error: %v", err) 146 } 147 trie, _ = New(root, triedb) 148 err = trie.TryDelete([]byte("123456")) 149 if _, ok := err.(*MissingNodeError); !ok { 150 t.Errorf("Wrong error: %v", err) 151 } 152 } 153 154 func TestInsert(t *testing.T) { 155 trie := newEmpty() 156 157 updateString(trie, "doe", "reindeer") 158 updateString(trie, "dog", "puppy") 159 updateString(trie, "dogglesworth", "cat") 160 161 exp := common.HexToHash("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3") 162 root := trie.Hash() 163 if root != exp { 164 t.Errorf("exp %x got %x", exp, root) 165 } 166 167 trie = newEmpty() 168 updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") 169 170 exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab") 171 root, err := trie.Commit(nil) 172 if err != nil { 173 t.Fatalf("commit error: %v", err) 174 } 175 if root != exp { 176 t.Errorf("exp %x got %x", exp, root) 177 } 178 } 179 180 func TestGet(t *testing.T) { 181 trie := newEmpty() 182 updateString(trie, "doe", "reindeer") 183 updateString(trie, "dog", "puppy") 184 updateString(trie, "dogglesworth", "cat") 185 186 for i := 0; i < 2; i++ { 187 res := getString(trie, "dog") 188 if !bytes.Equal(res, []byte("puppy")) { 189 t.Errorf("expected puppy got %x", res) 190 } 191 192 unknown := getString(trie, "unknown") 193 if unknown != nil { 194 t.Errorf("expected nil got %x", unknown) 195 } 196 197 if i == 1 { 198 return 199 } 200 trie.Commit(nil) 201 } 202 } 203 204 func TestDelete(t *testing.T) { 205 trie := newEmpty() 206 vals := []struct{ k, v string }{ 207 {"do", "verb"}, 208 {"ether", "wookiedoo"}, 209 {"horse", "stallion"}, 210 {"shaman", "horse"}, 211 {"doge", "coin"}, 212 {"ether", ""}, 213 {"dog", "puppy"}, 214 {"shaman", ""}, 215 } 216 for _, val := range vals { 217 if val.v != "" { 218 updateString(trie, val.k, val.v) 219 } else { 220 deleteString(trie, val.k) 221 } 222 } 223 224 hash := trie.Hash() 225 exp := common.HexToHash("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84") 226 if hash != exp { 227 t.Errorf("expected %x got %x", exp, hash) 228 } 229 } 230 231 func TestEmptyValues(t *testing.T) { 232 trie := newEmpty() 233 234 vals := []struct{ k, v string }{ 235 {"do", "verb"}, 236 {"ether", "wookiedoo"}, 237 {"horse", "stallion"}, 238 {"shaman", "horse"}, 239 {"doge", "coin"}, 240 {"ether", ""}, 241 {"dog", "puppy"}, 242 {"shaman", ""}, 243 } 244 for _, val := range vals { 245 updateString(trie, val.k, val.v) 246 } 247 248 hash := trie.Hash() 249 exp := common.HexToHash("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84") 250 if hash != exp { 251 t.Errorf("expected %x got %x", exp, hash) 252 } 253 } 254 255 func TestReplication(t *testing.T) { 256 trie := newEmpty() 257 vals := []struct{ k, v string }{ 258 {"do", "verb"}, 259 {"ether", "wookiedoo"}, 260 {"horse", "stallion"}, 261 {"shaman", "horse"}, 262 {"doge", "coin"}, 263 {"dog", "puppy"}, 264 {"somethingveryoddindeedthis is", "myothernodedata"}, 265 } 266 for _, val := range vals { 267 updateString(trie, val.k, val.v) 268 } 269 exp, err := trie.Commit(nil) 270 if err != nil { 271 t.Fatalf("commit error: %v", err) 272 } 273 274 // create a new trie on top of the database and check that lookups work. 275 trie2, err := New(exp, trie.db) 276 if err != nil { 277 t.Fatalf("can't recreate trie at %x: %v", exp, err) 278 } 279 for _, kv := range vals { 280 if string(getString(trie2, kv.k)) != kv.v { 281 t.Errorf("trie2 doesn't have %q => %q", kv.k, kv.v) 282 } 283 } 284 hash, err := trie2.Commit(nil) 285 if err != nil { 286 t.Fatalf("commit error: %v", err) 287 } 288 if hash != exp { 289 t.Errorf("root failure. expected %x got %x", exp, hash) 290 } 291 292 // perform some insertions on the new trie. 293 vals2 := []struct{ k, v string }{ 294 {"do", "verb"}, 295 {"ether", "wookiedoo"}, 296 {"horse", "stallion"}, 297 // {"shaman", "horse"}, 298 // {"doge", "coin"}, 299 // {"ether", ""}, 300 // {"dog", "puppy"}, 301 // {"somethingveryoddindeedthis is", "myothernodedata"}, 302 // {"shaman", ""}, 303 } 304 for _, val := range vals2 { 305 updateString(trie2, val.k, val.v) 306 } 307 if hash := trie2.Hash(); hash != exp { 308 t.Errorf("root failure. expected %x got %x", exp, hash) 309 } 310 } 311 312 func TestLargeValue(t *testing.T) { 313 trie := newEmpty() 314 trie.Update([]byte("key1"), []byte{99, 99, 99, 99}) 315 trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32)) 316 trie.Hash() 317 } 318 319 type countingDB struct { 320 ethdb.Database 321 gets map[string]int 322 } 323 324 func (db *countingDB) Get(key []byte) ([]byte, error) { 325 db.gets[string(key)]++ 326 return db.Database.Get(key) 327 } 328 329 // TestCacheUnload checks that decoded nodes are unloaded after a 330 // certain number of commit operations. 331 func TestCacheUnload(t *testing.T) { 332 // Create test trie with two branches. 333 trie := newEmpty() 334 key1 := "---------------------------------" 335 key2 := "---some other branch" 336 updateString(trie, key1, "this is the branch of key1.") 337 updateString(trie, key2, "this is the branch of key2.") 338 339 root, _ := trie.Commit(nil) 340 trie.db.Commit(root, true) 341 342 // Commit the trie repeatedly and access key1. 343 // The branch containing it is loaded from DB exactly two times: 344 // in the 0th and 6th iteration. 345 db := &countingDB{Database: trie.db.diskdb, gets: make(map[string]int)} 346 trie, _ = New(root, NewDatabase(db)) 347 trie.SetCacheLimit(5) 348 for i := 0; i < 12; i++ { 349 getString(trie, key1) 350 trie.Commit(nil) 351 } 352 // Check that it got loaded two times. 353 for dbkey, count := range db.gets { 354 if count != 2 { 355 t.Errorf("db key %x loaded %d times, want %d times", []byte(dbkey), count, 2) 356 } 357 } 358 } 359 360 // randTest performs random trie operations. 361 // Instances of this test are created by Generate. 362 type randTest []randTestStep 363 364 type randTestStep struct { 365 op int 366 key []byte // for opUpdate, opDelete, opGet 367 value []byte // for opUpdate 368 err error // for debugging 369 } 370 371 const ( 372 opUpdate = iota 373 opDelete 374 opGet 375 opCommit 376 opHash 377 opReset 378 opItercheckhash 379 opCheckCacheInvariant 380 opMax // boundary value, not an actual op 381 ) 382 383 func (randTest) Generate(r *rand.Rand, size int) reflect.Value { 384 var allKeys [][]byte 385 genKey := func() []byte { 386 if len(allKeys) < 2 || r.Intn(100) < 10 { 387 // new key 388 key := make([]byte, r.Intn(50)) 389 r.Read(key) 390 allKeys = append(allKeys, key) 391 return key 392 } 393 // use existing key 394 return allKeys[r.Intn(len(allKeys))] 395 } 396 397 var steps randTest 398 for i := 0; i < size; i++ { 399 step := randTestStep{op: r.Intn(opMax)} 400 switch step.op { 401 case opUpdate: 402 step.key = genKey() 403 step.value = make([]byte, 8) 404 binary.BigEndian.PutUint64(step.value, uint64(i)) 405 case opGet, opDelete: 406 step.key = genKey() 407 } 408 steps = append(steps, step) 409 } 410 return reflect.ValueOf(steps) 411 } 412 413 func runRandTest(rt randTest) bool { 414 triedb := NewDatabase(ethdb.NewMemDatabase()) 415 416 tr, _ := New(common.Hash{}, triedb) 417 values := make(map[string]string) // tracks content of the trie 418 419 for i, step := range rt { 420 switch step.op { 421 case opUpdate: 422 tr.Update(step.key, step.value) 423 values[string(step.key)] = string(step.value) 424 case opDelete: 425 tr.Delete(step.key) 426 delete(values, string(step.key)) 427 case opGet: 428 v := tr.Get(step.key) 429 want := values[string(step.key)] 430 if string(v) != want { 431 rt[i].err = fmt.Errorf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want) 432 } 433 case opCommit: 434 _, rt[i].err = tr.Commit(nil) 435 case opHash: 436 tr.Hash() 437 case opReset: 438 hash, err := tr.Commit(nil) 439 if err != nil { 440 rt[i].err = err 441 return false 442 } 443 newtr, err := New(hash, triedb) 444 if err != nil { 445 rt[i].err = err 446 return false 447 } 448 tr = newtr 449 case opItercheckhash: 450 checktr, _ := New(common.Hash{}, triedb) 451 it := NewIterator(tr.NodeIterator(nil)) 452 for it.Next() { 453 checktr.Update(it.Key, it.Value) 454 } 455 if tr.Hash() != checktr.Hash() { 456 rt[i].err = fmt.Errorf("hash mismatch in opItercheckhash") 457 } 458 case opCheckCacheInvariant: 459 rt[i].err = checkCacheInvariant(tr.root, nil, tr.cachegen, false, 0) 460 } 461 // Abort the test on error. 462 if rt[i].err != nil { 463 return false 464 } 465 } 466 return true 467 } 468 469 func checkCacheInvariant(n, parent node, parentCachegen uint16, parentDirty bool, depth int) error { 470 var children []node 471 var flag nodeFlag 472 switch n := n.(type) { 473 case *shortNode: 474 flag = n.flags 475 children = []node{n.Val} 476 case *fullNode: 477 flag = n.flags 478 children = n.Children[:] 479 default: 480 return nil 481 } 482 483 errorf := func(format string, args ...interface{}) error { 484 msg := fmt.Sprintf(format, args...) 485 msg += fmt.Sprintf("\nat depth %d node %s", depth, spew.Sdump(n)) 486 msg += fmt.Sprintf("parent: %s", spew.Sdump(parent)) 487 return errors.New(msg) 488 } 489 if flag.gen > parentCachegen { 490 return errorf("cache invariant violation: %d > %d\n", flag.gen, parentCachegen) 491 } 492 if depth > 0 && !parentDirty && flag.dirty { 493 return errorf("cache invariant violation: %d > %d\n", flag.gen, parentCachegen) 494 } 495 for _, child := range children { 496 if err := checkCacheInvariant(child, n, flag.gen, flag.dirty, depth+1); err != nil { 497 return err 498 } 499 } 500 return nil 501 } 502 503 func TestRandom(t *testing.T) { 504 if err := quick.Check(runRandTest, nil); err != nil { 505 if cerr, ok := err.(*quick.CheckError); ok { 506 t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In)) 507 } 508 t.Fatal(err) 509 } 510 } 511 512 func BenchmarkGet(b *testing.B) { benchGet(b, false) } 513 func BenchmarkGetDB(b *testing.B) { benchGet(b, true) } 514 func BenchmarkUpdateBE(b *testing.B) { benchUpdate(b, binary.BigEndian) } 515 func BenchmarkUpdateLE(b *testing.B) { benchUpdate(b, binary.LittleEndian) } 516 517 const benchElemCount = 20000 518 519 func benchGet(b *testing.B, commit bool) { 520 trie := new(Trie) 521 if commit { 522 _, tmpdb := tempDB() 523 trie, _ = New(common.Hash{}, tmpdb) 524 } 525 k := make([]byte, 32) 526 for i := 0; i < benchElemCount; i++ { 527 binary.LittleEndian.PutUint64(k, uint64(i)) 528 trie.Update(k, k) 529 } 530 binary.LittleEndian.PutUint64(k, benchElemCount/2) 531 if commit { 532 trie.Commit(nil) 533 } 534 535 b.ResetTimer() 536 for i := 0; i < b.N; i++ { 537 trie.Get(k) 538 } 539 b.StopTimer() 540 541 if commit { 542 ldb := trie.db.diskdb.(*ethdb.LDBDatabase) 543 ldb.Close() 544 os.RemoveAll(ldb.Path()) 545 } 546 } 547 548 func benchUpdate(b *testing.B, e binary.ByteOrder) *Trie { 549 trie := newEmpty() 550 k := make([]byte, 32) 551 for i := 0; i < b.N; i++ { 552 e.PutUint64(k, uint64(i)) 553 trie.Update(k, k) 554 } 555 return trie 556 } 557 558 // Benchmarks the trie hashing. Since the trie caches the result of any operation, 559 // we cannot use b.N as the number of hashing rouns, since all rounds apart from 560 // the first one will be NOOP. As such, we'll use b.N as the number of account to 561 // insert into the trie before measuring the hashing. 562 func BenchmarkHash(b *testing.B) { 563 // Make the random benchmark deterministic 564 random := rand.New(rand.NewSource(0)) 565 566 // Create a realistic account trie to hash 567 addresses := make([][20]byte, b.N) 568 for i := 0; i < len(addresses); i++ { 569 for j := 0; j < len(addresses[i]); j++ { 570 addresses[i][j] = byte(random.Intn(256)) 571 } 572 } 573 accounts := make([][]byte, len(addresses)) 574 for i := 0; i < len(accounts); i++ { 575 var ( 576 nonce = uint64(random.Int63()) 577 balance = new(big.Int).Rand(random, new(big.Int).Exp(common.Big2, common.Big256, nil)) 578 root = emptyRoot 579 code = crypto.Keccak256(nil) 580 ) 581 accounts[i], _ = rlp.EncodeToBytes([]interface{}{nonce, balance, root, code}) 582 } 583 // Insert the accounts into the trie and hash it 584 trie := newEmpty() 585 for i := 0; i < len(addresses); i++ { 586 trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i]) 587 } 588 b.ResetTimer() 589 b.ReportAllocs() 590 trie.Hash() 591 } 592 593 func tempDB() (string, *Database) { 594 dir, err := ioutil.TempDir("", "trie-bench") 595 if err != nil { 596 panic(fmt.Sprintf("can't create temporary directory: %v", err)) 597 } 598 diskdb, err := ethdb.NewLDBDatabase(dir, 256, 0) 599 if err != nil { 600 panic(fmt.Sprintf("can't create temporary database: %v", err)) 601 } 602 return dir, NewDatabase(diskdb) 603 } 604 605 func getString(trie *Trie, k string) []byte { 606 return trie.Get([]byte(k)) 607 } 608 609 func updateString(trie *Trie, k, v string) { 610 trie.Update([]byte(k), []byte(v)) 611 } 612 613 func deleteString(trie *Trie, k string) { 614 trie.Delete([]byte(k)) 615 }