github.com/klaytn/klaytn@v1.10.2/snapshot/disklayer_test.go (about)

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