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  }