github.com/core-coin/go-core/v2@v2.1.9/core/state/snapshot/snapshot_test.go (about)

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