github.com/matthieu/go-ethereum@v1.13.2/core/state/snapshot/snapshot_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  	"math/rand"
    23  	"testing"
    24  
    25  	"github.com/VictoriaMetrics/fastcache"
    26  	"github.com/matthieu/go-ethereum/common"
    27  	"github.com/matthieu/go-ethereum/core/rawdb"
    28  	"github.com/matthieu/go-ethereum/rlp"
    29  )
    30  
    31  // randomHash generates a random blob of data and returns it as a hash.
    32  func randomHash() common.Hash {
    33  	var hash common.Hash
    34  	if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil {
    35  		panic(err)
    36  	}
    37  	return hash
    38  }
    39  
    40  // randomAccount generates a random account and returns it RLP encoded.
    41  func randomAccount() []byte {
    42  	root := randomHash()
    43  	a := Account{
    44  		Balance:  big.NewInt(rand.Int63()),
    45  		Nonce:    rand.Uint64(),
    46  		Root:     root[:],
    47  		CodeHash: emptyCode[:],
    48  	}
    49  	data, _ := rlp.EncodeToBytes(a)
    50  	return data
    51  }
    52  
    53  // randomAccountSet generates a set of random accounts with the given strings as
    54  // the account address hashes.
    55  func randomAccountSet(hashes ...string) map[common.Hash][]byte {
    56  	accounts := make(map[common.Hash][]byte)
    57  	for _, hash := range hashes {
    58  		accounts[common.HexToHash(hash)] = randomAccount()
    59  	}
    60  	return accounts
    61  }
    62  
    63  // randomStorageSet generates a set of random slots with the given strings as
    64  // the slot addresses.
    65  func randomStorageSet(accounts []string, hashes [][]string, nilStorage [][]string) map[common.Hash]map[common.Hash][]byte {
    66  	storages := make(map[common.Hash]map[common.Hash][]byte)
    67  	for index, account := range accounts {
    68  		storages[common.HexToHash(account)] = make(map[common.Hash][]byte)
    69  
    70  		if index < len(hashes) {
    71  			hashes := hashes[index]
    72  			for _, hash := range hashes {
    73  				storages[common.HexToHash(account)][common.HexToHash(hash)] = randomHash().Bytes()
    74  			}
    75  		}
    76  		if index < len(nilStorage) {
    77  			nils := nilStorage[index]
    78  			for _, hash := range nils {
    79  				storages[common.HexToHash(account)][common.HexToHash(hash)] = nil
    80  			}
    81  		}
    82  	}
    83  	return storages
    84  }
    85  
    86  // Tests that if a disk layer becomes stale, no active external references will
    87  // be returned with junk data. This version of the test flattens every diff layer
    88  // to check internal corner case around the bottom-most memory accumulator.
    89  func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) {
    90  	// Create an empty base layer and a snapshot tree out of it
    91  	base := &diskLayer{
    92  		diskdb: rawdb.NewMemoryDatabase(),
    93  		root:   common.HexToHash("0x01"),
    94  		cache:  fastcache.New(1024 * 500),
    95  	}
    96  	snaps := &Tree{
    97  		layers: map[common.Hash]snapshot{
    98  			base.root: base,
    99  		},
   100  	}
   101  	// Retrieve a reference to the base and commit a diff on top
   102  	ref := snaps.Snapshot(base.root)
   103  
   104  	accounts := map[common.Hash][]byte{
   105  		common.HexToHash("0xa1"): randomAccount(),
   106  	}
   107  	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
   108  		t.Fatalf("failed to create a diff layer: %v", err)
   109  	}
   110  	if n := len(snaps.layers); n != 2 {
   111  		t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 2)
   112  	}
   113  	// Commit the diff layer onto the disk and ensure it's persisted
   114  	if err := snaps.Cap(common.HexToHash("0x02"), 0); err != nil {
   115  		t.Fatalf("failed to merge diff layer onto disk: %v", err)
   116  	}
   117  	// Since the base layer was modified, ensure that data retrieval on the external reference fail
   118  	if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
   119  		t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
   120  	}
   121  	if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
   122  		t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
   123  	}
   124  	if n := len(snaps.layers); n != 1 {
   125  		t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 1)
   126  		fmt.Println(snaps.layers)
   127  	}
   128  }
   129  
   130  // Tests that if a disk layer becomes stale, no active external references will
   131  // be returned with junk data. This version of the test retains the bottom diff
   132  // layer to check the usual mode of operation where the accumulator is retained.
   133  func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) {
   134  	// Create an empty base layer and a snapshot tree out of it
   135  	base := &diskLayer{
   136  		diskdb: rawdb.NewMemoryDatabase(),
   137  		root:   common.HexToHash("0x01"),
   138  		cache:  fastcache.New(1024 * 500),
   139  	}
   140  	snaps := &Tree{
   141  		layers: map[common.Hash]snapshot{
   142  			base.root: base,
   143  		},
   144  	}
   145  	// Retrieve a reference to the base and commit two diffs on top
   146  	ref := snaps.Snapshot(base.root)
   147  
   148  	accounts := map[common.Hash][]byte{
   149  		common.HexToHash("0xa1"): randomAccount(),
   150  	}
   151  	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
   152  		t.Fatalf("failed to create a diff layer: %v", err)
   153  	}
   154  	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil {
   155  		t.Fatalf("failed to create a diff layer: %v", err)
   156  	}
   157  	if n := len(snaps.layers); n != 3 {
   158  		t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 3)
   159  	}
   160  	// Commit the diff layer onto the disk and ensure it's persisted
   161  	defer func(memcap uint64) { aggregatorMemoryLimit = memcap }(aggregatorMemoryLimit)
   162  	aggregatorMemoryLimit = 0
   163  
   164  	if err := snaps.Cap(common.HexToHash("0x03"), 2); err != nil {
   165  		t.Fatalf("failed to merge diff layer onto disk: %v", err)
   166  	}
   167  	// Since the base layer was modified, ensure that data retrievald on the external reference fail
   168  	if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
   169  		t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
   170  	}
   171  	if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
   172  		t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
   173  	}
   174  	if n := len(snaps.layers); n != 2 {
   175  		t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 2)
   176  		fmt.Println(snaps.layers)
   177  	}
   178  }
   179  
   180  // Tests that if a diff layer becomes stale, no active external references will
   181  // be returned with junk data. This version of the test flattens every diff layer
   182  // to check internal corner case around the bottom-most memory accumulator.
   183  func TestDiffLayerExternalInvalidationFullFlatten(t *testing.T) {
   184  	// Create an empty base layer and a snapshot tree out of it
   185  	base := &diskLayer{
   186  		diskdb: rawdb.NewMemoryDatabase(),
   187  		root:   common.HexToHash("0x01"),
   188  		cache:  fastcache.New(1024 * 500),
   189  	}
   190  	snaps := &Tree{
   191  		layers: map[common.Hash]snapshot{
   192  			base.root: base,
   193  		},
   194  	}
   195  	// Commit two diffs on top and retrieve a reference to the bottommost
   196  	accounts := map[common.Hash][]byte{
   197  		common.HexToHash("0xa1"): randomAccount(),
   198  	}
   199  	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
   200  		t.Fatalf("failed to create a diff layer: %v", err)
   201  	}
   202  	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil {
   203  		t.Fatalf("failed to create a diff layer: %v", err)
   204  	}
   205  	if n := len(snaps.layers); n != 3 {
   206  		t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 3)
   207  	}
   208  	ref := snaps.Snapshot(common.HexToHash("0x02"))
   209  
   210  	// Flatten the diff layer into the bottom accumulator
   211  	if err := snaps.Cap(common.HexToHash("0x03"), 1); err != nil {
   212  		t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
   213  	}
   214  	// Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail
   215  	if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
   216  		t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
   217  	}
   218  	if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
   219  		t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
   220  	}
   221  	if n := len(snaps.layers); n != 2 {
   222  		t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 2)
   223  		fmt.Println(snaps.layers)
   224  	}
   225  }
   226  
   227  // Tests that if a diff layer becomes stale, no active external references will
   228  // be returned with junk data. This version of the test retains the bottom diff
   229  // layer to check the usual mode of operation where the accumulator is retained.
   230  func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) {
   231  	// Create an empty base layer and a snapshot tree out of it
   232  	base := &diskLayer{
   233  		diskdb: rawdb.NewMemoryDatabase(),
   234  		root:   common.HexToHash("0x01"),
   235  		cache:  fastcache.New(1024 * 500),
   236  	}
   237  	snaps := &Tree{
   238  		layers: map[common.Hash]snapshot{
   239  			base.root: base,
   240  		},
   241  	}
   242  	// Commit three diffs on top and retrieve a reference to the bottommost
   243  	accounts := map[common.Hash][]byte{
   244  		common.HexToHash("0xa1"): randomAccount(),
   245  	}
   246  	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
   247  		t.Fatalf("failed to create a diff layer: %v", err)
   248  	}
   249  	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil {
   250  		t.Fatalf("failed to create a diff layer: %v", err)
   251  	}
   252  	if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, accounts, nil); err != nil {
   253  		t.Fatalf("failed to create a diff layer: %v", err)
   254  	}
   255  	if n := len(snaps.layers); n != 4 {
   256  		t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 4)
   257  	}
   258  	ref := snaps.Snapshot(common.HexToHash("0x02"))
   259  
   260  	// Doing a Cap operation with many allowed layers should be a no-op
   261  	exp := len(snaps.layers)
   262  	if err := snaps.Cap(common.HexToHash("0x04"), 2000); err != nil {
   263  		t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
   264  	}
   265  	if got := len(snaps.layers); got != exp {
   266  		t.Errorf("layers modified, got %d exp %d", got, exp)
   267  	}
   268  	// Flatten the diff layer into the bottom accumulator
   269  	if err := snaps.Cap(common.HexToHash("0x04"), 2); err != nil {
   270  		t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
   271  	}
   272  	// Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail
   273  	if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
   274  		t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
   275  	}
   276  	if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
   277  		t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
   278  	}
   279  	if n := len(snaps.layers); n != 3 {
   280  		t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 3)
   281  		fmt.Println(snaps.layers)
   282  	}
   283  }
   284  
   285  // TestPostCapBasicDataAccess tests some functionality regarding capping/flattening.
   286  func TestPostCapBasicDataAccess(t *testing.T) {
   287  	// setAccount is a helper to construct a random account entry and assign it to
   288  	// an account slot in a snapshot
   289  	setAccount := func(accKey string) map[common.Hash][]byte {
   290  		return map[common.Hash][]byte{
   291  			common.HexToHash(accKey): randomAccount(),
   292  		}
   293  	}
   294  	// Create a starting base layer and a snapshot tree out of it
   295  	base := &diskLayer{
   296  		diskdb: rawdb.NewMemoryDatabase(),
   297  		root:   common.HexToHash("0x01"),
   298  		cache:  fastcache.New(1024 * 500),
   299  	}
   300  	snaps := &Tree{
   301  		layers: map[common.Hash]snapshot{
   302  			base.root: base,
   303  		},
   304  	}
   305  	// The lowest difflayer
   306  	snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil)
   307  	snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil)
   308  	snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xa1"), nil, setAccount("0xb2"), nil)
   309  
   310  	snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil)
   311  	snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xb2"), nil, setAccount("0xb3"), nil)
   312  
   313  	// checkExist verifies if an account exiss in a snapshot
   314  	checkExist := func(layer *diffLayer, key string) error {
   315  		if data, _ := layer.Account(common.HexToHash(key)); data == nil {
   316  			return fmt.Errorf("expected %x to exist, got nil", common.HexToHash(key))
   317  		}
   318  		return nil
   319  	}
   320  	// shouldErr checks that an account access errors as expected
   321  	shouldErr := func(layer *diffLayer, key string) error {
   322  		if data, err := layer.Account(common.HexToHash(key)); err == nil {
   323  			return fmt.Errorf("expected error, got data %x", data)
   324  		}
   325  		return nil
   326  	}
   327  	// check basics
   328  	snap := snaps.Snapshot(common.HexToHash("0xb3")).(*diffLayer)
   329  
   330  	if err := checkExist(snap, "0xa1"); err != nil {
   331  		t.Error(err)
   332  	}
   333  	if err := checkExist(snap, "0xb2"); err != nil {
   334  		t.Error(err)
   335  	}
   336  	if err := checkExist(snap, "0xb3"); err != nil {
   337  		t.Error(err)
   338  	}
   339  	// Cap to a bad root should fail
   340  	if err := snaps.Cap(common.HexToHash("0x1337"), 0); err == nil {
   341  		t.Errorf("expected error, got none")
   342  	}
   343  	// Now, merge the a-chain
   344  	snaps.Cap(common.HexToHash("0xa3"), 0)
   345  
   346  	// At this point, a2 got merged into a1. Thus, a1 is now modified, and as a1 is
   347  	// the parent of b2, b2 should no longer be able to iterate into parent.
   348  
   349  	// These should still be accessible
   350  	if err := checkExist(snap, "0xb2"); err != nil {
   351  		t.Error(err)
   352  	}
   353  	if err := checkExist(snap, "0xb3"); err != nil {
   354  		t.Error(err)
   355  	}
   356  	// But these would need iteration into the modified parent
   357  	if err := shouldErr(snap, "0xa1"); err != nil {
   358  		t.Error(err)
   359  	}
   360  	if err := shouldErr(snap, "0xa2"); err != nil {
   361  		t.Error(err)
   362  	}
   363  	if err := shouldErr(snap, "0xa3"); err != nil {
   364  		t.Error(err)
   365  	}
   366  	// Now, merge it again, just for fun. It should now error, since a3
   367  	// is a disk layer
   368  	if err := snaps.Cap(common.HexToHash("0xa3"), 0); err == nil {
   369  		t.Error("expected error capping the disk layer, got none")
   370  	}
   371  }