github.com/MetalBlockchain/subnet-evm@v0.4.9/core/state/snapshot/snapshot_test.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2017 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package snapshot
    28  
    29  import (
    30  	"fmt"
    31  	"math/big"
    32  	"math/rand"
    33  	"testing"
    34  	"time"
    35  
    36  	"github.com/MetalBlockchain/subnet-evm/core/rawdb"
    37  	"github.com/ethereum/go-ethereum/common"
    38  	"github.com/ethereum/go-ethereum/rlp"
    39  )
    40  
    41  // randomHash generates a random blob of data and returns it as a hash.
    42  func randomHash() common.Hash {
    43  	var hash common.Hash
    44  	if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil {
    45  		panic(err)
    46  	}
    47  	return hash
    48  }
    49  
    50  // randomAccount generates a random account and returns it RLP encoded.
    51  func randomAccount() []byte {
    52  	root := randomHash()
    53  	a := Account{
    54  		Balance:  big.NewInt(rand.Int63()),
    55  		Nonce:    rand.Uint64(),
    56  		Root:     root[:],
    57  		CodeHash: emptyCode[:],
    58  	}
    59  	data, _ := rlp.EncodeToBytes(a)
    60  	return data
    61  }
    62  
    63  // randomAccountSet generates a set of random accounts with the given strings as
    64  // the account address hashes.
    65  func randomAccountSet(hashes ...string) map[common.Hash][]byte {
    66  	accounts := make(map[common.Hash][]byte)
    67  	for _, hash := range hashes {
    68  		accounts[common.HexToHash(hash)] = randomAccount()
    69  	}
    70  	return accounts
    71  }
    72  
    73  // randomStorageSet generates a set of random slots with the given strings as
    74  // the slot addresses.
    75  func randomStorageSet(accounts []string, hashes [][]string, nilStorage [][]string) map[common.Hash]map[common.Hash][]byte {
    76  	storages := make(map[common.Hash]map[common.Hash][]byte)
    77  	for index, account := range accounts {
    78  		storages[common.HexToHash(account)] = make(map[common.Hash][]byte)
    79  
    80  		if index < len(hashes) {
    81  			hashes := hashes[index]
    82  			for _, hash := range hashes {
    83  				storages[common.HexToHash(account)][common.HexToHash(hash)] = randomHash().Bytes()
    84  			}
    85  		}
    86  		if index < len(nilStorage) {
    87  			nils := nilStorage[index]
    88  			for _, hash := range nils {
    89  				storages[common.HexToHash(account)][common.HexToHash(hash)] = nil
    90  			}
    91  		}
    92  	}
    93  	return storages
    94  }
    95  
    96  // Tests that if a disk layer becomes stale, no active external references will
    97  // be returned with junk data. This version of the test flattens every diff layer
    98  // to check internal corner case around the bottom-most memory accumulator.
    99  func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) {
   100  	// Create an empty base layer and a snapshot tree out of it
   101  	snaps := NewTestTree(rawdb.NewMemoryDatabase(), common.HexToHash("0x01"), common.HexToHash("0xff01"))
   102  	// Retrieve a reference to the base and commit a diff on top
   103  	ref := snaps.Snapshot(common.HexToHash("0xff01"))
   104  
   105  	accounts := map[common.Hash][]byte{
   106  		common.HexToHash("0xa1"): randomAccount(),
   107  	}
   108  	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0xff02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
   109  		t.Fatalf("failed to create a diff layer: %v", err)
   110  	}
   111  	if n := snaps.NumStateLayers(); n != 2 {
   112  		t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 2)
   113  	}
   114  	if n := snaps.NumBlockLayers(); n != 2 {
   115  		t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 2)
   116  	}
   117  	// Commit the diff layer onto the disk and ensure it's persisted
   118  	snaps.verified = true // Bypass validation of junk data
   119  	if err := snaps.Flatten(common.HexToHash("0x02")); err != nil {
   120  		t.Fatalf("failed to merge diff layer onto disk: %v", err)
   121  	}
   122  	// Since the base layer was modified, ensure that data retrieval on the external reference fail
   123  	if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
   124  		t.Errorf("stale reference returned account: %v (err: %v)", acc, err)
   125  	}
   126  	if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
   127  		t.Errorf("stale reference returned storage slot: %v (err: %v)", slot, err)
   128  	}
   129  	if n := snaps.NumStateLayers(); n != 1 {
   130  		t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 1)
   131  	}
   132  	if n := snaps.NumBlockLayers(); n != 1 {
   133  		t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 1)
   134  	}
   135  }
   136  
   137  // Tests that if a disk layer becomes stale, no active external references will
   138  // be returned with junk data. This version of the test retains the bottom diff
   139  // layer to check the usual mode of operation where the accumulator is retained.
   140  func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) {
   141  	// Create an empty base layer and a snapshot tree out of it
   142  	snaps := NewTestTree(rawdb.NewMemoryDatabase(), common.HexToHash("0x01"), common.HexToHash("0xff01"))
   143  	// Retrieve a reference to the base and commit two diffs on top
   144  	ref := snaps.Snapshot(common.HexToHash("0xff01"))
   145  
   146  	accounts := map[common.Hash][]byte{
   147  		common.HexToHash("0xa1"): randomAccount(),
   148  	}
   149  	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0xff02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
   150  		t.Fatalf("failed to create a diff layer: %v", err)
   151  	}
   152  	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0xff03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil {
   153  		t.Fatalf("failed to create a diff layer: %v", err)
   154  	}
   155  	if n := snaps.NumBlockLayers(); n != 3 {
   156  		t.Errorf("pre-cap block layer count mismatch: have %d, want %d", n, 3)
   157  	}
   158  	if n := snaps.NumStateLayers(); n != 3 {
   159  		t.Errorf("pre-cap state 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  	snaps.verified = true // Bypass validation of junk data
   166  	if err := snaps.Flatten(common.HexToHash("0x02")); err != nil {
   167  		t.Fatalf("Failed to flatten diff layer onto disk: %v", err)
   168  	}
   169  	// Since the base layer was modified, ensure that data retrieval on the external reference fails
   170  	if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
   171  		t.Errorf("stale reference returned account: %v (err: %v)", acc, err)
   172  	}
   173  	if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
   174  		t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
   175  	}
   176  	if n := snaps.NumBlockLayers(); n != 2 {
   177  		t.Errorf("pre-cap block layer count mismatch: have %d, want %d", n, 2)
   178  	}
   179  	if n := snaps.NumStateLayers(); n != 2 {
   180  		t.Errorf("pre-cap state layer count mismatch: have %d, want %d", n, 2)
   181  	}
   182  }
   183  
   184  // Tests that if a diff layer becomes stale, no active external references will
   185  // be returned with junk data. This version of the test retains the bottom diff
   186  // layer to check the usual mode of operation where the accumulator is retained.
   187  func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) {
   188  	// Create an empty base layer and a snapshot tree out of it
   189  	snaps := NewTestTree(rawdb.NewMemoryDatabase(), common.HexToHash("0x01"), common.HexToHash("0xff01"))
   190  	// Commit three diffs on top and retrieve a reference to the bottommost
   191  	accounts := map[common.Hash][]byte{
   192  		common.HexToHash("0xa1"): randomAccount(),
   193  	}
   194  	if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0xff02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
   195  		t.Fatalf("failed to create a diff layer: %v", err)
   196  	}
   197  	if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0xff03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil {
   198  		t.Fatalf("failed to create a diff layer: %v", err)
   199  	}
   200  	if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0xff04"), common.HexToHash("0x03"), nil, accounts, nil); err != nil {
   201  		t.Fatalf("failed to create a diff layer: %v", err)
   202  	}
   203  	if n := snaps.NumStateLayers(); n != 4 {
   204  		t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 4)
   205  	}
   206  	if n := snaps.NumBlockLayers(); n != 4 {
   207  		t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 4)
   208  	}
   209  	ref := snaps.Snapshot(common.HexToHash("0xff02"))
   210  
   211  	snaps.verified = true // Bypass validation of junk data
   212  	if err := snaps.Flatten(common.HexToHash("0x02")); err != nil {
   213  		t.Fatal(err)
   214  	}
   215  	// Since the accumulator diff layer was modified, ensure that data retrieval on the external reference fails
   216  	if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
   217  		t.Errorf("stale reference returned account: %v (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 := snaps.NumStateLayers(); n != 3 {
   223  		t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 3)
   224  	}
   225  	if n := snaps.NumBlockLayers(); n != 3 {
   226  		t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 3)
   227  	}
   228  }
   229  
   230  // TestPostCapBasicDataAccess tests some functionality regarding capping/flattening.
   231  func TestPostFlattenBasicDataAccess(t *testing.T) {
   232  	// setAccount is a helper to construct a random account entry and assign it to
   233  	// an account slot in a snapshot
   234  	setAccount := func(accKey string) map[common.Hash][]byte {
   235  		return map[common.Hash][]byte{
   236  			common.HexToHash(accKey): randomAccount(),
   237  		}
   238  	}
   239  	// Create a starting base layer and a snapshot tree out of it
   240  	snaps := NewTestTree(rawdb.NewMemoryDatabase(), common.HexToHash("0x01"), common.HexToHash("0xff01"))
   241  	// The lowest difflayer
   242  	snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0xffa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil)
   243  	snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xffa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil)
   244  	snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xffb2"), common.HexToHash("0xa1"), nil, setAccount("0xb2"), nil)
   245  
   246  	snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xffa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil)
   247  	snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xffb3"), common.HexToHash("0xb2"), nil, setAccount("0xb3"), nil)
   248  
   249  	// checkExist verifies if an account exists in a snapshot
   250  	checkExist := func(layer Snapshot, key string) error {
   251  		if data, _ := layer.Account(common.HexToHash(key)); data == nil {
   252  			return fmt.Errorf("expected %x to exist, got nil", common.HexToHash(key))
   253  		}
   254  		return nil
   255  	}
   256  	checkNotExist := func(layer Snapshot, key string) error {
   257  		if data, err := layer.Account(common.HexToHash(key)); err != nil {
   258  			return fmt.Errorf("expected %x to not error: %w", key, err)
   259  		} else if data != nil {
   260  			return fmt.Errorf("expected %x to be empty, got %v", key, data)
   261  		}
   262  		return nil
   263  	}
   264  	// shouldErr checks that an account access errors as expected
   265  	shouldErr := func(layer Snapshot, key string) error {
   266  		if data, err := layer.Account(common.HexToHash(key)); err == nil {
   267  			return fmt.Errorf("expected error, got data %v", data)
   268  		}
   269  		return nil
   270  	}
   271  	// Check basics for both snapshots 0xffa3 and 0xffb3
   272  	snap := snaps.Snapshot(common.HexToHash("0xffa3")).(*diffLayer)
   273  
   274  	// Check that the accounts that should exist are present in the snapshot
   275  	if err := checkExist(snap, "0xa1"); err != nil {
   276  		t.Error(err)
   277  	}
   278  	if err := checkExist(snap, "0xa2"); err != nil {
   279  		t.Error(err)
   280  	}
   281  	if err := checkExist(snap, "0xa1"); err != nil {
   282  		t.Error(err)
   283  	}
   284  	// Check that the accounts that should only exist in the other branch
   285  	// of the snapshot tree are not present in the snapshot.
   286  	if err := checkNotExist(snap, "0xb1"); err != nil {
   287  		t.Error(err)
   288  	}
   289  	if err := checkNotExist(snap, "0xb2"); err != nil {
   290  		t.Error(err)
   291  	}
   292  
   293  	snap = snaps.Snapshot(common.HexToHash("0xffb3")).(*diffLayer)
   294  
   295  	// Check that the accounts that should exist are present in the snapshot
   296  	if err := checkExist(snap, "0xa1"); err != nil {
   297  		t.Error(err)
   298  	}
   299  	if err := checkExist(snap, "0xb2"); err != nil {
   300  		t.Error(err)
   301  	}
   302  	if err := checkExist(snap, "0xb3"); err != nil {
   303  		t.Error(err)
   304  	}
   305  	// Check that the accounts that should only exist in the other branch
   306  	// of the snapshot tree are not present in the snapshot.
   307  	if err := checkNotExist(snap, "0xa2"); err != nil {
   308  		t.Error(err)
   309  	}
   310  
   311  	// Flatten a non-existent block should fail
   312  	if err := snaps.Flatten(common.HexToHash("0x1337")); err == nil {
   313  		t.Errorf("expected error, got none")
   314  	}
   315  
   316  	// Now, merge the a-chain layer by layer
   317  	snaps.verified = true // Bypass validation of junk data
   318  	if err := snaps.Flatten(common.HexToHash("0xa1")); err != nil {
   319  		t.Error(err)
   320  	}
   321  	if err := snaps.Flatten(common.HexToHash("0xa2")); err != nil {
   322  		t.Error(err)
   323  	}
   324  
   325  	// At this point, a2 got merged into a1. Thus, a1 is now modified, and as a1 is
   326  	// the parent of b2, b2 should no longer be able to iterate into parent.
   327  
   328  	// These should still be accessible since it doesn't require iteration into the
   329  	// disk layer. However, it's also valid for these diffLayers to be marked as stale.
   330  	if err := checkExist(snap, "0xb2"); err != nil {
   331  		t.Error(err)
   332  	}
   333  	if err := checkExist(snap, "0xb3"); err != nil {
   334  		t.Error(err)
   335  	}
   336  	// But 0xa1 would need iteration into the modified parent
   337  	// and 0xa2 and 0xa3 should never be present since they are
   338  	// on the other branch of the snapshot tree. These should strictly
   339  	// error since they're not in the diff layers and will result in
   340  	// traversing into the stale disk layer.
   341  	if err := shouldErr(snap, "0xa1"); err != nil {
   342  		t.Error(err)
   343  	}
   344  	if err := shouldErr(snap, "0xa2"); err != nil {
   345  		t.Error(err)
   346  	}
   347  	if err := shouldErr(snap, "0xa3"); err != nil {
   348  		t.Error(err)
   349  	}
   350  
   351  	snap = snaps.Snapshot(common.HexToHash("0xffa3")).(*diffLayer)
   352  	// Check that the accounts that should exist are present in the snapshot
   353  	if err := checkExist(snap, "0xa1"); err != nil {
   354  		t.Error(err)
   355  	}
   356  	if err := checkExist(snap, "0xa2"); err != nil {
   357  		t.Error(err)
   358  	}
   359  	if err := checkExist(snap, "0xa3"); err != nil {
   360  		t.Error(err)
   361  	}
   362  	// Check that the accounts that should only exist in the other branch
   363  	// of the snapshot tree are not present in the snapshot.
   364  	if err := checkNotExist(snap, "0xb1"); err != nil {
   365  		t.Error(err)
   366  	}
   367  	if err := checkNotExist(snap, "0xb2"); err != nil {
   368  		t.Error(err)
   369  	}
   370  
   371  	diskLayer := snaps.Snapshot(common.HexToHash("0xffa2"))
   372  	// Check that the accounts that should exist are present in the snapshot
   373  	if err := checkExist(diskLayer, "0xa1"); err != nil {
   374  		t.Error(err)
   375  	}
   376  	if err := checkExist(diskLayer, "0xa2"); err != nil {
   377  		t.Error(err)
   378  	}
   379  	// 0xa3 should not be included until the diffLayer built on top of
   380  	// 0xffa2.
   381  	if err := checkNotExist(diskLayer, "0xa3"); err != nil {
   382  		t.Error(err)
   383  	}
   384  	// Check that the accounts that should only exist in the other branch
   385  	// of the snapshot tree are not present in the snapshot.
   386  	if err := checkNotExist(diskLayer, "0xb1"); err != nil {
   387  		t.Error(err)
   388  	}
   389  	if err := checkNotExist(diskLayer, "0xb2"); err != nil {
   390  		t.Error(err)
   391  	}
   392  }
   393  
   394  // TestTreeFlattenDoesNotDropPendingLayers tests that Tree.Flatten correctly
   395  // retains layers built on top of the given root layer in the presence of multiple
   396  // different blocks inserted with an identical state root.
   397  // In this example, (B, C) and (D, E) share the identical state root, but were
   398  // inserted under different blocks.
   399  //    A
   400  //   /  \
   401  //  B    C
   402  //  |    |
   403  //  D    E
   404  //
   405  // `t.Flatten(C)` should result in:
   406  //
   407  //  B    C
   408  //  |    |
   409  //  D    E
   410  // With the branch D, E, hanging and relying on Discard to be called to
   411  // garbage collect the references.
   412  func TestTreeFlattenDoesNotDropPendingLayers(t *testing.T) {
   413  	var (
   414  		baseRoot      = common.HexToHash("0xffff01")
   415  		baseBlockHash = common.HexToHash("0x01")
   416  	)
   417  	snaps := NewTestTree(rawdb.NewMemoryDatabase(), baseBlockHash, baseRoot)
   418  	accounts := map[common.Hash][]byte{
   419  		common.HexToHash("0xa1"): randomAccount(),
   420  	}
   421  
   422  	// Create N layers on top of base (N+1) total
   423  	parentAHash := baseBlockHash
   424  	parentBHash := baseBlockHash
   425  	totalLayers := 10
   426  	for i := 2; i <= totalLayers; i++ {
   427  		diffBlockAHash := common.Hash{0xee, 0xee, byte(i)}
   428  		diffBlockBHash := common.Hash{0xdd, 0xdd, byte(i)}
   429  		diffBlockRoot := common.Hash{0xff, 0xff, byte(i)}
   430  		if err := snaps.Update(diffBlockAHash, diffBlockRoot, parentAHash, nil, accounts, nil); err != nil {
   431  			t.Fatalf("failed to create a diff layer: %v", err)
   432  		}
   433  		if err := snaps.Update(diffBlockBHash, diffBlockRoot, parentBHash, nil, accounts, nil); err != nil {
   434  			t.Fatalf("failed to create a diff layer: %v", err)
   435  		}
   436  
   437  		parentAHash = diffBlockAHash
   438  		parentBHash = diffBlockBHash
   439  	}
   440  
   441  	if n := snaps.NumStateLayers(); n != 10 {
   442  		t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 10)
   443  	}
   444  	if n := snaps.NumBlockLayers(); n != 19 {
   445  		t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 19)
   446  	}
   447  
   448  	snaps.verified = true // Bypass validation of junk data
   449  	if err := snaps.Flatten(common.Hash{0xee, 0xee, byte(2)}); err != nil {
   450  		t.Fatalf("failed to flatten diff layer from chain A: %v", err)
   451  	}
   452  
   453  	if n := snaps.NumStateLayers(); n != 9 {
   454  		t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 9)
   455  	}
   456  	if n := snaps.NumBlockLayers(); n != 18 {
   457  		t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 18)
   458  	}
   459  
   460  	// Attempt to flatten from chain B should error
   461  	if err := snaps.Flatten(common.Hash{0xdd, 0xdd, byte(2)}); err == nil {
   462  		t.Errorf("expected flattening abandoned chain to error")
   463  	}
   464  
   465  	for i := 2; i <= 10; i++ {
   466  		if err := snaps.Discard(common.Hash{0xdd, 0xdd, byte(i)}); err != nil {
   467  			t.Errorf("attempt to discard snapshot from abandoned chain failed: %v", err)
   468  		}
   469  	}
   470  
   471  	if n := snaps.NumStateLayers(); n != 9 {
   472  		t.Errorf("pre-flatten state layer count mismatch: have %d, want %d", n, 9)
   473  	}
   474  	if n := snaps.NumBlockLayers(); n != 9 {
   475  		t.Errorf("pre-flatten block layer count mismatch: have %d, want %d", n, 18)
   476  	}
   477  }
   478  
   479  func TestStaleOriginLayer(t *testing.T) {
   480  	var (
   481  		baseRoot       = common.HexToHash("0xffff01")
   482  		baseBlockHash  = common.HexToHash("0x01")
   483  		diffRootA      = common.HexToHash("0xff02")
   484  		diffBlockHashA = common.HexToHash("0x02")
   485  		diffRootB      = common.HexToHash("0xff03")
   486  		diffBlockHashB = common.HexToHash("0x03")
   487  		diffRootC      = common.HexToHash("0xff04")
   488  		diffBlockHashC = common.HexToHash("0x04")
   489  	)
   490  	snaps := NewTestTree(rawdb.NewMemoryDatabase(), baseBlockHash, baseRoot)
   491  	addrA := randomHash()
   492  	accountsA := map[common.Hash][]byte{
   493  		addrA: randomAccount(),
   494  	}
   495  	addrB := randomHash()
   496  	accountsB := map[common.Hash][]byte{
   497  		addrB: randomAccount(),
   498  	}
   499  	addrC := randomHash()
   500  	accountsC := map[common.Hash][]byte{
   501  		addrC: randomAccount(),
   502  	}
   503  
   504  	// Create diff layer A containing account 0xa1
   505  	if err := snaps.Update(diffBlockHashA, diffRootA, baseBlockHash, nil, accountsA, nil); err != nil {
   506  		t.Errorf("failed to create diff layer A: %v", err)
   507  	}
   508  	// Flatten account 0xa1 to disk
   509  	snaps.verified = true // Bypass validation of junk data
   510  	if err := snaps.Flatten(diffBlockHashA); err != nil {
   511  		t.Errorf("failed to flatten diff block A: %v", err)
   512  	}
   513  	// Create diff layer B containing account 0xa2
   514  	// The bloom filter should contain only 0xa2.
   515  	if err := snaps.Update(diffBlockHashB, diffRootB, diffBlockHashA, nil, accountsB, nil); err != nil {
   516  		t.Errorf("failed to create diff layer B: %v", err)
   517  	}
   518  	// Create diff layer C containing account 0xa3
   519  	// The bloom filter should contain 0xa2 and 0xa3
   520  	if err := snaps.Update(diffBlockHashC, diffRootC, diffBlockHashB, nil, accountsC, nil); err != nil {
   521  		t.Errorf("failed to create diff layer C: %v", err)
   522  	}
   523  
   524  	if err := snaps.Flatten(diffBlockHashB); err != nil {
   525  		t.Errorf("failed to flattten diff block A: %v", err)
   526  	}
   527  	snap := snaps.Snapshot(diffRootC)
   528  	if _, err := snap.Account(addrA); err != nil {
   529  		t.Errorf("expected account to exist: %v", err)
   530  	}
   531  }
   532  
   533  func TestRebloomOnFlatten(t *testing.T) {
   534  	// Create diff layers, including a level with two children, then flatten
   535  	// the layers and assert that the bloom filters are being updated correctly.
   536  	//
   537  	// Each layer will add an account which will be in the blooms for every
   538  	// following difflayer. No accesses would need to touch disk. Layers C and D
   539  	// should have all addrs in their blooms except for each others.
   540  	//
   541  	// After flattening A into the root, the remaining blooms should no longer
   542  	// have addrA but should retain all the others.
   543  	//
   544  	// After flattening B into A, blooms C and D should contain only their own
   545  	// addrs.
   546  	//
   547  	//          Initial Root (diskLayer)
   548  	//                   |
   549  	//                   A <- First diffLayer. Adds addrA
   550  	//                   |
   551  	//                   B <- Second diffLayer. Adds addrB
   552  	//                 /  \
   553  	//  Adds addrC -> C    D <- Adds addrD
   554  
   555  	var (
   556  		baseRoot       = common.HexToHash("0xff01")
   557  		baseBlockHash  = common.HexToHash("0x01")
   558  		diffRootA      = common.HexToHash("0xff02")
   559  		diffBlockHashA = common.HexToHash("0x02")
   560  		diffRootB      = common.HexToHash("0xff03")
   561  		diffBlockHashB = common.HexToHash("0x03")
   562  		diffRootC      = common.HexToHash("0xff04")
   563  		diffBlockHashC = common.HexToHash("0x04")
   564  		diffRootD      = common.HexToHash("0xff05")
   565  		diffBlockHashD = common.HexToHash("0x05")
   566  	)
   567  
   568  	snaps := NewTestTree(rawdb.NewMemoryDatabase(), baseBlockHash, baseRoot)
   569  	addrA := randomHash()
   570  	accountsA := map[common.Hash][]byte{
   571  		addrA: randomAccount(),
   572  	}
   573  	addrB := randomHash()
   574  	accountsB := map[common.Hash][]byte{
   575  		addrB: randomAccount(),
   576  	}
   577  	addrC := randomHash()
   578  	accountsC := map[common.Hash][]byte{
   579  		addrC: randomAccount(),
   580  	}
   581  	addrD := randomHash()
   582  	accountsD := map[common.Hash][]byte{
   583  		addrD: randomAccount(),
   584  	}
   585  
   586  	// Build the tree
   587  	if err := snaps.Update(diffBlockHashA, diffRootA, baseBlockHash, nil, accountsA, nil); err != nil {
   588  		t.Errorf("failed to create diff layer A: %v", err)
   589  	}
   590  	if err := snaps.Update(diffBlockHashB, diffRootB, diffBlockHashA, nil, accountsB, nil); err != nil {
   591  		t.Errorf("failed to create diff layer B: %v", err)
   592  	}
   593  	if err := snaps.Update(diffBlockHashC, diffRootC, diffBlockHashB, nil, accountsC, nil); err != nil {
   594  		t.Errorf("failed to create diff layer C: %v", err)
   595  	}
   596  	if err := snaps.Update(diffBlockHashD, diffRootD, diffBlockHashB, nil, accountsD, nil); err != nil {
   597  		t.Errorf("failed to create diff layer D: %v", err)
   598  	}
   599  
   600  	assertBlooms := func(snap Snapshot, hitsA, hitsB, hitsC, hitsD bool) {
   601  		dl, ok := snap.(*diffLayer)
   602  		if !ok {
   603  			t.Fatal("snapshot should be a diffLayer")
   604  		}
   605  
   606  		if hitsA != dl.diffed.Contains(accountBloomHasher(addrA)) {
   607  			t.Errorf("expected bloom filter to return %t but got %t", hitsA, !hitsA)
   608  		}
   609  
   610  		if hitsB != dl.diffed.Contains(accountBloomHasher(addrB)) {
   611  			t.Errorf("expected bloom filter to return %t but got %t", hitsB, !hitsB)
   612  		}
   613  
   614  		if hitsC != dl.diffed.Contains(accountBloomHasher(addrC)) {
   615  			t.Errorf("expected bloom filter to return %t but got %t", hitsC, !hitsC)
   616  		}
   617  
   618  		if hitsD != dl.diffed.Contains(accountBloomHasher(addrD)) {
   619  			t.Errorf("expected bloom filter to return %t but got %t", hitsD, !hitsD)
   620  		}
   621  	}
   622  
   623  	// First check that each layer's bloom has all current and ancestor addrs,
   624  	// but no sibling-only addrs.
   625  	assertBlooms(snaps.Snapshot(diffRootB), true, true, false, false)
   626  	assertBlooms(snaps.Snapshot(diffRootC), true, true, true, false)
   627  	assertBlooms(snaps.Snapshot(diffRootD), true, true, false, true)
   628  
   629  	// Flatten diffLayer A, making it a disk layer and trigger a rebloom on B, C
   630  	// and D. If we didn't rebloom, or didn't rebloom recursively, then blooms C
   631  	// and D would still think addrA was in the diff layers
   632  	snaps.verified = true // Bypass validation of junk data
   633  	if err := snaps.Flatten(diffBlockHashA); err != nil {
   634  		t.Errorf("failed to flattten diff block A: %v", err)
   635  	}
   636  
   637  	// Check that no blooms still have addrA, but they have all the others
   638  	assertBlooms(snaps.Snapshot(diffRootB), false, true, false, false)
   639  	assertBlooms(snaps.Snapshot(diffRootC), false, true, true, false)
   640  	assertBlooms(snaps.Snapshot(diffRootD), false, true, false, true)
   641  
   642  	// Flatten diffLayer B, making it a disk layer and trigger a rebloom on C
   643  	// and D. If we didn't rebloom, or didn't rebloom recursively, then blooms C
   644  	// and D would still think addrB was in the diff layers
   645  	if err := snaps.Flatten(diffBlockHashB); err != nil {
   646  		t.Errorf("failed to flattten diff block A: %v", err)
   647  	}
   648  
   649  	// Blooms C and D should now have only their own addrs
   650  	assertBlooms(snaps.Snapshot(diffRootC), false, false, true, false)
   651  	assertBlooms(snaps.Snapshot(diffRootD), false, false, false, true)
   652  }
   653  
   654  // TestReadStateDuringFlattening tests the scenario that, during the
   655  // bottom diff layers are merging which tags these as stale, the read
   656  // happens via a pre-created top snapshot layer which tries to access
   657  // the state in these stale layers. Ensure this read can retrieve the
   658  // right state back(block until the flattening is finished) instead of
   659  // an unexpected error(snapshot layer is stale).
   660  func TestReadStateDuringFlattening(t *testing.T) {
   661  	// setAccount is a helper to construct a random account entry and assign it to
   662  	// an account slot in a snapshot
   663  	setAccount := func(accKey string) map[common.Hash][]byte {
   664  		return map[common.Hash][]byte{
   665  			common.HexToHash(accKey): randomAccount(),
   666  		}
   667  	}
   668  
   669  	var (
   670  		baseRoot       = common.HexToHash("0xff01")
   671  		baseBlockHash  = common.HexToHash("0x01")
   672  		diffRootA      = common.HexToHash("0xff02")
   673  		diffBlockHashA = common.HexToHash("0x02")
   674  		diffRootB      = common.HexToHash("0xff03")
   675  		diffBlockHashB = common.HexToHash("0x03")
   676  		diffRootC      = common.HexToHash("0xff04")
   677  		diffBlockHashC = common.HexToHash("0x04")
   678  	)
   679  
   680  	snaps := NewTestTree(rawdb.NewMemoryDatabase(), baseBlockHash, baseRoot)
   681  
   682  	// 4 layers in total, 3 diff layers and 1 disk layers
   683  	snaps.Update(diffBlockHashA, diffRootA, baseBlockHash, nil, setAccount("0xa1"), nil)
   684  	snaps.Update(diffBlockHashB, diffRootB, diffBlockHashA, nil, setAccount("0xa2"), nil)
   685  	snaps.Update(diffBlockHashC, diffRootC, diffBlockHashB, nil, setAccount("0xa3"), nil)
   686  
   687  	// Obtain the topmost snapshot handler for state accessing
   688  	snap := snaps.Snapshot(diffRootC)
   689  
   690  	// Register the testing hook to access the state after flattening
   691  	var result = make(chan *Account)
   692  	snaps.onFlatten = func() {
   693  		// Spin up a thread to read the account from the pre-created
   694  		// snapshot handler. It's expected to be blocked.
   695  		go func() {
   696  			account, _ := snap.Account(common.HexToHash("0xa1"))
   697  			result <- account
   698  		}()
   699  		select {
   700  		case res := <-result:
   701  			t.Fatalf("Unexpected return %v", res)
   702  		case <-time.NewTimer(time.Millisecond * 300).C:
   703  		}
   704  	}
   705  	// Flatten the first diff block, which will mark the bottom-most layer as stale.
   706  	snaps.Flatten(diffBlockHashA)
   707  	select {
   708  	case account := <-result:
   709  		if account == nil {
   710  			t.Fatal("Failed to retrieve account")
   711  		}
   712  	case <-time.NewTimer(time.Millisecond * 300).C:
   713  		t.Fatal("Unexpected blocker")
   714  	}
   715  }