github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/core/state/snapshot/disklayer_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  	"testing"
    22  
    23  	"github.com/VictoriaMetrics/fastcache"
    24  	"github.com/tirogen/go-ethereum/common"
    25  	"github.com/tirogen/go-ethereum/core/rawdb"
    26  	"github.com/tirogen/go-ethereum/ethdb/memorydb"
    27  	"github.com/tirogen/go-ethereum/rlp"
    28  )
    29  
    30  // reverse reverses the contents of a byte slice. It's used to update random accs
    31  // with deterministic changes.
    32  func reverse(blob []byte) []byte {
    33  	res := make([]byte, len(blob))
    34  	for i, b := range blob {
    35  		res[len(blob)-1-i] = b
    36  	}
    37  	return res
    38  }
    39  
    40  // Tests that merging something into a disk layer persists it into the database
    41  // and invalidates any previously written and cached values.
    42  func TestDiskMerge(t *testing.T) {
    43  	// Create some accounts in the disk layer
    44  	db := memorydb.New()
    45  
    46  	var (
    47  		accNoModNoCache     = common.Hash{0x1}
    48  		accNoModCache       = common.Hash{0x2}
    49  		accModNoCache       = common.Hash{0x3}
    50  		accModCache         = common.Hash{0x4}
    51  		accDelNoCache       = common.Hash{0x5}
    52  		accDelCache         = common.Hash{0x6}
    53  		conNoModNoCache     = common.Hash{0x7}
    54  		conNoModNoCacheSlot = common.Hash{0x70}
    55  		conNoModCache       = common.Hash{0x8}
    56  		conNoModCacheSlot   = common.Hash{0x80}
    57  		conModNoCache       = common.Hash{0x9}
    58  		conModNoCacheSlot   = common.Hash{0x90}
    59  		conModCache         = common.Hash{0xa}
    60  		conModCacheSlot     = common.Hash{0xa0}
    61  		conDelNoCache       = common.Hash{0xb}
    62  		conDelNoCacheSlot   = common.Hash{0xb0}
    63  		conDelCache         = common.Hash{0xc}
    64  		conDelCacheSlot     = common.Hash{0xc0}
    65  		conNukeNoCache      = common.Hash{0xd}
    66  		conNukeNoCacheSlot  = common.Hash{0xd0}
    67  		conNukeCache        = common.Hash{0xe}
    68  		conNukeCacheSlot    = common.Hash{0xe0}
    69  		baseRoot            = randomHash()
    70  		diffRoot            = randomHash()
    71  	)
    72  
    73  	rawdb.WriteAccountSnapshot(db, accNoModNoCache, accNoModNoCache[:])
    74  	rawdb.WriteAccountSnapshot(db, accNoModCache, accNoModCache[:])
    75  	rawdb.WriteAccountSnapshot(db, accModNoCache, accModNoCache[:])
    76  	rawdb.WriteAccountSnapshot(db, accModCache, accModCache[:])
    77  	rawdb.WriteAccountSnapshot(db, accDelNoCache, accDelNoCache[:])
    78  	rawdb.WriteAccountSnapshot(db, accDelCache, accDelCache[:])
    79  
    80  	rawdb.WriteAccountSnapshot(db, conNoModNoCache, conNoModNoCache[:])
    81  	rawdb.WriteStorageSnapshot(db, conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
    82  	rawdb.WriteAccountSnapshot(db, conNoModCache, conNoModCache[:])
    83  	rawdb.WriteStorageSnapshot(db, conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
    84  	rawdb.WriteAccountSnapshot(db, conModNoCache, conModNoCache[:])
    85  	rawdb.WriteStorageSnapshot(db, conModNoCache, conModNoCacheSlot, conModNoCacheSlot[:])
    86  	rawdb.WriteAccountSnapshot(db, conModCache, conModCache[:])
    87  	rawdb.WriteStorageSnapshot(db, conModCache, conModCacheSlot, conModCacheSlot[:])
    88  	rawdb.WriteAccountSnapshot(db, conDelNoCache, conDelNoCache[:])
    89  	rawdb.WriteStorageSnapshot(db, conDelNoCache, conDelNoCacheSlot, conDelNoCacheSlot[:])
    90  	rawdb.WriteAccountSnapshot(db, conDelCache, conDelCache[:])
    91  	rawdb.WriteStorageSnapshot(db, conDelCache, conDelCacheSlot, conDelCacheSlot[:])
    92  
    93  	rawdb.WriteAccountSnapshot(db, conNukeNoCache, conNukeNoCache[:])
    94  	rawdb.WriteStorageSnapshot(db, conNukeNoCache, conNukeNoCacheSlot, conNukeNoCacheSlot[:])
    95  	rawdb.WriteAccountSnapshot(db, conNukeCache, conNukeCache[:])
    96  	rawdb.WriteStorageSnapshot(db, conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
    97  
    98  	rawdb.WriteSnapshotRoot(db, baseRoot)
    99  
   100  	// Create a disk layer based on the above and cache in some data
   101  	snaps := &Tree{
   102  		layers: map[common.Hash]snapshot{
   103  			baseRoot: &diskLayer{
   104  				diskdb: db,
   105  				cache:  fastcache.New(500 * 1024),
   106  				root:   baseRoot,
   107  			},
   108  		},
   109  	}
   110  	base := snaps.Snapshot(baseRoot)
   111  	base.AccountRLP(accNoModCache)
   112  	base.AccountRLP(accModCache)
   113  	base.AccountRLP(accDelCache)
   114  	base.Storage(conNoModCache, conNoModCacheSlot)
   115  	base.Storage(conModCache, conModCacheSlot)
   116  	base.Storage(conDelCache, conDelCacheSlot)
   117  	base.Storage(conNukeCache, conNukeCacheSlot)
   118  
   119  	// Modify or delete some accounts, flatten everything onto disk
   120  	if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
   121  		accDelNoCache:  {},
   122  		accDelCache:    {},
   123  		conNukeNoCache: {},
   124  		conNukeCache:   {},
   125  	}, map[common.Hash][]byte{
   126  		accModNoCache: reverse(accModNoCache[:]),
   127  		accModCache:   reverse(accModCache[:]),
   128  	}, map[common.Hash]map[common.Hash][]byte{
   129  		conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
   130  		conModCache:   {conModCacheSlot: reverse(conModCacheSlot[:])},
   131  		conDelNoCache: {conDelNoCacheSlot: nil},
   132  		conDelCache:   {conDelCacheSlot: nil},
   133  	}); err != nil {
   134  		t.Fatalf("failed to update snapshot tree: %v", err)
   135  	}
   136  	if err := snaps.Cap(diffRoot, 0); err != nil {
   137  		t.Fatalf("failed to flatten snapshot tree: %v", err)
   138  	}
   139  	// Retrieve all the data through the disk layer and validate it
   140  	base = snaps.Snapshot(diffRoot)
   141  	if _, ok := base.(*diskLayer); !ok {
   142  		t.Fatalf("update not flattend into the disk layer")
   143  	}
   144  
   145  	// assertAccount ensures that an account matches the given blob.
   146  	assertAccount := func(account common.Hash, data []byte) {
   147  		t.Helper()
   148  		blob, err := base.AccountRLP(account)
   149  		if err != nil {
   150  			t.Errorf("account access (%x) failed: %v", account, err)
   151  		} else if !bytes.Equal(blob, data) {
   152  			t.Errorf("account access (%x) mismatch: have %x, want %x", account, blob, data)
   153  		}
   154  	}
   155  	assertAccount(accNoModNoCache, accNoModNoCache[:])
   156  	assertAccount(accNoModCache, accNoModCache[:])
   157  	assertAccount(accModNoCache, reverse(accModNoCache[:]))
   158  	assertAccount(accModCache, reverse(accModCache[:]))
   159  	assertAccount(accDelNoCache, nil)
   160  	assertAccount(accDelCache, nil)
   161  
   162  	// assertStorage ensures that a storage slot matches the given blob.
   163  	assertStorage := func(account common.Hash, slot common.Hash, data []byte) {
   164  		t.Helper()
   165  		blob, err := base.Storage(account, slot)
   166  		if err != nil {
   167  			t.Errorf("storage access (%x:%x) failed: %v", account, slot, err)
   168  		} else if !bytes.Equal(blob, data) {
   169  			t.Errorf("storage access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data)
   170  		}
   171  	}
   172  	assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
   173  	assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
   174  	assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
   175  	assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
   176  	assertStorage(conDelNoCache, conDelNoCacheSlot, nil)
   177  	assertStorage(conDelCache, conDelCacheSlot, nil)
   178  	assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
   179  	assertStorage(conNukeCache, conNukeCacheSlot, nil)
   180  
   181  	// Retrieve all the data directly from the database and validate it
   182  
   183  	// assertDatabaseAccount ensures that an account from the database matches the given blob.
   184  	assertDatabaseAccount := func(account common.Hash, data []byte) {
   185  		t.Helper()
   186  		if blob := rawdb.ReadAccountSnapshot(db, account); !bytes.Equal(blob, data) {
   187  			t.Errorf("account database access (%x) mismatch: have %x, want %x", account, blob, data)
   188  		}
   189  	}
   190  	assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:])
   191  	assertDatabaseAccount(accNoModCache, accNoModCache[:])
   192  	assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:]))
   193  	assertDatabaseAccount(accModCache, reverse(accModCache[:]))
   194  	assertDatabaseAccount(accDelNoCache, nil)
   195  	assertDatabaseAccount(accDelCache, nil)
   196  
   197  	// assertDatabaseStorage ensures that a storage slot from the database matches the given blob.
   198  	assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) {
   199  		t.Helper()
   200  		if blob := rawdb.ReadStorageSnapshot(db, account, slot); !bytes.Equal(blob, data) {
   201  			t.Errorf("storage database access (%x:%x) mismatch: have %x, want %x", account, slot, blob, data)
   202  		}
   203  	}
   204  	assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
   205  	assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
   206  	assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
   207  	assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
   208  	assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil)
   209  	assertDatabaseStorage(conDelCache, conDelCacheSlot, nil)
   210  	assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
   211  	assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil)
   212  }
   213  
   214  // Tests that merging something into a disk layer persists it into the database
   215  // and invalidates any previously written and cached values, discarding anything
   216  // after the in-progress generation marker.
   217  func TestDiskPartialMerge(t *testing.T) {
   218  	// Iterate the test a few times to ensure we pick various internal orderings
   219  	// for the data slots as well as the progress marker.
   220  	for i := 0; i < 1024; i++ {
   221  		// Create some accounts in the disk layer
   222  		db := memorydb.New()
   223  
   224  		var (
   225  			accNoModNoCache     = randomHash()
   226  			accNoModCache       = randomHash()
   227  			accModNoCache       = randomHash()
   228  			accModCache         = randomHash()
   229  			accDelNoCache       = randomHash()
   230  			accDelCache         = randomHash()
   231  			conNoModNoCache     = randomHash()
   232  			conNoModNoCacheSlot = randomHash()
   233  			conNoModCache       = randomHash()
   234  			conNoModCacheSlot   = randomHash()
   235  			conModNoCache       = randomHash()
   236  			conModNoCacheSlot   = randomHash()
   237  			conModCache         = randomHash()
   238  			conModCacheSlot     = randomHash()
   239  			conDelNoCache       = randomHash()
   240  			conDelNoCacheSlot   = randomHash()
   241  			conDelCache         = randomHash()
   242  			conDelCacheSlot     = randomHash()
   243  			conNukeNoCache      = randomHash()
   244  			conNukeNoCacheSlot  = randomHash()
   245  			conNukeCache        = randomHash()
   246  			conNukeCacheSlot    = randomHash()
   247  			baseRoot            = randomHash()
   248  			diffRoot            = randomHash()
   249  			genMarker           = append(randomHash().Bytes(), randomHash().Bytes()...)
   250  		)
   251  
   252  		// insertAccount injects an account into the database if it's after the
   253  		// generator marker, drops the op otherwise. This is needed to seed the
   254  		// database with a valid starting snapshot.
   255  		insertAccount := func(account common.Hash, data []byte) {
   256  			if bytes.Compare(account[:], genMarker) <= 0 {
   257  				rawdb.WriteAccountSnapshot(db, account, data[:])
   258  			}
   259  		}
   260  		insertAccount(accNoModNoCache, accNoModNoCache[:])
   261  		insertAccount(accNoModCache, accNoModCache[:])
   262  		insertAccount(accModNoCache, accModNoCache[:])
   263  		insertAccount(accModCache, accModCache[:])
   264  		insertAccount(accDelNoCache, accDelNoCache[:])
   265  		insertAccount(accDelCache, accDelCache[:])
   266  
   267  		// insertStorage injects a storage slot into the database if it's after
   268  		// the  generator marker, drops the op otherwise. This is needed to seed
   269  		// the  database with a valid starting snapshot.
   270  		insertStorage := func(account common.Hash, slot common.Hash, data []byte) {
   271  			if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 {
   272  				rawdb.WriteStorageSnapshot(db, account, slot, data[:])
   273  			}
   274  		}
   275  		insertAccount(conNoModNoCache, conNoModNoCache[:])
   276  		insertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
   277  		insertAccount(conNoModCache, conNoModCache[:])
   278  		insertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
   279  		insertAccount(conModNoCache, conModNoCache[:])
   280  		insertStorage(conModNoCache, conModNoCacheSlot, conModNoCacheSlot[:])
   281  		insertAccount(conModCache, conModCache[:])
   282  		insertStorage(conModCache, conModCacheSlot, conModCacheSlot[:])
   283  		insertAccount(conDelNoCache, conDelNoCache[:])
   284  		insertStorage(conDelNoCache, conDelNoCacheSlot, conDelNoCacheSlot[:])
   285  		insertAccount(conDelCache, conDelCache[:])
   286  		insertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:])
   287  
   288  		insertAccount(conNukeNoCache, conNukeNoCache[:])
   289  		insertStorage(conNukeNoCache, conNukeNoCacheSlot, conNukeNoCacheSlot[:])
   290  		insertAccount(conNukeCache, conNukeCache[:])
   291  		insertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
   292  
   293  		rawdb.WriteSnapshotRoot(db, baseRoot)
   294  
   295  		// Create a disk layer based on the above using a random progress marker
   296  		// and cache in some data.
   297  		snaps := &Tree{
   298  			layers: map[common.Hash]snapshot{
   299  				baseRoot: &diskLayer{
   300  					diskdb: db,
   301  					cache:  fastcache.New(500 * 1024),
   302  					root:   baseRoot,
   303  				},
   304  			},
   305  		}
   306  		snaps.layers[baseRoot].(*diskLayer).genMarker = genMarker
   307  		base := snaps.Snapshot(baseRoot)
   308  
   309  		// assertAccount ensures that an account matches the given blob if it's
   310  		// already covered by the disk snapshot, and errors out otherwise.
   311  		assertAccount := func(account common.Hash, data []byte) {
   312  			t.Helper()
   313  			blob, err := base.AccountRLP(account)
   314  			if bytes.Compare(account[:], genMarker) > 0 && err != ErrNotCoveredYet {
   315  				t.Fatalf("test %d: post-marker (%x) account access (%x) succeeded: %x", i, genMarker, account, blob)
   316  			}
   317  			if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) {
   318  				t.Fatalf("test %d: pre-marker (%x) account access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data)
   319  			}
   320  		}
   321  		assertAccount(accNoModCache, accNoModCache[:])
   322  		assertAccount(accModCache, accModCache[:])
   323  		assertAccount(accDelCache, accDelCache[:])
   324  
   325  		// assertStorage ensures that a storage slot matches the given blob if
   326  		// it's already covered by the disk snapshot, and errors out otherwise.
   327  		assertStorage := func(account common.Hash, slot common.Hash, data []byte) {
   328  			t.Helper()
   329  			blob, err := base.Storage(account, slot)
   330  			if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && err != ErrNotCoveredYet {
   331  				t.Fatalf("test %d: post-marker (%x) storage access (%x:%x) succeeded: %x", i, genMarker, account, slot, blob)
   332  			}
   333  			if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) {
   334  				t.Fatalf("test %d: pre-marker (%x) storage access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data)
   335  			}
   336  		}
   337  		assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
   338  		assertStorage(conModCache, conModCacheSlot, conModCacheSlot[:])
   339  		assertStorage(conDelCache, conDelCacheSlot, conDelCacheSlot[:])
   340  		assertStorage(conNukeCache, conNukeCacheSlot, conNukeCacheSlot[:])
   341  
   342  		// Modify or delete some accounts, flatten everything onto disk
   343  		if err := snaps.Update(diffRoot, baseRoot, map[common.Hash]struct{}{
   344  			accDelNoCache:  {},
   345  			accDelCache:    {},
   346  			conNukeNoCache: {},
   347  			conNukeCache:   {},
   348  		}, map[common.Hash][]byte{
   349  			accModNoCache: reverse(accModNoCache[:]),
   350  			accModCache:   reverse(accModCache[:]),
   351  		}, map[common.Hash]map[common.Hash][]byte{
   352  			conModNoCache: {conModNoCacheSlot: reverse(conModNoCacheSlot[:])},
   353  			conModCache:   {conModCacheSlot: reverse(conModCacheSlot[:])},
   354  			conDelNoCache: {conDelNoCacheSlot: nil},
   355  			conDelCache:   {conDelCacheSlot: nil},
   356  		}); err != nil {
   357  			t.Fatalf("test %d: failed to update snapshot tree: %v", i, err)
   358  		}
   359  		if err := snaps.Cap(diffRoot, 0); err != nil {
   360  			t.Fatalf("test %d: failed to flatten snapshot tree: %v", i, err)
   361  		}
   362  		// Retrieve all the data through the disk layer and validate it
   363  		base = snaps.Snapshot(diffRoot)
   364  		if _, ok := base.(*diskLayer); !ok {
   365  			t.Fatalf("test %d: update not flattend into the disk layer", i)
   366  		}
   367  		assertAccount(accNoModNoCache, accNoModNoCache[:])
   368  		assertAccount(accNoModCache, accNoModCache[:])
   369  		assertAccount(accModNoCache, reverse(accModNoCache[:]))
   370  		assertAccount(accModCache, reverse(accModCache[:]))
   371  		assertAccount(accDelNoCache, nil)
   372  		assertAccount(accDelCache, nil)
   373  
   374  		assertStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
   375  		assertStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
   376  		assertStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
   377  		assertStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
   378  		assertStorage(conDelNoCache, conDelNoCacheSlot, nil)
   379  		assertStorage(conDelCache, conDelCacheSlot, nil)
   380  		assertStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
   381  		assertStorage(conNukeCache, conNukeCacheSlot, nil)
   382  
   383  		// Retrieve all the data directly from the database and validate it
   384  
   385  		// assertDatabaseAccount ensures that an account inside the database matches
   386  		// the given blob if it's already covered by the disk snapshot, and does not
   387  		// exist otherwise.
   388  		assertDatabaseAccount := func(account common.Hash, data []byte) {
   389  			t.Helper()
   390  			blob := rawdb.ReadAccountSnapshot(db, account)
   391  			if bytes.Compare(account[:], genMarker) > 0 && blob != nil {
   392  				t.Fatalf("test %d: post-marker (%x) account database access (%x) succeeded: %x", i, genMarker, account, blob)
   393  			}
   394  			if bytes.Compare(account[:], genMarker) <= 0 && !bytes.Equal(blob, data) {
   395  				t.Fatalf("test %d: pre-marker (%x) account database access (%x) mismatch: have %x, want %x", i, genMarker, account, blob, data)
   396  			}
   397  		}
   398  		assertDatabaseAccount(accNoModNoCache, accNoModNoCache[:])
   399  		assertDatabaseAccount(accNoModCache, accNoModCache[:])
   400  		assertDatabaseAccount(accModNoCache, reverse(accModNoCache[:]))
   401  		assertDatabaseAccount(accModCache, reverse(accModCache[:]))
   402  		assertDatabaseAccount(accDelNoCache, nil)
   403  		assertDatabaseAccount(accDelCache, nil)
   404  
   405  		// assertDatabaseStorage ensures that a storage slot inside the database
   406  		// matches the given blob if it's already covered by the disk snapshot,
   407  		// and does not exist otherwise.
   408  		assertDatabaseStorage := func(account common.Hash, slot common.Hash, data []byte) {
   409  			t.Helper()
   410  			blob := rawdb.ReadStorageSnapshot(db, account, slot)
   411  			if bytes.Compare(append(account[:], slot[:]...), genMarker) > 0 && blob != nil {
   412  				t.Fatalf("test %d: post-marker (%x) storage database access (%x:%x) succeeded: %x", i, genMarker, account, slot, blob)
   413  			}
   414  			if bytes.Compare(append(account[:], slot[:]...), genMarker) <= 0 && !bytes.Equal(blob, data) {
   415  				t.Fatalf("test %d: pre-marker (%x) storage database access (%x:%x) mismatch: have %x, want %x", i, genMarker, account, slot, blob, data)
   416  			}
   417  		}
   418  		assertDatabaseStorage(conNoModNoCache, conNoModNoCacheSlot, conNoModNoCacheSlot[:])
   419  		assertDatabaseStorage(conNoModCache, conNoModCacheSlot, conNoModCacheSlot[:])
   420  		assertDatabaseStorage(conModNoCache, conModNoCacheSlot, reverse(conModNoCacheSlot[:]))
   421  		assertDatabaseStorage(conModCache, conModCacheSlot, reverse(conModCacheSlot[:]))
   422  		assertDatabaseStorage(conDelNoCache, conDelNoCacheSlot, nil)
   423  		assertDatabaseStorage(conDelCache, conDelCacheSlot, nil)
   424  		assertDatabaseStorage(conNukeNoCache, conNukeNoCacheSlot, nil)
   425  		assertDatabaseStorage(conNukeCache, conNukeCacheSlot, nil)
   426  	}
   427  }
   428  
   429  // Tests that when the bottom-most diff layer is merged into the disk
   430  // layer whether the corresponding generator is persisted correctly.
   431  func TestDiskGeneratorPersistence(t *testing.T) {
   432  	var (
   433  		accOne        = randomHash()
   434  		accTwo        = randomHash()
   435  		accOneSlotOne = randomHash()
   436  		accOneSlotTwo = randomHash()
   437  
   438  		accThree     = randomHash()
   439  		accThreeSlot = randomHash()
   440  		baseRoot     = randomHash()
   441  		diffRoot     = randomHash()
   442  		diffTwoRoot  = randomHash()
   443  		genMarker    = append(randomHash().Bytes(), randomHash().Bytes()...)
   444  	)
   445  	// Testing scenario 1, the disk layer is still under the construction.
   446  	db := rawdb.NewMemoryDatabase()
   447  
   448  	rawdb.WriteAccountSnapshot(db, accOne, accOne[:])
   449  	rawdb.WriteStorageSnapshot(db, accOne, accOneSlotOne, accOneSlotOne[:])
   450  	rawdb.WriteStorageSnapshot(db, accOne, accOneSlotTwo, accOneSlotTwo[:])
   451  	rawdb.WriteSnapshotRoot(db, baseRoot)
   452  
   453  	// Create a disk layer based on all above updates
   454  	snaps := &Tree{
   455  		layers: map[common.Hash]snapshot{
   456  			baseRoot: &diskLayer{
   457  				diskdb:    db,
   458  				cache:     fastcache.New(500 * 1024),
   459  				root:      baseRoot,
   460  				genMarker: genMarker,
   461  			},
   462  		},
   463  	}
   464  	// Modify or delete some accounts, flatten everything onto disk
   465  	if err := snaps.Update(diffRoot, baseRoot, nil, map[common.Hash][]byte{
   466  		accTwo: accTwo[:],
   467  	}, nil); err != nil {
   468  		t.Fatalf("failed to update snapshot tree: %v", err)
   469  	}
   470  	if err := snaps.Cap(diffRoot, 0); err != nil {
   471  		t.Fatalf("failed to flatten snapshot tree: %v", err)
   472  	}
   473  	blob := rawdb.ReadSnapshotGenerator(db)
   474  	var generator journalGenerator
   475  	if err := rlp.DecodeBytes(blob, &generator); err != nil {
   476  		t.Fatalf("Failed to decode snapshot generator %v", err)
   477  	}
   478  	if !bytes.Equal(generator.Marker, genMarker) {
   479  		t.Fatalf("Generator marker is not matched")
   480  	}
   481  	// Test scenario 2, the disk layer is fully generated
   482  	// Modify or delete some accounts, flatten everything onto disk
   483  	if err := snaps.Update(diffTwoRoot, diffRoot, nil, map[common.Hash][]byte{
   484  		accThree: accThree.Bytes(),
   485  	}, map[common.Hash]map[common.Hash][]byte{
   486  		accThree: {accThreeSlot: accThreeSlot.Bytes()},
   487  	}); err != nil {
   488  		t.Fatalf("failed to update snapshot tree: %v", err)
   489  	}
   490  	diskLayer := snaps.layers[snaps.diskRoot()].(*diskLayer)
   491  	diskLayer.genMarker = nil // Construction finished
   492  	if err := snaps.Cap(diffTwoRoot, 0); err != nil {
   493  		t.Fatalf("failed to flatten snapshot tree: %v", err)
   494  	}
   495  	blob = rawdb.ReadSnapshotGenerator(db)
   496  	if err := rlp.DecodeBytes(blob, &generator); err != nil {
   497  		t.Fatalf("Failed to decode snapshot generator %v", err)
   498  	}
   499  	if len(generator.Marker) != 0 {
   500  		t.Fatalf("Failed to update snapshot generator")
   501  	}
   502  }
   503  
   504  // Tests that merging something into a disk layer persists it into the database
   505  // and invalidates any previously written and cached values, discarding anything
   506  // after the in-progress generation marker.
   507  //
   508  // This test case is a tiny specialized case of TestDiskPartialMerge, which tests
   509  // some very specific cornercases that random tests won't ever trigger.
   510  func TestDiskMidAccountPartialMerge(t *testing.T) {
   511  	// TODO(@karalabe) ?
   512  }
   513  
   514  // TestDiskSeek tests that seek-operations work on the disk layer
   515  func TestDiskSeek(t *testing.T) {
   516  	// Create some accounts in the disk layer
   517  	db := rawdb.NewMemoryDatabase()
   518  	defer db.Close()
   519  
   520  	// Fill even keys [0,2,4...]
   521  	for i := 0; i < 0xff; i += 2 {
   522  		acc := common.Hash{byte(i)}
   523  		rawdb.WriteAccountSnapshot(db, acc, acc[:])
   524  	}
   525  	// Add an 'higher' key, with incorrect (higher) prefix
   526  	highKey := []byte{rawdb.SnapshotAccountPrefix[0] + 1}
   527  	db.Put(highKey, []byte{0xff, 0xff})
   528  
   529  	baseRoot := randomHash()
   530  	rawdb.WriteSnapshotRoot(db, baseRoot)
   531  
   532  	snaps := &Tree{
   533  		layers: map[common.Hash]snapshot{
   534  			baseRoot: &diskLayer{
   535  				diskdb: db,
   536  				cache:  fastcache.New(500 * 1024),
   537  				root:   baseRoot,
   538  			},
   539  		},
   540  	}
   541  	// Test some different seek positions
   542  	type testcase struct {
   543  		pos    byte
   544  		expkey byte
   545  	}
   546  	var cases = []testcase{
   547  		{0xff, 0x55}, // this should exit immediately without checking key
   548  		{0x01, 0x02},
   549  		{0xfe, 0xfe},
   550  		{0xfd, 0xfe},
   551  		{0x00, 0x00},
   552  	}
   553  	for i, tc := range cases {
   554  		it, err := snaps.AccountIterator(baseRoot, common.Hash{tc.pos})
   555  		if err != nil {
   556  			t.Fatalf("case %d, error: %v", i, err)
   557  		}
   558  		count := 0
   559  		for it.Next() {
   560  			k, v, err := it.Hash()[0], it.Account()[0], it.Error()
   561  			if err != nil {
   562  				t.Fatalf("test %d, item %d, error: %v", i, count, err)
   563  			}
   564  			// First item in iterator should have the expected key
   565  			if count == 0 && k != tc.expkey {
   566  				t.Fatalf("test %d, item %d, got %v exp %v", i, count, k, tc.expkey)
   567  			}
   568  			count++
   569  			if v != k {
   570  				t.Fatalf("test %d, item %d, value wrong, got %v exp %v", i, count, v, k)
   571  			}
   572  		}
   573  	}
   574  }