mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
139 lines
5.1 KiB
Go
139 lines
5.1 KiB
Go
// (c) 2021-2024, Lux Partners Limited. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package statesync
|
|
|
|
import (
|
|
"bytes"
|
|
"math/rand"
|
|
"testing"
|
|
|
|
"github.com/luxdefi/evm/accounts/keystore"
|
|
"github.com/luxdefi/evm/core/rawdb"
|
|
"github.com/luxdefi/evm/core/state/snapshot"
|
|
"github.com/luxdefi/evm/core/types"
|
|
"github.com/luxdefi/evm/ethdb"
|
|
"github.com/luxdefi/evm/trie"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// assertDBConsistency checks [serverTrieDB] and [clientTrieDB] have the same EVM state trie at [root],
|
|
// and that [clientTrieDB.DiskDB] has corresponding account & snapshot values.
|
|
// Also verifies any code referenced by the EVM state is present in [clientTrieDB] and the hash is correct.
|
|
func assertDBConsistency(t testing.TB, root common.Hash, clientDB ethdb.Database, serverTrieDB, clientTrieDB *trie.Database) {
|
|
numSnapshotAccounts := 0
|
|
accountIt := rawdb.IterateAccountSnapshots(clientDB)
|
|
defer accountIt.Release()
|
|
for accountIt.Next() {
|
|
if !bytes.HasPrefix(accountIt.Key(), rawdb.SnapshotAccountPrefix) || len(accountIt.Key()) != len(rawdb.SnapshotAccountPrefix)+common.HashLength {
|
|
continue
|
|
}
|
|
numSnapshotAccounts++
|
|
}
|
|
if err := accountIt.Error(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
trieAccountLeaves := 0
|
|
|
|
trie.AssertTrieConsistency(t, root, serverTrieDB, clientTrieDB, func(key, val []byte) error {
|
|
trieAccountLeaves++
|
|
accHash := common.BytesToHash(key)
|
|
var acc types.StateAccount
|
|
if err := rlp.DecodeBytes(val, &acc); err != nil {
|
|
return err
|
|
}
|
|
// check snapshot consistency
|
|
snapshotVal := rawdb.ReadAccountSnapshot(clientDB, accHash)
|
|
expectedSnapshotVal := snapshot.SlimAccountRLP(acc.Nonce, acc.Balance, acc.Root, acc.CodeHash)
|
|
assert.Equal(t, expectedSnapshotVal, snapshotVal)
|
|
|
|
// check code consistency
|
|
if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash[:]) {
|
|
codeHash := common.BytesToHash(acc.CodeHash)
|
|
code := rawdb.ReadCode(clientDB, codeHash)
|
|
actualHash := crypto.Keccak256Hash(code)
|
|
assert.NotZero(t, len(code))
|
|
assert.Equal(t, codeHash, actualHash)
|
|
}
|
|
if acc.Root == types.EmptyRootHash {
|
|
return nil
|
|
}
|
|
|
|
storageIt := rawdb.IterateStorageSnapshots(clientDB, accHash)
|
|
defer storageIt.Release()
|
|
|
|
snapshotStorageKeysCount := 0
|
|
for storageIt.Next() {
|
|
snapshotStorageKeysCount++
|
|
}
|
|
|
|
storageTrieLeavesCount := 0
|
|
|
|
// check storage trie and storage snapshot consistency
|
|
trie.AssertTrieConsistency(t, acc.Root, serverTrieDB, clientTrieDB, func(key, val []byte) error {
|
|
storageTrieLeavesCount++
|
|
snapshotVal := rawdb.ReadStorageSnapshot(clientDB, accHash, common.BytesToHash(key))
|
|
assert.Equal(t, val, snapshotVal)
|
|
return nil
|
|
})
|
|
|
|
assert.Equal(t, storageTrieLeavesCount, snapshotStorageKeysCount)
|
|
return nil
|
|
})
|
|
|
|
// Check that the number of accounts in the snapshot matches the number of leaves in the accounts trie
|
|
assert.Equal(t, trieAccountLeaves, numSnapshotAccounts)
|
|
}
|
|
|
|
func fillAccountsWithStorage(t *testing.T, serverDB ethdb.Database, serverTrieDB *trie.Database, root common.Hash, numAccounts int) common.Hash {
|
|
newRoot, _ := trie.FillAccounts(t, serverTrieDB, root, numAccounts, func(t *testing.T, index int, account types.StateAccount) types.StateAccount {
|
|
codeBytes := make([]byte, 256)
|
|
_, err := rand.Read(codeBytes)
|
|
if err != nil {
|
|
t.Fatalf("error reading random code bytes: %v", err)
|
|
}
|
|
|
|
codeHash := crypto.Keccak256Hash(codeBytes)
|
|
rawdb.WriteCode(serverDB, codeHash, codeBytes)
|
|
account.CodeHash = codeHash[:]
|
|
|
|
// now create state trie
|
|
numKeys := 16
|
|
account.Root, _, _ = trie.GenerateTrie(t, serverTrieDB, numKeys, common.HashLength)
|
|
return account
|
|
})
|
|
return newRoot
|
|
}
|
|
|
|
// FillAccountsWithOverlappingStorage adds [numAccounts] randomly generated accounts to the secure trie at [root]
|
|
// and commits it to [trieDB]. For each 3 accounts created:
|
|
// - One does not have a storage trie,
|
|
// - One has a storage trie shared with other accounts (total number of shared storage tries [numOverlappingStorageRoots]),
|
|
// - One has a uniquely generated storage trie,
|
|
// returns the new trie root and a map of funded keys to StateAccount structs.
|
|
func FillAccountsWithOverlappingStorage(
|
|
t *testing.T, trieDB *trie.Database, root common.Hash, numAccounts int, numOverlappingStorageRoots int,
|
|
) (common.Hash, map[*keystore.Key]*types.StateAccount) {
|
|
storageRoots := make([]common.Hash, 0, numOverlappingStorageRoots)
|
|
for i := 0; i < numOverlappingStorageRoots; i++ {
|
|
storageRoot, _, _ := trie.GenerateTrie(t, trieDB, 16, common.HashLength)
|
|
storageRoots = append(storageRoots, storageRoot)
|
|
}
|
|
storageRootIndex := 0
|
|
return trie.FillAccounts(t, trieDB, root, numAccounts, func(t *testing.T, i int, account types.StateAccount) types.StateAccount {
|
|
switch i % 3 {
|
|
case 0: // unmodified account
|
|
case 1: // account with overlapping storage root
|
|
account.Root = storageRoots[storageRootIndex%numOverlappingStorageRoots]
|
|
storageRootIndex++
|
|
case 2: // account with unique storage root
|
|
account.Root, _, _ = trie.GenerateTrie(t, trieDB, 16, common.HashLength)
|
|
}
|
|
|
|
return account
|
|
})
|
|
}
|