github.com/MetalBlockchain/subnet-evm@v0.4.9/sync/statesync/test_sync.go (about)

     1  // (c) 2021-2022, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package statesync
     5  
     6  import (
     7  	"bytes"
     8  	"math/rand"
     9  	"testing"
    10  
    11  	"github.com/MetalBlockchain/subnet-evm/accounts/keystore"
    12  	"github.com/MetalBlockchain/subnet-evm/core/rawdb"
    13  	"github.com/MetalBlockchain/subnet-evm/core/state/snapshot"
    14  	"github.com/MetalBlockchain/subnet-evm/core/types"
    15  	"github.com/MetalBlockchain/subnet-evm/trie"
    16  	"github.com/ethereum/go-ethereum/common"
    17  	"github.com/ethereum/go-ethereum/crypto"
    18  	"github.com/ethereum/go-ethereum/rlp"
    19  	"github.com/stretchr/testify/assert"
    20  )
    21  
    22  // assertDBConsistency checks [serverTrieDB] and [clientTrieDB] have the same EVM state trie at [root],
    23  // and that [clientTrieDB.DiskDB] has corresponding account & snapshot values.
    24  // Also verifies any code referenced by the EVM state is present in [clientTrieDB] and the hash is correct.
    25  func assertDBConsistency(t testing.TB, root common.Hash, serverTrieDB, clientTrieDB *trie.Database) {
    26  	clientDB := clientTrieDB.DiskDB()
    27  	numSnapshotAccounts := 0
    28  	accountIt := rawdb.IterateAccountSnapshots(clientDB)
    29  	defer accountIt.Release()
    30  	for accountIt.Next() {
    31  		if !bytes.HasPrefix(accountIt.Key(), rawdb.SnapshotAccountPrefix) || len(accountIt.Key()) != len(rawdb.SnapshotAccountPrefix)+common.HashLength {
    32  			continue
    33  		}
    34  		numSnapshotAccounts++
    35  	}
    36  	if err := accountIt.Error(); err != nil {
    37  		t.Fatal(err)
    38  	}
    39  	trieAccountLeaves := 0
    40  
    41  	trie.AssertTrieConsistency(t, root, serverTrieDB, clientTrieDB, func(key, val []byte) error {
    42  		trieAccountLeaves++
    43  		accHash := common.BytesToHash(key)
    44  		var acc types.StateAccount
    45  		if err := rlp.DecodeBytes(val, &acc); err != nil {
    46  			return err
    47  		}
    48  		// check snapshot consistency
    49  		snapshotVal := rawdb.ReadAccountSnapshot(clientTrieDB.DiskDB(), accHash)
    50  		expectedSnapshotVal := snapshot.SlimAccountRLP(acc.Nonce, acc.Balance, acc.Root, acc.CodeHash)
    51  		assert.Equal(t, expectedSnapshotVal, snapshotVal)
    52  
    53  		// check code consistency
    54  		if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) {
    55  			codeHash := common.BytesToHash(acc.CodeHash)
    56  			code := rawdb.ReadCode(clientTrieDB.DiskDB(), codeHash)
    57  			actualHash := crypto.Keccak256Hash(code)
    58  			assert.NotZero(t, len(code))
    59  			assert.Equal(t, codeHash, actualHash)
    60  		}
    61  		if acc.Root == types.EmptyRootHash {
    62  			return nil
    63  		}
    64  
    65  		storageIt := rawdb.IterateStorageSnapshots(clientDB, accHash)
    66  		defer storageIt.Release()
    67  
    68  		snapshotStorageKeysCount := 0
    69  		for storageIt.Next() {
    70  			snapshotStorageKeysCount++
    71  		}
    72  
    73  		storageTrieLeavesCount := 0
    74  
    75  		// check storage trie and storage snapshot consistency
    76  		trie.AssertTrieConsistency(t, acc.Root, serverTrieDB, clientTrieDB, func(key, val []byte) error {
    77  			storageTrieLeavesCount++
    78  			snapshotVal := rawdb.ReadStorageSnapshot(clientTrieDB.DiskDB(), accHash, common.BytesToHash(key))
    79  			assert.Equal(t, val, snapshotVal)
    80  			return nil
    81  		})
    82  
    83  		assert.Equal(t, storageTrieLeavesCount, snapshotStorageKeysCount)
    84  		return nil
    85  	})
    86  
    87  	// Check that the number of accounts in the snapshot matches the number of leaves in the accounts trie
    88  	assert.Equal(t, trieAccountLeaves, numSnapshotAccounts)
    89  }
    90  
    91  func fillAccountsWithStorage(t *testing.T, serverTrieDB *trie.Database, root common.Hash, numAccounts int) common.Hash {
    92  	newRoot, _ := trie.FillAccounts(t, serverTrieDB, root, numAccounts, func(t *testing.T, index int, account types.StateAccount) types.StateAccount {
    93  		codeBytes := make([]byte, 256)
    94  		_, err := rand.Read(codeBytes)
    95  		if err != nil {
    96  			t.Fatalf("error reading random code bytes: %v", err)
    97  		}
    98  
    99  		codeHash := crypto.Keccak256Hash(codeBytes)
   100  		rawdb.WriteCode(serverTrieDB.DiskDB(), codeHash, codeBytes)
   101  		account.CodeHash = codeHash[:]
   102  
   103  		// now create state trie
   104  		numKeys := 16
   105  		account.Root, _, _ = trie.GenerateTrie(t, serverTrieDB, numKeys, common.HashLength)
   106  		return account
   107  	})
   108  	return newRoot
   109  }
   110  
   111  // FillAccountsWithOverlappingStorage adds [numAccounts] randomly generated accounts to the secure trie at [root]
   112  // and commits it to [trieDB]. For each 3 accounts created:
   113  // - One does not have a storage trie,
   114  // - One has a storage trie shared with other accounts (total number of shared storage tries [numOverlappingStorageRoots]),
   115  // - One has a uniquely generated storage trie,
   116  // returns the new trie root and a map of funded keys to StateAccount structs.
   117  func FillAccountsWithOverlappingStorage(
   118  	t *testing.T, trieDB *trie.Database, root common.Hash, numAccounts int, numOverlappingStorageRoots int,
   119  ) (common.Hash, map[*keystore.Key]*types.StateAccount) {
   120  	storageRoots := make([]common.Hash, 0, numOverlappingStorageRoots)
   121  	for i := 0; i < numOverlappingStorageRoots; i++ {
   122  		storageRoot, _, _ := trie.GenerateTrie(t, trieDB, 16, common.HashLength)
   123  		storageRoots = append(storageRoots, storageRoot)
   124  	}
   125  	storageRootIndex := 0
   126  	return trie.FillAccounts(t, trieDB, root, numAccounts, func(t *testing.T, i int, account types.StateAccount) types.StateAccount {
   127  		switch i % 3 {
   128  		case 0: // unmodified account
   129  		case 1: // account with overlapping storage root
   130  			account.Root = storageRoots[storageRootIndex%numOverlappingStorageRoots]
   131  			storageRootIndex++
   132  		case 2: // account with unique storage root
   133  			account.Root, _, _ = trie.GenerateTrie(t, trieDB, 16, common.HashLength)
   134  		}
   135  
   136  		return account
   137  	})
   138  }