@ -42,7 +42,7 @@ type testAccount struct {
func makeTestState ( ) ( ethdb . Database , Database , common . Hash , [ ] * testAccount ) {
// Create an empty state
db := rawdb . NewMemoryDatabase ( )
sdb := NewDatabase ( db )
sdb := NewDatabaseWithConfig ( db , & trie . Config { Preimages : true } )
state , _ := New ( types . EmptyRootHash , sdb , nil )
// Fill it with some arbitrary data
@ -100,28 +100,9 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou
}
}
// checkTrieConsistency checks that all nodes in a (sub-)trie are indeed present.
func checkTrieConsistency ( db ethdb . Database , root common . Hash ) error {
if v , _ := db . Get ( root [ : ] ) ; v == nil {
return nil // Consider a non existent state consistent.
}
trie , err := trie . New ( trie . StateTrieID ( root ) , trie . NewDatabase ( db ) )
if err != nil {
return err
}
it := trie . MustNodeIterator ( nil )
for it . Next ( true ) {
}
return it . Error ( )
}
// checkStateConsistency checks that all data of a state root is present.
func checkStateConsistency ( db ethdb . Database , root common . Hash ) error {
// Create and iterate a state trie rooted in a sub-node
if _ , err := db . Get ( root . Bytes ( ) ) ; err != nil {
return nil // Consider a non existent state consistent.
}
state , err := New ( root , NewDatabase ( db ) , nil )
state , err := New ( root , NewDatabaseWithConfig ( db , & trie . Config { Preimages : true } ) , nil )
if err != nil {
return err
}
@ -171,7 +152,7 @@ type stateElement struct {
func testIterativeStateSync ( t * testing . T , count int , commit bool , bypath bool ) {
// Create a random state to copy
_ , srcDb , srcRoot , srcAccounts := makeTestState ( )
srcDisk , srcDb , srcRoot , srcAccounts := makeTestState ( )
if commit {
srcDb . TrieDB ( ) . Commit ( srcRoot , false )
}
@ -204,7 +185,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
codeResults = make ( [ ] trie . CodeSyncResult , len ( codeElements ) )
)
for i , element := range codeElements {
data , err := srcDb . ContractCode ( common . Hash { } , element . code )
data , err := srcDb . ContractCode ( common . Address { } , element . code )
if err != nil {
t . Fatalf ( "failed to retrieve contract bytecode for hash %x" , element . code )
}
@ -274,6 +255,10 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
} )
}
}
// Copy the preimages from source db in order to traverse the state.
srcDb . TrieDB ( ) . WritePreimages ( )
copyPreimages ( srcDisk , dstDb )
// Cross check that the two states are in sync
checkStateAccounts ( t , dstDb , srcRoot , srcAccounts )
}
@ -282,7 +267,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
// partial results are returned, and the others sent only later.
func TestIterativeDelayedStateSync ( t * testing . T ) {
// Create a random state to copy
_ , srcDb , srcRoot , srcAccounts := makeTestState ( )
srcDisk , srcDb , srcRoot , srcAccounts := makeTestState ( )
// Create a destination state and sync with the scheduler
dstDb := rawdb . NewMemoryDatabase ( )
@ -312,7 +297,7 @@ func TestIterativeDelayedStateSync(t *testing.T) {
if len ( codeElements ) > 0 {
codeResults := make ( [ ] trie . CodeSyncResult , len ( codeElements ) / 2 + 1 )
for i , element := range codeElements [ : len ( codeResults ) ] {
data , err := srcDb . ContractCode ( common . Hash { } , element . code )
data , err := srcDb . ContractCode ( common . Address { } , element . code )
if err != nil {
t . Fatalf ( "failed to retrieve contract bytecode for %x" , element . code )
}
@ -363,6 +348,10 @@ func TestIterativeDelayedStateSync(t *testing.T) {
} )
}
}
// Copy the preimages from source db in order to traverse the state.
srcDb . TrieDB ( ) . WritePreimages ( )
copyPreimages ( srcDisk , dstDb )
// Cross check that the two states are in sync
checkStateAccounts ( t , dstDb , srcRoot , srcAccounts )
}
@ -375,7 +364,7 @@ func TestIterativeRandomStateSyncBatched(t *testing.T) { testIterativeRandomS
func testIterativeRandomStateSync ( t * testing . T , count int ) {
// Create a random state to copy
_ , srcDb , srcRoot , srcAccounts := makeTestState ( )
srcDisk , srcDb , srcRoot , srcAccounts := makeTestState ( )
// Create a destination state and sync with the scheduler
dstDb := rawdb . NewMemoryDatabase ( )
@ -399,7 +388,7 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
if len ( codeQueue ) > 0 {
results := make ( [ ] trie . CodeSyncResult , 0 , len ( codeQueue ) )
for hash := range codeQueue {
data , err := srcDb . ContractCode ( common . Hash { } , hash )
data , err := srcDb . ContractCode ( common . Address { } , hash )
if err != nil {
t . Fatalf ( "failed to retrieve node data for %x" , hash )
}
@ -447,6 +436,10 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
codeQueue [ hash ] = struct { } { }
}
}
// Copy the preimages from source db in order to traverse the state.
srcDb . TrieDB ( ) . WritePreimages ( )
copyPreimages ( srcDisk , dstDb )
// Cross check that the two states are in sync
checkStateAccounts ( t , dstDb , srcRoot , srcAccounts )
}
@ -455,7 +448,7 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
// partial results are returned (Even those randomly), others sent only later.
func TestIterativeRandomDelayedStateSync ( t * testing . T ) {
// Create a random state to copy
_ , srcDb , srcRoot , srcAccounts := makeTestState ( )
srcDisk , srcDb , srcRoot , srcAccounts := makeTestState ( )
// Create a destination state and sync with the scheduler
dstDb := rawdb . NewMemoryDatabase ( )
@ -481,7 +474,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
for hash := range codeQueue {
delete ( codeQueue , hash )
data , err := srcDb . ContractCode ( common . Hash { } , hash )
data , err := srcDb . ContractCode ( common . Address { } , hash )
if err != nil {
t . Fatalf ( "failed to retrieve node data for %x" , hash )
}
@ -537,6 +530,10 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
codeQueue [ hash ] = struct { } { }
}
}
// Copy the preimages from source db in order to traverse the state.
srcDb . TrieDB ( ) . WritePreimages ( )
copyPreimages ( srcDisk , dstDb )
// Cross check that the two states are in sync
checkStateAccounts ( t , dstDb , srcRoot , srcAccounts )
}
@ -555,7 +552,6 @@ func TestIncompleteStateSync(t *testing.T) {
}
}
isCode [ types . EmptyCodeHash ] = struct { } { }
checkTrieConsistency ( db , srcRoot )
// Create a destination state and sync with the scheduler
dstDb := rawdb . NewMemoryDatabase ( )
@ -588,7 +584,7 @@ func TestIncompleteStateSync(t *testing.T) {
if len ( codeQueue ) > 0 {
results := make ( [ ] trie . CodeSyncResult , 0 , len ( codeQueue ) )
for hash := range codeQueue {
data , err := srcDb . ContractCode ( common . Hash { } , hash )
data , err := srcDb . ContractCode ( common . Address { } , hash )
if err != nil {
t . Fatalf ( "failed to retrieve node data for %x" , hash )
}
@ -602,7 +598,6 @@ func TestIncompleteStateSync(t *testing.T) {
}
}
}
var nodehashes [ ] common . Hash
if len ( nodeQueue ) > 0 {
results := make ( [ ] trie . NodeSyncResult , 0 , len ( nodeQueue ) )
for path , element := range nodeQueue {
@ -617,7 +612,6 @@ func TestIncompleteStateSync(t *testing.T) {
addedPaths = append ( addedPaths , element . path )
addedHashes = append ( addedHashes , element . hash )
}
nodehashes = append ( nodehashes , element . hash )
}
// Process each of the state nodes
for _ , result := range results {
@ -632,13 +626,6 @@ func TestIncompleteStateSync(t *testing.T) {
}
batch . Write ( )
for _ , root := range nodehashes {
// Can't use checkStateConsistency here because subtrie keys may have odd
// length and crash in LeafKey.
if err := checkTrieConsistency ( dstDb , root ) ; err != nil {
t . Fatalf ( "state inconsistent: %v" , err )
}
}
// Fetch the next batch to retrieve
nodeQueue = make ( map [ string ] stateElement )
codeQueue = make ( map [ common . Hash ] struct { } )
@ -654,6 +641,10 @@ func TestIncompleteStateSync(t *testing.T) {
codeQueue [ hash ] = struct { } { }
}
}
// Copy the preimages from source db in order to traverse the state.
srcDb . TrieDB ( ) . WritePreimages ( )
copyPreimages ( db , dstDb )
// Sanity check that removing any node from the database is detected
for _ , node := range addedCodes {
val := rawdb . ReadCode ( dstDb , node )
@ -678,3 +669,15 @@ func TestIncompleteStateSync(t *testing.T) {
rawdb . WriteTrieNode ( dstDb , owner , inner , hash , val , scheme )
}
}
func copyPreimages ( srcDb , dstDb ethdb . Database ) {
it := srcDb . NewIterator ( rawdb . PreimagePrefix , nil )
defer it . Release ( )
preimages := make ( map [ common . Hash ] [ ] byte )
for it . Next ( ) {
hash := it . Key ( ) [ len ( rawdb . PreimagePrefix ) : ]
preimages [ common . BytesToHash ( hash ) ] = common . CopyBytes ( it . Value ( ) )
}
rawdb . WritePreimages ( dstDb , preimages )
}