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