Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
1657e439
Unverified
Commit
1657e439
authored
Jul 15, 2022
by
rjl493456442
Committed by
GitHub
Jul 15, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
core, les, eth: port snap sync changes (#24898)
core, eth, les, trie: rework snap sync
parent
1c9afc56
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
864 additions
and
509 deletions
+864
-509
statedb.go
core/state/statedb.go
+1
-1
sync.go
core/state/sync.go
+8
-8
sync_test.go
core/state/sync_test.go
+332
-154
skeleton_test.go
eth/downloader/skeleton_test.go
+1
-2
sort_test.go
eth/protocols/snap/sort_test.go
+9
-16
sync.go
eth/protocols/snap/sync.go
+46
-41
statesync.go
les/downloader/statesync.go
+46
-25
committer.go
trie/committer.go
+13
-11
sync.go
trie/sync.go
+178
-123
sync_test.go
trie/sync_test.go
+225
-123
trie.go
trie/trie.go
+4
-4
trie_test.go
trie/trie_test.go
+1
-1
No files found.
core/state/statedb.go
View file @
1657e439
...
@@ -940,7 +940,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
...
@@ -940,7 +940,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
// The onleaf func is called _serially_, so we can reuse the same account
// The onleaf func is called _serially_, so we can reuse the same account
// for unmarshalling every time.
// for unmarshalling every time.
var
account
types
.
StateAccount
var
account
types
.
StateAccount
root
,
accountCommitted
,
err
:=
s
.
trie
.
Commit
(
func
(
_
[][]
byte
,
_
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
)
error
{
root
,
accountCommitted
,
err
:=
s
.
trie
.
Commit
(
func
(
_
[][]
byte
,
_
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
,
_
[]
byte
)
error
{
if
err
:=
rlp
.
DecodeBytes
(
leaf
,
&
account
);
err
!=
nil
{
if
err
:=
rlp
.
DecodeBytes
(
leaf
,
&
account
);
err
!=
nil
{
return
nil
return
nil
}
}
...
...
core/state/sync.go
View file @
1657e439
...
@@ -27,20 +27,20 @@ import (
...
@@ -27,20 +27,20 @@ import (
)
)
// NewStateSync create a new state trie download scheduler.
// NewStateSync create a new state trie download scheduler.
func
NewStateSync
(
root
common
.
Hash
,
database
ethdb
.
KeyValueReader
,
onLeaf
func
(
path
s
[][]
byte
,
leaf
[]
byte
)
error
)
*
trie
.
Sync
{
func
NewStateSync
(
root
common
.
Hash
,
database
ethdb
.
KeyValueReader
,
onLeaf
func
(
key
s
[][]
byte
,
leaf
[]
byte
)
error
)
*
trie
.
Sync
{
// Register the storage slot callback if the external callback is specified.
// Register the storage slot callback if the external callback is specified.
var
onSlot
func
(
paths
[][]
byte
,
hexpath
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
)
error
var
onSlot
func
(
keys
[][]
byte
,
path
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
,
parentPath
[]
byte
)
error
if
onLeaf
!=
nil
{
if
onLeaf
!=
nil
{
onSlot
=
func
(
paths
[][]
byte
,
hexpath
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
)
error
{
onSlot
=
func
(
keys
[][]
byte
,
path
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
,
parentPath
[]
byte
)
error
{
return
onLeaf
(
path
s
,
leaf
)
return
onLeaf
(
key
s
,
leaf
)
}
}
}
}
// Register the account callback to connect the state trie and the storage
// Register the account callback to connect the state trie and the storage
// trie belongs to the contract.
// trie belongs to the contract.
var
syncer
*
trie
.
Sync
var
syncer
*
trie
.
Sync
onAccount
:=
func
(
paths
[][]
byte
,
hexpath
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
)
error
{
onAccount
:=
func
(
keys
[][]
byte
,
path
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
,
parentPath
[]
byte
)
error
{
if
onLeaf
!=
nil
{
if
onLeaf
!=
nil
{
if
err
:=
onLeaf
(
path
s
,
leaf
);
err
!=
nil
{
if
err
:=
onLeaf
(
key
s
,
leaf
);
err
!=
nil
{
return
err
return
err
}
}
}
}
...
@@ -48,8 +48,8 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(p
...
@@ -48,8 +48,8 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(p
if
err
:=
rlp
.
Decode
(
bytes
.
NewReader
(
leaf
),
&
obj
);
err
!=
nil
{
if
err
:=
rlp
.
Decode
(
bytes
.
NewReader
(
leaf
),
&
obj
);
err
!=
nil
{
return
err
return
err
}
}
syncer
.
AddSubTrie
(
obj
.
Root
,
hexpath
,
parent
,
onSlot
)
syncer
.
AddSubTrie
(
obj
.
Root
,
path
,
parent
,
parentPath
,
onSlot
)
syncer
.
AddCodeEntry
(
common
.
BytesToHash
(
obj
.
CodeHash
),
hexpath
,
parent
)
syncer
.
AddCodeEntry
(
common
.
BytesToHash
(
obj
.
CodeHash
),
path
,
parent
,
parentPath
)
return
nil
return
nil
}
}
syncer
=
trie
.
NewSync
(
root
,
database
,
onAccount
)
syncer
=
trie
.
NewSync
(
root
,
database
,
onAccount
)
...
...
core/state/sync_test.go
View file @
1657e439
...
@@ -134,8 +134,8 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
...
@@ -134,8 +134,8 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
func
TestEmptyStateSync
(
t
*
testing
.
T
)
{
func
TestEmptyStateSync
(
t
*
testing
.
T
)
{
empty
:=
common
.
HexToHash
(
"56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
)
empty
:=
common
.
HexToHash
(
"56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
)
sync
:=
NewStateSync
(
empty
,
rawdb
.
NewMemoryDatabase
(),
nil
)
sync
:=
NewStateSync
(
empty
,
rawdb
.
NewMemoryDatabase
(),
nil
)
if
nodes
,
paths
,
codes
:=
sync
.
Missing
(
1
);
len
(
nodes
)
!=
0
||
len
(
path
s
)
!=
0
||
len
(
codes
)
!=
0
{
if
paths
,
nodes
,
codes
:=
sync
.
Missing
(
1
);
len
(
paths
)
!=
0
||
len
(
node
s
)
!=
0
||
len
(
codes
)
!=
0
{
t
.
Errorf
(
"
content requested for empty state: %v, %v, %v"
,
nodes
,
paths
,
codes
)
t
.
Errorf
(
"content requested for empty state: %v, %v, %v"
,
nodes
,
paths
,
codes
)
}
}
}
}
...
@@ -160,6 +160,14 @@ func TestIterativeStateSyncBatchedByPath(t *testing.T) {
...
@@ -160,6 +160,14 @@ func TestIterativeStateSyncBatchedByPath(t *testing.T) {
testIterativeStateSync
(
t
,
100
,
false
,
true
)
testIterativeStateSync
(
t
,
100
,
false
,
true
)
}
}
// stateElement represents the element in the state trie(bytecode or trie node).
type
stateElement
struct
{
path
string
hash
common
.
Hash
code
common
.
Hash
syncPath
trie
.
SyncPath
}
func
testIterativeStateSync
(
t
*
testing
.
T
,
count
int
,
commit
bool
,
bypath
bool
)
{
func
testIterativeStateSync
(
t
*
testing
.
T
,
count
int
,
commit
bool
,
bypath
bool
)
{
// Create a random state to copy
// Create a random state to copy
srcDb
,
srcRoot
,
srcAccounts
:=
makeTestState
()
srcDb
,
srcRoot
,
srcAccounts
:=
makeTestState
()
...
@@ -172,54 +180,73 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
...
@@ -172,54 +180,73 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
dstDb
:=
rawdb
.
NewMemoryDatabase
()
dstDb
:=
rawdb
.
NewMemoryDatabase
()
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
nodes
,
paths
,
codes
:=
sched
.
Missing
(
count
)
var
(
var
(
hashQueue
[]
common
.
Hash
nodeElements
[]
stateElement
pathQueue
[]
trie
.
SyncPath
codeElements
[]
stateElement
)
)
if
!
bypath
{
paths
,
nodes
,
codes
:=
sched
.
Missing
(
count
)
hashQueue
=
append
(
append
(
hashQueue
[
:
0
],
nodes
...
),
codes
...
)
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
}
else
{
nodeElements
=
append
(
nodeElements
,
stateElement
{
hashQueue
=
append
(
hashQueue
[
:
0
],
codes
...
)
path
:
paths
[
i
],
pathQueue
=
append
(
pathQueue
[
:
0
],
paths
...
)
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
}
for
len
(
hashQueue
)
+
len
(
pathQueue
)
>
0
{
for
i
:=
0
;
i
<
len
(
codes
);
i
++
{
results
:=
make
([]
trie
.
SyncResult
,
len
(
hashQueue
)
+
len
(
pathQueue
))
codeElements
=
append
(
codeElements
,
stateElement
{
for
i
,
hash
:=
range
hashQueue
{
code
:
codes
[
i
],
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
hash
)
})
if
err
!=
nil
{
}
data
,
err
=
srcDb
.
ContractCode
(
common
.
Hash
{},
hash
)
for
len
(
nodeElements
)
+
len
(
codeElements
)
>
0
{
}
var
(
nodeResults
=
make
([]
trie
.
NodeSyncResult
,
len
(
nodeElements
))
codeResults
=
make
([]
trie
.
CodeSyncResult
,
len
(
codeElements
))
)
for
i
,
element
:=
range
codeElements
{
data
,
err
:=
srcDb
.
ContractCode
(
common
.
Hash
{},
element
.
code
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve
node data for hash %x"
,
hash
)
t
.
Fatalf
(
"failed to retrieve
contract bytecode for hash %x"
,
element
.
code
)
}
}
results
[
i
]
=
trie
.
SyncResult
{
Hash
:
hash
,
Data
:
data
}
codeResults
[
i
]
=
trie
.
CodeSyncResult
{
Hash
:
element
.
code
,
Data
:
data
}
}
}
for
i
,
path
:=
range
pathQueue
{
for
i
,
node
:=
range
nodeElements
{
if
len
(
path
)
==
1
{
if
bypath
{
data
,
_
,
err
:=
srcTrie
.
TryGetNode
(
path
[
0
])
if
len
(
node
.
syncPath
)
==
1
{
if
err
!=
nil
{
data
,
_
,
err
:=
srcTrie
.
TryGetNode
(
node
.
syncPath
[
0
])
t
.
Fatalf
(
"failed to retrieve node data for path %x: %v"
,
path
,
err
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for path %x: %v"
,
node
.
syncPath
[
0
],
err
)
}
nodeResults
[
i
]
=
trie
.
NodeSyncResult
{
Path
:
node
.
path
,
Data
:
data
}
}
else
{
var
acc
types
.
StateAccount
if
err
:=
rlp
.
DecodeBytes
(
srcTrie
.
Get
(
node
.
syncPath
[
0
]),
&
acc
);
err
!=
nil
{
t
.
Fatalf
(
"failed to decode account on path %x: %v"
,
node
.
syncPath
[
0
],
err
)
}
stTrie
,
err
:=
trie
.
New
(
common
.
BytesToHash
(
node
.
syncPath
[
0
]),
acc
.
Root
,
srcDb
.
TrieDB
())
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retriev storage trie for path %x: %v"
,
node
.
syncPath
[
1
],
err
)
}
data
,
_
,
err
:=
stTrie
.
TryGetNode
(
node
.
syncPath
[
1
])
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for path %x: %v"
,
node
.
syncPath
[
1
],
err
)
}
nodeResults
[
i
]
=
trie
.
NodeSyncResult
{
Path
:
node
.
path
,
Data
:
data
}
}
}
results
[
len
(
hashQueue
)
+
i
]
=
trie
.
SyncResult
{
Hash
:
crypto
.
Keccak256Hash
(
data
),
Data
:
data
}
}
else
{
}
else
{
var
acc
types
.
StateAccount
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
node
.
hash
)
if
err
:=
rlp
.
DecodeBytes
(
srcTrie
.
Get
(
path
[
0
]),
&
acc
);
err
!=
nil
{
t
.
Fatalf
(
"failed to decode account on path %x: %v"
,
path
,
err
)
}
stTrie
,
err
:=
trie
.
New
(
common
.
BytesToHash
(
path
[
0
]),
acc
.
Root
,
srcDb
.
TrieDB
())
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retriev
storage trie for path %x: %v"
,
path
,
err
)
t
.
Fatalf
(
"failed to retriev
e node data for key %v"
,
[]
byte
(
node
.
path
)
)
}
}
data
,
_
,
err
:=
stTrie
.
TryGetNode
(
path
[
1
])
nodeResults
[
i
]
=
trie
.
NodeSyncResult
{
Path
:
node
.
path
,
Data
:
data
}
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for path %x: %v"
,
path
,
err
)
}
results
[
len
(
hashQueue
)
+
i
]
=
trie
.
SyncResult
{
Hash
:
crypto
.
Keccak256Hash
(
data
),
Data
:
data
}
}
}
}
}
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
codeResults
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
if
err
:=
sched
.
ProcessCode
(
result
);
err
!=
nil
{
t
.
Errorf
(
"failed to process result %v"
,
err
)
}
}
for
_
,
result
:=
range
nodeResults
{
if
err
:=
sched
.
ProcessNode
(
result
);
err
!=
nil
{
t
.
Errorf
(
"failed to process result %v"
,
err
)
t
.
Errorf
(
"failed to process result %v"
,
err
)
}
}
}
}
...
@@ -229,12 +256,20 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
...
@@ -229,12 +256,20 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
}
}
batch
.
Write
()
batch
.
Write
()
nodes
,
paths
,
codes
=
sched
.
Missing
(
count
)
paths
,
nodes
,
codes
=
sched
.
Missing
(
count
)
if
!
bypath
{
nodeElements
=
nodeElements
[
:
0
]
hashQueue
=
append
(
append
(
hashQueue
[
:
0
],
nodes
...
),
codes
...
)
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
}
else
{
nodeElements
=
append
(
nodeElements
,
stateElement
{
hashQueue
=
append
(
hashQueue
[
:
0
],
codes
...
)
path
:
paths
[
i
],
pathQueue
=
append
(
pathQueue
[
:
0
],
paths
...
)
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
codeElements
=
codeElements
[
:
0
]
for
i
:=
0
;
i
<
len
(
codes
);
i
++
{
codeElements
=
append
(
codeElements
,
stateElement
{
code
:
codes
[
i
],
})
}
}
}
}
// Cross check that the two states are in sync
// Cross check that the two states are in sync
...
@@ -251,26 +286,58 @@ func TestIterativeDelayedStateSync(t *testing.T) {
...
@@ -251,26 +286,58 @@ func TestIterativeDelayedStateSync(t *testing.T) {
dstDb
:=
rawdb
.
NewMemoryDatabase
()
dstDb
:=
rawdb
.
NewMemoryDatabase
()
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
nodes
,
_
,
codes
:=
sched
.
Missing
(
0
)
var
(
queue
:=
append
(
append
([]
common
.
Hash
{},
nodes
...
),
codes
...
)
nodeElements
[]
stateElement
codeElements
[]
stateElement
for
len
(
queue
)
>
0
{
)
paths
,
nodes
,
codes
:=
sched
.
Missing
(
0
)
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
nodeElements
=
append
(
nodeElements
,
stateElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
for
i
:=
0
;
i
<
len
(
codes
);
i
++
{
codeElements
=
append
(
codeElements
,
stateElement
{
code
:
codes
[
i
],
})
}
for
len
(
nodeElements
)
+
len
(
codeElements
)
>
0
{
// Sync only half of the scheduled nodes
// Sync only half of the scheduled nodes
results
:=
make
([]
trie
.
SyncResult
,
len
(
queue
)
/
2
+
1
)
var
nodeProcessd
int
for
i
,
hash
:=
range
queue
[
:
len
(
results
)]
{
var
codeProcessd
int
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
hash
)
if
len
(
codeElements
)
>
0
{
if
err
!=
nil
{
codeResults
:=
make
([]
trie
.
CodeSyncResult
,
len
(
codeElements
)
/
2
+
1
)
data
,
err
=
srcDb
.
ContractCode
(
common
.
Hash
{},
hash
)
for
i
,
element
:=
range
codeElements
[
:
len
(
codeResults
)]
{
data
,
err
:=
srcDb
.
ContractCode
(
common
.
Hash
{},
element
.
code
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve contract bytecode for %x"
,
element
.
code
)
}
codeResults
[
i
]
=
trie
.
CodeSyncResult
{
Hash
:
element
.
code
,
Data
:
data
}
}
}
if
err
!=
nil
{
for
_
,
result
:=
range
codeResults
{
t
.
Fatalf
(
"failed to retrieve node data for %x"
,
hash
)
if
err
:=
sched
.
ProcessCode
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
results
[
i
]
=
trie
.
SyncResult
{
Hash
:
hash
,
Data
:
data
}
codeProcessd
=
len
(
codeResults
)
}
}
for
_
,
result
:=
range
results
{
if
len
(
nodeElements
)
>
0
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
nodeResults
:=
make
([]
trie
.
NodeSyncResult
,
len
(
nodeElements
)
/
2
+
1
)
t
.
Fatalf
(
"failed to process result %v"
,
err
)
for
i
,
element
:=
range
nodeElements
[
:
len
(
nodeResults
)]
{
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
element
.
hash
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve contract bytecode for %x"
,
element
.
code
)
}
nodeResults
[
i
]
=
trie
.
NodeSyncResult
{
Path
:
element
.
path
,
Data
:
data
}
}
for
_
,
result
:=
range
nodeResults
{
if
err
:=
sched
.
ProcessNode
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
nodeProcessd
=
len
(
nodeResults
)
}
}
batch
:=
dstDb
.
NewBatch
()
batch
:=
dstDb
.
NewBatch
()
if
err
:=
sched
.
Commit
(
batch
);
err
!=
nil
{
if
err
:=
sched
.
Commit
(
batch
);
err
!=
nil
{
...
@@ -278,8 +345,21 @@ func TestIterativeDelayedStateSync(t *testing.T) {
...
@@ -278,8 +345,21 @@ func TestIterativeDelayedStateSync(t *testing.T) {
}
}
batch
.
Write
()
batch
.
Write
()
nodes
,
_
,
codes
=
sched
.
Missing
(
0
)
paths
,
nodes
,
codes
=
sched
.
Missing
(
0
)
queue
=
append
(
append
(
queue
[
len
(
results
)
:
],
nodes
...
),
codes
...
)
nodeElements
=
nodeElements
[
nodeProcessd
:
]
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
nodeElements
=
append
(
nodeElements
,
stateElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
codeElements
=
codeElements
[
codeProcessd
:
]
for
i
:=
0
;
i
<
len
(
codes
);
i
++
{
codeElements
=
append
(
codeElements
,
stateElement
{
code
:
codes
[
i
],
})
}
}
}
// Cross check that the two states are in sync
// Cross check that the two states are in sync
checkStateAccounts
(
t
,
dstDb
,
srcRoot
,
srcAccounts
)
checkStateAccounts
(
t
,
dstDb
,
srcRoot
,
srcAccounts
)
...
@@ -299,40 +379,70 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
...
@@ -299,40 +379,70 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
dstDb
:=
rawdb
.
NewMemoryDatabase
()
dstDb
:=
rawdb
.
NewMemoryDatabase
()
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
queue
:=
make
(
map
[
common
.
Hash
]
struct
{})
nodeQueue
:=
make
(
map
[
string
]
stateElement
)
nodes
,
_
,
codes
:=
sched
.
Missing
(
count
)
codeQueue
:=
make
(
map
[
common
.
Hash
]
struct
{})
for
_
,
hash
:=
range
append
(
nodes
,
codes
...
)
{
paths
,
nodes
,
codes
:=
sched
.
Missing
(
count
)
queue
[
hash
]
=
struct
{}{}
for
i
,
path
:=
range
paths
{
nodeQueue
[
path
]
=
stateElement
{
path
:
path
,
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
path
)),
}
}
}
for
len
(
queue
)
>
0
{
for
_
,
hash
:=
range
codes
{
codeQueue
[
hash
]
=
struct
{}{}
}
for
len
(
nodeQueue
)
+
len
(
codeQueue
)
>
0
{
// Fetch all the queued nodes in a random order
// Fetch all the queued nodes in a random order
results
:=
make
([]
trie
.
SyncResult
,
0
,
len
(
queue
))
if
len
(
codeQueue
)
>
0
{
for
hash
:=
range
queue
{
results
:=
make
([]
trie
.
CodeSyncResult
,
0
,
len
(
codeQueue
))
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
hash
)
for
hash
:=
range
codeQueue
{
if
err
!=
nil
{
data
,
err
:=
srcDb
.
ContractCode
(
common
.
Hash
{},
hash
)
data
,
err
=
srcDb
.
ContractCode
(
common
.
Hash
{},
hash
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x"
,
hash
)
}
results
=
append
(
results
,
trie
.
CodeSyncResult
{
Hash
:
hash
,
Data
:
data
})
}
}
if
err
!=
nil
{
for
_
,
result
:=
range
results
{
t
.
Fatalf
(
"failed to retrieve node data for %x"
,
hash
)
if
err
:=
sched
.
ProcessCode
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
results
=
append
(
results
,
trie
.
SyncResult
{
Hash
:
hash
,
Data
:
data
})
}
}
// Feed the retrieved results back and queue new tasks
if
len
(
nodeQueue
)
>
0
{
for
_
,
result
:=
range
results
{
results
:=
make
([]
trie
.
NodeSyncResult
,
0
,
len
(
nodeQueue
))
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
for
path
,
element
:=
range
nodeQueue
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
element
.
hash
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x %v %v"
,
element
.
hash
,
[]
byte
(
element
.
path
),
element
.
path
)
}
results
=
append
(
results
,
trie
.
NodeSyncResult
{
Path
:
path
,
Data
:
data
})
}
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
ProcessNode
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
}
// Feed the retrieved results back and queue new tasks
batch
:=
dstDb
.
NewBatch
()
batch
:=
dstDb
.
NewBatch
()
if
err
:=
sched
.
Commit
(
batch
);
err
!=
nil
{
if
err
:=
sched
.
Commit
(
batch
);
err
!=
nil
{
t
.
Fatalf
(
"failed to commit data: %v"
,
err
)
t
.
Fatalf
(
"failed to commit data: %v"
,
err
)
}
}
batch
.
Write
()
batch
.
Write
()
queue
=
make
(
map
[
common
.
Hash
]
struct
{})
nodeQueue
=
make
(
map
[
string
]
stateElement
)
nodes
,
_
,
codes
=
sched
.
Missing
(
count
)
codeQueue
=
make
(
map
[
common
.
Hash
]
struct
{})
for
_
,
hash
:=
range
append
(
nodes
,
codes
...
)
{
paths
,
nodes
,
codes
:=
sched
.
Missing
(
count
)
queue
[
hash
]
=
struct
{}{}
for
i
,
path
:=
range
paths
{
nodeQueue
[
path
]
=
stateElement
{
path
:
path
,
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
path
)),
}
}
for
_
,
hash
:=
range
codes
{
codeQueue
[
hash
]
=
struct
{}{}
}
}
}
}
// Cross check that the two states are in sync
// Cross check that the two states are in sync
...
@@ -349,34 +459,62 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
...
@@ -349,34 +459,62 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
dstDb
:=
rawdb
.
NewMemoryDatabase
()
dstDb
:=
rawdb
.
NewMemoryDatabase
()
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
queue
:=
make
(
map
[
common
.
Hash
]
struct
{})
nodeQueue
:=
make
(
map
[
string
]
stateElement
)
nodes
,
_
,
codes
:=
sched
.
Missing
(
0
)
codeQueue
:=
make
(
map
[
common
.
Hash
]
struct
{})
for
_
,
hash
:=
range
append
(
nodes
,
codes
...
)
{
paths
,
nodes
,
codes
:=
sched
.
Missing
(
0
)
queue
[
hash
]
=
struct
{}{}
for
i
,
path
:=
range
paths
{
nodeQueue
[
path
]
=
stateElement
{
path
:
path
,
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
path
)),
}
}
for
_
,
hash
:=
range
codes
{
codeQueue
[
hash
]
=
struct
{}{}
}
}
for
len
(
q
ueue
)
>
0
{
for
len
(
nodeQueue
)
+
len
(
codeQ
ueue
)
>
0
{
// Sync only half of the scheduled nodes, even those in random order
// Sync only half of the scheduled nodes, even those in random order
results
:=
make
([]
trie
.
SyncResult
,
0
,
len
(
queue
)
/
2
+
1
)
if
len
(
codeQueue
)
>
0
{
for
hash
:=
range
queue
{
results
:=
make
([]
trie
.
CodeSyncResult
,
0
,
len
(
codeQueue
)
/
2
+
1
)
delete
(
queue
,
hash
)
for
hash
:=
range
codeQueue
{
delete
(
codeQueue
,
hash
)
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
hash
)
data
,
err
:=
srcDb
.
ContractCode
(
common
.
Hash
{},
hash
)
if
err
!=
nil
{
if
err
!=
nil
{
data
,
err
=
srcDb
.
ContractCode
(
common
.
Hash
{},
hash
)
t
.
Fatalf
(
"failed to retrieve node data for %x"
,
hash
)
}
results
=
append
(
results
,
trie
.
CodeSyncResult
{
Hash
:
hash
,
Data
:
data
})
if
len
(
results
)
>=
cap
(
results
)
{
break
}
}
}
if
err
!=
nil
{
for
_
,
result
:=
range
results
{
t
.
Fatalf
(
"failed to retrieve node data for %x"
,
hash
)
if
err
:=
sched
.
ProcessCode
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
results
=
append
(
results
,
trie
.
SyncResult
{
Hash
:
hash
,
Data
:
data
})
}
if
len
(
nodeQueue
)
>
0
{
results
:=
make
([]
trie
.
NodeSyncResult
,
0
,
len
(
nodeQueue
)
/
2
+
1
)
for
path
,
element
:=
range
nodeQueue
{
delete
(
nodeQueue
,
path
)
if
len
(
results
)
>=
cap
(
results
)
{
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
element
.
hash
)
break
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x"
,
element
.
hash
)
}
results
=
append
(
results
,
trie
.
NodeSyncResult
{
Path
:
path
,
Data
:
data
})
if
len
(
results
)
>=
cap
(
results
)
{
break
}
}
}
}
// Feed the retrieved results back and queue new tasks
// Feed the retrieved results back and queue new tasks
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
ProcessNode
(
result
);
err
!=
nil
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
}
batch
:=
dstDb
.
NewBatch
()
batch
:=
dstDb
.
NewBatch
()
...
@@ -384,12 +522,17 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
...
@@ -384,12 +522,17 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
t
.
Fatalf
(
"failed to commit data: %v"
,
err
)
t
.
Fatalf
(
"failed to commit data: %v"
,
err
)
}
}
batch
.
Write
()
batch
.
Write
()
for
_
,
result
:=
range
results
{
delete
(
queue
,
result
.
Hash
)
paths
,
nodes
,
codes
:=
sched
.
Missing
(
0
)
for
i
,
path
:=
range
paths
{
nodeQueue
[
path
]
=
stateElement
{
path
:
path
,
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
path
)),
}
}
}
nodes
,
_
,
codes
=
sched
.
Missing
(
0
)
for
_
,
hash
:=
range
codes
{
for
_
,
hash
:=
range
append
(
nodes
,
codes
...
)
{
codeQueue
[
hash
]
=
struct
{}{}
queue
[
hash
]
=
struct
{}{}
}
}
}
}
// Cross check that the two states are in sync
// Cross check that the two states are in sync
...
@@ -416,28 +559,62 @@ func TestIncompleteStateSync(t *testing.T) {
...
@@ -416,28 +559,62 @@ func TestIncompleteStateSync(t *testing.T) {
dstDb
:=
rawdb
.
NewMemoryDatabase
()
dstDb
:=
rawdb
.
NewMemoryDatabase
()
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
sched
:=
NewStateSync
(
srcRoot
,
dstDb
,
nil
)
var
added
[]
common
.
Hash
var
(
addedCodes
[]
common
.
Hash
nodes
,
_
,
codes
:=
sched
.
Missing
(
1
)
addedNodes
[]
common
.
Hash
queue
:=
append
(
append
([]
common
.
Hash
{},
nodes
...
),
codes
...
)
)
nodeQueue
:=
make
(
map
[
string
]
stateElement
)
for
len
(
queue
)
>
0
{
codeQueue
:=
make
(
map
[
common
.
Hash
]
struct
{})
paths
,
nodes
,
codes
:=
sched
.
Missing
(
1
)
for
i
,
path
:=
range
paths
{
nodeQueue
[
path
]
=
stateElement
{
path
:
path
,
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
path
)),
}
}
for
_
,
hash
:=
range
codes
{
codeQueue
[
hash
]
=
struct
{}{}
}
for
len
(
nodeQueue
)
+
len
(
codeQueue
)
>
0
{
// Fetch a batch of state nodes
// Fetch a batch of state nodes
results
:=
make
([]
trie
.
SyncResult
,
len
(
queue
))
if
len
(
codeQueue
)
>
0
{
for
i
,
hash
:=
range
queue
{
results
:=
make
([]
trie
.
CodeSyncResult
,
0
,
len
(
codeQueue
))
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
hash
)
for
hash
:=
range
codeQueue
{
if
err
!=
nil
{
data
,
err
:=
srcDb
.
ContractCode
(
common
.
Hash
{},
hash
)
data
,
err
=
srcDb
.
ContractCode
(
common
.
Hash
{},
hash
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x"
,
hash
)
}
results
=
append
(
results
,
trie
.
CodeSyncResult
{
Hash
:
hash
,
Data
:
data
})
addedCodes
=
append
(
addedCodes
,
hash
)
}
}
if
err
!=
nil
{
// Process each of the state nodes
t
.
Fatalf
(
"failed to retrieve node data for %x"
,
hash
)
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
ProcessCode
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
results
[
i
]
=
trie
.
SyncResult
{
Hash
:
hash
,
Data
:
data
}
}
}
// Process each of the state nodes
var
nodehashes
[]
common
.
Hash
for
_
,
result
:=
range
results
{
if
len
(
nodeQueue
)
>
0
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
results
:=
make
([]
trie
.
NodeSyncResult
,
0
,
len
(
nodeQueue
))
t
.
Fatalf
(
"failed to process result %v"
,
err
)
for
key
,
element
:=
range
nodeQueue
{
data
,
err
:=
srcDb
.
TrieDB
()
.
Node
(
element
.
hash
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x"
,
element
.
hash
)
}
results
=
append
(
results
,
trie
.
NodeSyncResult
{
Path
:
key
,
Data
:
data
})
if
element
.
hash
!=
srcRoot
{
addedNodes
=
append
(
addedNodes
,
element
.
hash
)
}
nodehashes
=
append
(
nodehashes
,
element
.
hash
)
}
// Process each of the state nodes
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
ProcessNode
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
}
batch
:=
dstDb
.
NewBatch
()
batch
:=
dstDb
.
NewBatch
()
...
@@ -445,43 +622,44 @@ func TestIncompleteStateSync(t *testing.T) {
...
@@ -445,43 +622,44 @@ func TestIncompleteStateSync(t *testing.T) {
t
.
Fatalf
(
"failed to commit data: %v"
,
err
)
t
.
Fatalf
(
"failed to commit data: %v"
,
err
)
}
}
batch
.
Write
()
batch
.
Write
()
for
_
,
result
:=
range
results
{
added
=
append
(
added
,
result
.
Hash
)
for
_
,
root
:=
range
nodehashes
{
// Check that all known sub-tries added so far are complete or missing entirely.
if
_
,
ok
:=
isCode
[
result
.
Hash
];
ok
{
continue
}
// Can't use checkStateConsistency here because subtrie keys may have odd
// Can't use checkStateConsistency here because subtrie keys may have odd
// length and crash in LeafKey.
// length and crash in LeafKey.
if
err
:=
checkTrieConsistency
(
dstDb
,
r
esult
.
Hash
);
err
!=
nil
{
if
err
:=
checkTrieConsistency
(
dstDb
,
r
oot
);
err
!=
nil
{
t
.
Fatalf
(
"state inconsistent: %v"
,
err
)
t
.
Fatalf
(
"state inconsistent: %v"
,
err
)
}
}
}
}
// Fetch the next batch to retrieve
// Fetch the next batch to retrieve
nodes
,
_
,
codes
=
sched
.
Missing
(
1
)
nodeQueue
=
make
(
map
[
string
]
stateElement
)
queue
=
append
(
append
(
queue
[
:
0
],
nodes
...
),
codes
...
)
codeQueue
=
make
(
map
[
common
.
Hash
]
struct
{})
paths
,
nodes
,
codes
:=
sched
.
Missing
(
1
)
for
i
,
path
:=
range
paths
{
nodeQueue
[
path
]
=
stateElement
{
path
:
path
,
hash
:
nodes
[
i
],
syncPath
:
trie
.
NewSyncPath
([]
byte
(
path
)),
}
}
for
_
,
hash
:=
range
codes
{
codeQueue
[
hash
]
=
struct
{}{}
}
}
}
// Sanity check that removing any node from the database is detected
// Sanity check that removing any node from the database is detected
for
_
,
node
:=
range
added
[
1
:
]
{
for
_
,
node
:=
range
addedCodes
{
var
(
val
:=
rawdb
.
ReadCode
(
dstDb
,
node
)
key
=
node
.
Bytes
()
rawdb
.
DeleteCode
(
dstDb
,
node
)
_
,
code
=
isCode
[
node
]
if
err
:=
checkStateConsistency
(
dstDb
,
srcRoot
);
err
==
nil
{
val
[]
byte
t
.
Errorf
(
"trie inconsistency not caught, missing: %x"
,
node
)
)
if
code
{
val
=
rawdb
.
ReadCode
(
dstDb
,
node
)
rawdb
.
DeleteCode
(
dstDb
,
node
)
}
else
{
val
=
rawdb
.
ReadTrieNode
(
dstDb
,
node
)
rawdb
.
DeleteTrieNode
(
dstDb
,
node
)
}
}
if
err
:=
checkStateConsistency
(
dstDb
,
added
[
0
]);
err
==
nil
{
rawdb
.
WriteCode
(
dstDb
,
node
,
val
)
t
.
Fatalf
(
"trie inconsistency not caught, missing: %x"
,
key
)
}
}
for
_
,
node
:=
range
addedNodes
{
if
code
{
val
:=
rawdb
.
ReadTrieNode
(
dstDb
,
node
)
rawdb
.
WriteCode
(
dstDb
,
node
,
val
)
rawdb
.
DeleteTrieNode
(
dstDb
,
node
)
}
else
{
if
err
:=
checkStateConsistency
(
dstDb
,
srcRoot
);
err
==
nil
{
rawdb
.
WriteTrieNode
(
dstDb
,
node
,
val
)
t
.
Errorf
(
"trie inconsistency not caught, missing: %v"
,
node
.
Hex
()
)
}
}
rawdb
.
WriteTrieNode
(
dstDb
,
node
,
val
)
}
}
}
}
eth/downloader/skeleton_test.go
View file @
1657e439
...
@@ -21,7 +21,6 @@ import (
...
@@ -21,7 +21,6 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"math/big"
"math/big"
"os"
"sync/atomic"
"sync/atomic"
"testing"
"testing"
"time"
"time"
...
@@ -515,7 +514,7 @@ func TestSkeletonSyncExtend(t *testing.T) {
...
@@ -515,7 +514,7 @@ func TestSkeletonSyncExtend(t *testing.T) {
// Tests that the skeleton sync correctly retrieves headers from one or more
// Tests that the skeleton sync correctly retrieves headers from one or more
// peers without duplicates or other strange side effects.
// peers without duplicates or other strange side effects.
func
TestSkeletonSyncRetrievals
(
t
*
testing
.
T
)
{
func
TestSkeletonSyncRetrievals
(
t
*
testing
.
T
)
{
log
.
Root
()
.
SetHandler
(
log
.
LvlFilterHandler
(
log
.
LvlTrace
,
log
.
StreamHandler
(
os
.
Stderr
,
log
.
TerminalFormat
(
true
))))
//
log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
// Since skeleton headers don't need to be meaningful, beyond a parent hash
// Since skeleton headers don't need to be meaningful, beyond a parent hash
// progression, create a long fake chain to test with.
// progression, create a long fake chain to test with.
...
...
eth/protocols/snap/sort_test.go
View file @
1657e439
...
@@ -22,7 +22,6 @@ import (
...
@@ -22,7 +22,6 @@ import (
"testing"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/trie"
)
)
func
hexToNibbles
(
s
string
)
[]
byte
{
func
hexToNibbles
(
s
string
)
[]
byte
{
...
@@ -38,22 +37,17 @@ func hexToNibbles(s string) []byte {
...
@@ -38,22 +37,17 @@ func hexToNibbles(s string) []byte {
}
}
func
TestRequestSorting
(
t
*
testing
.
T
)
{
func
TestRequestSorting
(
t
*
testing
.
T
)
{
// - Path 0x9 -> {0x19}
// - Path 0x9 -> {0x19}
// - Path 0x99 -> {0x0099}
// - Path 0x99 -> {0x0099}
// - Path 0x01234567890123456789012345678901012345678901234567890123456789019 -> {0x0123456789012345678901234567890101234567890123456789012345678901, 0x19}
// - Path 0x01234567890123456789012345678901012345678901234567890123456789019 -> {0x0123456789012345678901234567890101234567890123456789012345678901, 0x19}
// - Path 0x012345678901234567890123456789010123456789012345678901234567890199 -> {0x0123456789012345678901234567890101234567890123456789012345678901, 0x0099}
// - Path 0x012345678901234567890123456789010123456789012345678901234567890199 -> {0x0123456789012345678901234567890101234567890123456789012345678901, 0x0099}
var
f
=
func
(
path
string
)
(
trie
.
SyncPath
,
TrieNodePathSet
,
common
.
Hash
)
{
var
f
=
func
(
path
string
)
string
{
data
:=
hexToNibbles
(
path
)
data
:=
hexToNibbles
(
path
)
sp
:=
trie
.
NewSyncPath
(
data
)
return
string
(
data
)
tnps
:=
TrieNodePathSet
([][]
byte
(
sp
))
hash
:=
common
.
Hash
{}
return
sp
,
tnps
,
hash
}
}
var
(
var
(
hashes
[]
common
.
Hash
hashes
[]
common
.
Hash
paths
[]
trie
.
SyncPath
paths
[]
string
pathsets
[]
TrieNodePathSet
)
)
for
_
,
x
:=
range
[]
string
{
for
_
,
x
:=
range
[]
string
{
"0x9"
,
"0x9"
,
...
@@ -67,15 +61,14 @@ func TestRequestSorting(t *testing.T) {
...
@@ -67,15 +61,14 @@ func TestRequestSorting(t *testing.T) {
"0x01234567890123456789012345678901012345678901234567890123456789010"
,
"0x01234567890123456789012345678901012345678901234567890123456789010"
,
"0x01234567890123456789012345678901012345678901234567890123456789011"
,
"0x01234567890123456789012345678901012345678901234567890123456789011"
,
}
{
}
{
sp
,
_
,
hash
:=
f
(
x
)
paths
=
append
(
paths
,
f
(
x
))
hashes
=
append
(
hashes
,
hash
)
hashes
=
append
(
hashes
,
common
.
Hash
{})
paths
=
append
(
paths
,
sp
)
}
}
_
,
paths
,
pathsets
=
sortByAccountPath
(
hashes
,
path
s
)
_
,
_
,
syncPaths
,
pathsets
:=
sortByAccountPath
(
paths
,
hashe
s
)
{
{
var
b
=
new
(
bytes
.
Buffer
)
var
b
=
new
(
bytes
.
Buffer
)
for
i
:=
0
;
i
<
len
(
p
aths
);
i
++
{
for
i
:=
0
;
i
<
len
(
syncP
aths
);
i
++
{
fmt
.
Fprintf
(
b
,
"
\n
%d. paths %x"
,
i
,
p
aths
[
i
])
fmt
.
Fprintf
(
b
,
"
\n
%d. paths %x"
,
i
,
syncP
aths
[
i
])
}
}
want
:=
`
want
:=
`
0. paths [0099]
0. paths [0099]
...
...
eth/protocols/snap/sync.go
View file @
1657e439
...
@@ -230,8 +230,8 @@ type trienodeHealRequest struct {
...
@@ -230,8 +230,8 @@ type trienodeHealRequest struct {
timeout
*
time
.
Timer
// Timer to track delivery timeout
timeout
*
time
.
Timer
// Timer to track delivery timeout
stale
chan
struct
{}
// Channel to signal the request was dropped
stale
chan
struct
{}
// Channel to signal the request was dropped
hashes
[]
common
.
Hash
// Trie node hashes to validate responses
paths
[]
string
// Trie node paths for identifying trie node
paths
[]
trie
.
SyncPath
// Trie node paths requested for rescheduling
hashes
[]
common
.
Hash
// Trie node hashes to validate responses
task
*
healTask
// Task which this request is filling (only access fields through the runloop!!)
task
*
healTask
// Task which this request is filling (only access fields through the runloop!!)
}
}
...
@@ -240,9 +240,9 @@ type trienodeHealRequest struct {
...
@@ -240,9 +240,9 @@ type trienodeHealRequest struct {
type
trienodeHealResponse
struct
{
type
trienodeHealResponse
struct
{
task
*
healTask
// Task which this request is filling
task
*
healTask
// Task which this request is filling
hashes
[]
common
.
Hash
// Hashes of the trie nodes to avoid double hashing
paths
[]
string
// Paths of the trie nodes
paths
[]
trie
.
SyncPath
// Trie node paths requested for rescheduling missing ones
hashes
[]
common
.
Hash
// Hashes of the trie nodes to avoid double hashing
nodes
[][]
byte
// Actual trie nodes to store into the database (nil = missing)
nodes
[][]
byte
// Actual trie nodes to store into the database (nil = missing)
}
}
// bytecodeHealRequest tracks a pending bytecode request to ensure responses are to
// bytecodeHealRequest tracks a pending bytecode request to ensure responses are to
...
@@ -321,8 +321,8 @@ type storageTask struct {
...
@@ -321,8 +321,8 @@ type storageTask struct {
type
healTask
struct
{
type
healTask
struct
{
scheduler
*
trie
.
Sync
// State trie sync scheduler defining the tasks
scheduler
*
trie
.
Sync
// State trie sync scheduler defining the tasks
trieTasks
map
[
common
.
Hash
]
trie
.
SyncPath
// Set of trie node tasks currently queued for retrieval
trieTasks
map
[
string
]
common
.
Hash
// Set of trie node tasks currently queued for retrieval, indexed by node path
codeTasks
map
[
common
.
Hash
]
struct
{}
// Set of byte code tasks currently queued for retrieval
codeTasks
map
[
common
.
Hash
]
struct
{}
// Set of byte code tasks currently queued for retrieval, indexed by code hash
}
}
// SyncProgress is a database entry to allow suspending and resuming a snapshot state
// SyncProgress is a database entry to allow suspending and resuming a snapshot state
...
@@ -540,7 +540,7 @@ func (s *Syncer) Unregister(id string) error {
...
@@ -540,7 +540,7 @@ func (s *Syncer) Unregister(id string) error {
return
nil
return
nil
}
}
// Sync starts (or resumes a previous) sync cycle to iterate over a
n
state trie
// Sync starts (or resumes a previous) sync cycle to iterate over a state trie
// with the given root and reconstruct the nodes based on the snapshot leaves.
// with the given root and reconstruct the nodes based on the snapshot leaves.
// Previously downloaded segments will not be redownloaded of fixed, rather any
// Previously downloaded segments will not be redownloaded of fixed, rather any
// errors will be healed after the leaves are fully accumulated.
// errors will be healed after the leaves are fully accumulated.
...
@@ -551,7 +551,7 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error {
...
@@ -551,7 +551,7 @@ func (s *Syncer) Sync(root common.Hash, cancel chan struct{}) error {
s
.
root
=
root
s
.
root
=
root
s
.
healer
=
&
healTask
{
s
.
healer
=
&
healTask
{
scheduler
:
state
.
NewStateSync
(
root
,
s
.
db
,
s
.
onHealState
),
scheduler
:
state
.
NewStateSync
(
root
,
s
.
db
,
s
.
onHealState
),
trieTasks
:
make
(
map
[
common
.
Hash
]
trie
.
SyncPat
h
),
trieTasks
:
make
(
map
[
string
]
common
.
Has
h
),
codeTasks
:
make
(
map
[
common
.
Hash
]
struct
{}),
codeTasks
:
make
(
map
[
common
.
Hash
]
struct
{}),
}
}
s
.
statelessPeers
=
make
(
map
[
string
]
struct
{})
s
.
statelessPeers
=
make
(
map
[
string
]
struct
{})
...
@@ -743,7 +743,7 @@ func (s *Syncer) loadSyncStatus() {
...
@@ -743,7 +743,7 @@ func (s *Syncer) loadSyncStatus() {
return
return
}
}
}
}
// Either we've failed to decode the previus state, or there was none.
// Either we've failed to decode the previ
o
us state, or there was none.
// Start a fresh sync by chunking up the account range and scheduling
// Start a fresh sync by chunking up the account range and scheduling
// them for retrieval.
// them for retrieval.
s
.
tasks
=
nil
s
.
tasks
=
nil
...
@@ -1280,9 +1280,9 @@ func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fai
...
@@ -1280,9 +1280,9 @@ func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fai
want
=
maxTrieRequestCount
+
maxCodeRequestCount
want
=
maxTrieRequestCount
+
maxCodeRequestCount
)
)
if
have
<
want
{
if
have
<
want
{
nodes
,
path
s
,
codes
:=
s
.
healer
.
scheduler
.
Missing
(
want
-
have
)
paths
,
hashe
s
,
codes
:=
s
.
healer
.
scheduler
.
Missing
(
want
-
have
)
for
i
,
hash
:=
range
node
s
{
for
i
,
path
:=
range
path
s
{
s
.
healer
.
trieTasks
[
hash
]
=
path
s
[
i
]
s
.
healer
.
trieTasks
[
path
]
=
hashe
s
[
i
]
}
}
for
_
,
hash
:=
range
codes
{
for
_
,
hash
:=
range
codes
{
s
.
healer
.
codeTasks
[
hash
]
=
struct
{}{}
s
.
healer
.
codeTasks
[
hash
]
=
struct
{}{}
...
@@ -1323,21 +1323,20 @@ func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fai
...
@@ -1323,21 +1323,20 @@ func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fai
}
}
var
(
var
(
hashes
=
make
([]
common
.
Hash
,
0
,
cap
)
hashes
=
make
([]
common
.
Hash
,
0
,
cap
)
paths
=
make
([]
trie
.
SyncPath
,
0
,
cap
)
paths
=
make
([]
string
,
0
,
cap
)
pathsets
=
make
([]
TrieNodePathSet
,
0
,
cap
)
pathsets
=
make
([]
TrieNodePathSet
,
0
,
cap
)
)
)
for
hash
,
pathset
:=
range
s
.
healer
.
trieTasks
{
for
path
,
hash
:=
range
s
.
healer
.
trieTasks
{
delete
(
s
.
healer
.
trieTasks
,
has
h
)
delete
(
s
.
healer
.
trieTasks
,
pat
h
)
paths
=
append
(
paths
,
path
)
hashes
=
append
(
hashes
,
hash
)
hashes
=
append
(
hashes
,
hash
)
paths
=
append
(
paths
,
pathset
)
if
len
(
paths
)
>=
cap
{
if
len
(
hashes
)
>=
cap
{
break
break
}
}
}
}
// Group requests by account hash
// Group requests by account hash
hashes
,
paths
,
pathsets
=
sortByAccountPath
(
hashes
,
path
s
)
paths
,
hashes
,
_
,
pathsets
=
sortByAccountPath
(
paths
,
hashe
s
)
req
:=
&
trienodeHealRequest
{
req
:=
&
trienodeHealRequest
{
peer
:
idle
,
peer
:
idle
,
id
:
reqid
,
id
:
reqid
,
...
@@ -1346,8 +1345,8 @@ func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fai
...
@@ -1346,8 +1345,8 @@ func (s *Syncer) assignTrienodeHealTasks(success chan *trienodeHealResponse, fai
revert
:
fail
,
revert
:
fail
,
cancel
:
cancel
,
cancel
:
cancel
,
stale
:
make
(
chan
struct
{}),
stale
:
make
(
chan
struct
{}),
hashes
:
hashes
,
paths
:
paths
,
paths
:
paths
,
hashes
:
hashes
,
task
:
s
.
healer
,
task
:
s
.
healer
,
}
}
req
.
timeout
=
time
.
AfterFunc
(
s
.
rates
.
TargetTimeout
(),
func
()
{
req
.
timeout
=
time
.
AfterFunc
(
s
.
rates
.
TargetTimeout
(),
func
()
{
...
@@ -1405,9 +1404,9 @@ func (s *Syncer) assignBytecodeHealTasks(success chan *bytecodeHealResponse, fai
...
@@ -1405,9 +1404,9 @@ func (s *Syncer) assignBytecodeHealTasks(success chan *bytecodeHealResponse, fai
want
=
maxTrieRequestCount
+
maxCodeRequestCount
want
=
maxTrieRequestCount
+
maxCodeRequestCount
)
)
if
have
<
want
{
if
have
<
want
{
nodes
,
path
s
,
codes
:=
s
.
healer
.
scheduler
.
Missing
(
want
-
have
)
paths
,
hashe
s
,
codes
:=
s
.
healer
.
scheduler
.
Missing
(
want
-
have
)
for
i
,
hash
:=
range
node
s
{
for
i
,
path
:=
range
path
s
{
s
.
healer
.
trieTasks
[
hash
]
=
path
s
[
i
]
s
.
healer
.
trieTasks
[
path
]
=
hashe
s
[
i
]
}
}
for
_
,
hash
:=
range
codes
{
for
_
,
hash
:=
range
codes
{
s
.
healer
.
codeTasks
[
hash
]
=
struct
{}{}
s
.
healer
.
codeTasks
[
hash
]
=
struct
{}{}
...
@@ -1703,10 +1702,10 @@ func (s *Syncer) revertTrienodeHealRequest(req *trienodeHealRequest) {
...
@@ -1703,10 +1702,10 @@ func (s *Syncer) revertTrienodeHealRequest(req *trienodeHealRequest) {
s
.
lock
.
Unlock
()
s
.
lock
.
Unlock
()
// If there's a timeout timer still running, abort it and mark the trie node
// If there's a timeout timer still running, abort it and mark the trie node
// retrievals as not-pending, ready for resheduling
// retrievals as not-pending, ready for res
c
heduling
req
.
timeout
.
Stop
()
req
.
timeout
.
Stop
()
for
i
,
hash
:=
range
req
.
hashe
s
{
for
i
,
path
:=
range
req
.
path
s
{
req
.
task
.
trieTasks
[
hash
]
=
req
.
path
s
[
i
]
req
.
task
.
trieTasks
[
path
]
=
req
.
hashe
s
[
i
]
}
}
}
}
...
@@ -2096,14 +2095,14 @@ func (s *Syncer) processTrienodeHealResponse(res *trienodeHealResponse) {
...
@@ -2096,14 +2095,14 @@ func (s *Syncer) processTrienodeHealResponse(res *trienodeHealResponse) {
// If the trie node was not delivered, reschedule it
// If the trie node was not delivered, reschedule it
if
node
==
nil
{
if
node
==
nil
{
res
.
task
.
trieTasks
[
hash
]
=
res
.
path
s
[
i
]
res
.
task
.
trieTasks
[
res
.
paths
[
i
]]
=
res
.
hashe
s
[
i
]
continue
continue
}
}
// Push the trie node into the state syncer
// Push the trie node into the state syncer
s
.
trienodeHealSynced
++
s
.
trienodeHealSynced
++
s
.
trienodeHealBytes
+=
common
.
StorageSize
(
len
(
node
))
s
.
trienodeHealBytes
+=
common
.
StorageSize
(
len
(
node
))
err
:=
s
.
healer
.
scheduler
.
Process
(
trie
.
SyncResult
{
Hash
:
hash
,
Data
:
node
})
err
:=
s
.
healer
.
scheduler
.
Process
Node
(
trie
.
NodeSyncResult
{
Path
:
res
.
paths
[
i
]
,
Data
:
node
})
switch
err
{
switch
err
{
case
nil
:
case
nil
:
case
trie
.
ErrAlreadyProcessed
:
case
trie
.
ErrAlreadyProcessed
:
...
@@ -2139,7 +2138,7 @@ func (s *Syncer) processBytecodeHealResponse(res *bytecodeHealResponse) {
...
@@ -2139,7 +2138,7 @@ func (s *Syncer) processBytecodeHealResponse(res *bytecodeHealResponse) {
s
.
bytecodeHealSynced
++
s
.
bytecodeHealSynced
++
s
.
bytecodeHealBytes
+=
common
.
StorageSize
(
len
(
node
))
s
.
bytecodeHealBytes
+=
common
.
StorageSize
(
len
(
node
))
err
:=
s
.
healer
.
scheduler
.
Process
(
trie
.
SyncResult
{
Hash
:
hash
,
Data
:
node
})
err
:=
s
.
healer
.
scheduler
.
Process
Code
(
trie
.
Code
SyncResult
{
Hash
:
hash
,
Data
:
node
})
switch
err
{
switch
err
{
case
nil
:
case
nil
:
case
trie
.
ErrAlreadyProcessed
:
case
trie
.
ErrAlreadyProcessed
:
...
@@ -2666,9 +2665,9 @@ func (s *Syncer) OnTrieNodes(peer SyncPeer, id uint64, trienodes [][]byte) error
...
@@ -2666,9 +2665,9 @@ func (s *Syncer) OnTrieNodes(peer SyncPeer, id uint64, trienodes [][]byte) error
}
}
// Response validated, send it to the scheduler for filling
// Response validated, send it to the scheduler for filling
response
:=
&
trienodeHealResponse
{
response
:=
&
trienodeHealResponse
{
paths
:
req
.
paths
,
task
:
req
.
task
,
task
:
req
.
task
,
hashes
:
req
.
hashes
,
hashes
:
req
.
hashes
,
paths
:
req
.
paths
,
nodes
:
nodes
,
nodes
:
nodes
,
}
}
select
{
select
{
...
@@ -2913,8 +2912,9 @@ func (s *capacitySort) Swap(i, j int) {
...
@@ -2913,8 +2912,9 @@ func (s *capacitySort) Swap(i, j int) {
// healRequestSort implements the Sort interface, allowing sorting trienode
// healRequestSort implements the Sort interface, allowing sorting trienode
// heal requests, which is a prerequisite for merging storage-requests.
// heal requests, which is a prerequisite for merging storage-requests.
type
healRequestSort
struct
{
type
healRequestSort
struct
{
hashes
[]
common
.
Hash
paths
[]
string
paths
[]
trie
.
SyncPath
hashes
[]
common
.
Hash
syncPaths
[]
trie
.
SyncPath
}
}
func
(
t
*
healRequestSort
)
Len
()
int
{
func
(
t
*
healRequestSort
)
Len
()
int
{
...
@@ -2922,8 +2922,8 @@ func (t *healRequestSort) Len() int {
...
@@ -2922,8 +2922,8 @@ func (t *healRequestSort) Len() int {
}
}
func
(
t
*
healRequestSort
)
Less
(
i
,
j
int
)
bool
{
func
(
t
*
healRequestSort
)
Less
(
i
,
j
int
)
bool
{
a
:=
t
.
p
aths
[
i
]
a
:=
t
.
syncP
aths
[
i
]
b
:=
t
.
p
aths
[
j
]
b
:=
t
.
syncP
aths
[
j
]
switch
bytes
.
Compare
(
a
[
0
],
b
[
0
])
{
switch
bytes
.
Compare
(
a
[
0
],
b
[
0
])
{
case
-
1
:
case
-
1
:
return
true
return
true
...
@@ -2944,8 +2944,9 @@ func (t *healRequestSort) Less(i, j int) bool {
...
@@ -2944,8 +2944,9 @@ func (t *healRequestSort) Less(i, j int) bool {
}
}
func
(
t
*
healRequestSort
)
Swap
(
i
,
j
int
)
{
func
(
t
*
healRequestSort
)
Swap
(
i
,
j
int
)
{
t
.
hashes
[
i
],
t
.
hashes
[
j
]
=
t
.
hashes
[
j
],
t
.
hashes
[
i
]
t
.
paths
[
i
],
t
.
paths
[
j
]
=
t
.
paths
[
j
],
t
.
paths
[
i
]
t
.
paths
[
i
],
t
.
paths
[
j
]
=
t
.
paths
[
j
],
t
.
paths
[
i
]
t
.
hashes
[
i
],
t
.
hashes
[
j
]
=
t
.
hashes
[
j
],
t
.
hashes
[
i
]
t
.
syncPaths
[
i
],
t
.
syncPaths
[
j
]
=
t
.
syncPaths
[
j
],
t
.
syncPaths
[
i
]
}
}
// Merge merges the pathsets, so that several storage requests concerning the
// Merge merges the pathsets, so that several storage requests concerning the
...
@@ -2953,7 +2954,7 @@ func (t *healRequestSort) Swap(i, j int) {
...
@@ -2953,7 +2954,7 @@ func (t *healRequestSort) Swap(i, j int) {
// OBS: This operation is moot if t has not first been sorted.
// OBS: This operation is moot if t has not first been sorted.
func
(
t
*
healRequestSort
)
Merge
()
[]
TrieNodePathSet
{
func
(
t
*
healRequestSort
)
Merge
()
[]
TrieNodePathSet
{
var
result
[]
TrieNodePathSet
var
result
[]
TrieNodePathSet
for
_
,
path
:=
range
t
.
p
aths
{
for
_
,
path
:=
range
t
.
syncP
aths
{
pathset
:=
TrieNodePathSet
([][]
byte
(
path
))
pathset
:=
TrieNodePathSet
([][]
byte
(
path
))
if
len
(
path
)
==
1
{
if
len
(
path
)
==
1
{
// It's an account reference.
// It's an account reference.
...
@@ -2962,7 +2963,7 @@ func (t *healRequestSort) Merge() []TrieNodePathSet {
...
@@ -2962,7 +2963,7 @@ func (t *healRequestSort) Merge() []TrieNodePathSet {
// It's a storage reference.
// It's a storage reference.
end
:=
len
(
result
)
-
1
end
:=
len
(
result
)
-
1
if
len
(
result
)
==
0
||
!
bytes
.
Equal
(
pathset
[
0
],
result
[
end
][
0
])
{
if
len
(
result
)
==
0
||
!
bytes
.
Equal
(
pathset
[
0
],
result
[
end
][
0
])
{
// The account doesn't
doesn't
match last, create a new entry.
// The account doesn't match last, create a new entry.
result
=
append
(
result
,
pathset
)
result
=
append
(
result
,
pathset
)
}
else
{
}
else
{
// It's the same account as the previous one, add to the storage
// It's the same account as the previous one, add to the storage
...
@@ -2976,9 +2977,13 @@ func (t *healRequestSort) Merge() []TrieNodePathSet {
...
@@ -2976,9 +2977,13 @@ func (t *healRequestSort) Merge() []TrieNodePathSet {
// sortByAccountPath takes hashes and paths, and sorts them. After that, it generates
// sortByAccountPath takes hashes and paths, and sorts them. After that, it generates
// the TrieNodePaths and merges paths which belongs to the same account path.
// the TrieNodePaths and merges paths which belongs to the same account path.
func
sortByAccountPath
(
hashes
[]
common
.
Hash
,
paths
[]
trie
.
SyncPath
)
([]
common
.
Hash
,
[]
trie
.
SyncPath
,
[]
TrieNodePathSet
)
{
func
sortByAccountPath
(
paths
[]
string
,
hashes
[]
common
.
Hash
)
([]
string
,
[]
common
.
Hash
,
[]
trie
.
SyncPath
,
[]
TrieNodePathSet
)
{
n
:=
&
healRequestSort
{
hashes
,
paths
}
var
syncPaths
[]
trie
.
SyncPath
for
_
,
path
:=
range
paths
{
syncPaths
=
append
(
syncPaths
,
trie
.
NewSyncPath
([]
byte
(
path
)))
}
n
:=
&
healRequestSort
{
paths
,
hashes
,
syncPaths
}
sort
.
Sort
(
n
)
sort
.
Sort
(
n
)
pathsets
:=
n
.
Merge
()
pathsets
:=
n
.
Merge
()
return
n
.
hashes
,
n
.
p
aths
,
pathsets
return
n
.
paths
,
n
.
hashes
,
n
.
syncP
aths
,
pathsets
}
}
les/downloader/statesync.go
View file @
1657e439
...
@@ -34,7 +34,7 @@ import (
...
@@ -34,7 +34,7 @@ import (
// a single data retrieval network packet.
// a single data retrieval network packet.
type
stateReq
struct
{
type
stateReq
struct
{
nItems
uint16
// Number of items requested for download (max is 384, so uint16 is sufficient)
nItems
uint16
// Number of items requested for download (max is 384, so uint16 is sufficient)
trieTasks
map
[
common
.
Hash
]
*
trieTask
// Trie node download tasks to track previous attempts
trieTasks
map
[
string
]
*
trieTask
// Trie node download tasks to track previous attempts
codeTasks
map
[
common
.
Hash
]
*
codeTask
// Byte code download tasks to track previous attempts
codeTasks
map
[
common
.
Hash
]
*
codeTask
// Byte code download tasks to track previous attempts
timeout
time
.
Duration
// Maximum round trip time for this to complete
timeout
time
.
Duration
// Maximum round trip time for this to complete
timer
*
time
.
Timer
// Timer to fire when the RTT timeout expires
timer
*
time
.
Timer
// Timer to fire when the RTT timeout expires
...
@@ -263,8 +263,8 @@ type stateSync struct {
...
@@ -263,8 +263,8 @@ type stateSync struct {
sched
*
trie
.
Sync
// State trie sync scheduler defining the tasks
sched
*
trie
.
Sync
// State trie sync scheduler defining the tasks
keccak
crypto
.
KeccakState
// Keccak256 hasher to verify deliveries with
keccak
crypto
.
KeccakState
// Keccak256 hasher to verify deliveries with
trieTasks
map
[
common
.
Hash
]
*
trieTask
// Set of trie node tasks currently queued for retrieval
trieTasks
map
[
string
]
*
trieTask
// Set of trie node tasks currently queued for retrieval, indexed by path
codeTasks
map
[
common
.
Hash
]
*
codeTask
// Set of byte code tasks currently queued for retrieval
codeTasks
map
[
common
.
Hash
]
*
codeTask
// Set of byte code tasks currently queued for retrieval
, indexed by hash
numUncommitted
int
numUncommitted
int
bytesUncommitted
int
bytesUncommitted
int
...
@@ -281,6 +281,7 @@ type stateSync struct {
...
@@ -281,6 +281,7 @@ type stateSync struct {
// trieTask represents a single trie node download task, containing a set of
// trieTask represents a single trie node download task, containing a set of
// peers already attempted retrieval from to detect stalled syncs and abort.
// peers already attempted retrieval from to detect stalled syncs and abort.
type
trieTask
struct
{
type
trieTask
struct
{
hash
common
.
Hash
path
[][]
byte
path
[][]
byte
attempts
map
[
string
]
struct
{}
attempts
map
[
string
]
struct
{}
}
}
...
@@ -299,7 +300,7 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync {
...
@@ -299,7 +300,7 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync {
root
:
root
,
root
:
root
,
sched
:
state
.
NewStateSync
(
root
,
d
.
stateDB
,
nil
),
sched
:
state
.
NewStateSync
(
root
,
d
.
stateDB
,
nil
),
keccak
:
sha3
.
NewLegacyKeccak256
()
.
(
crypto
.
KeccakState
),
keccak
:
sha3
.
NewLegacyKeccak256
()
.
(
crypto
.
KeccakState
),
trieTasks
:
make
(
map
[
common
.
Hash
]
*
trieTask
),
trieTasks
:
make
(
map
[
string
]
*
trieTask
),
codeTasks
:
make
(
map
[
common
.
Hash
]
*
codeTask
),
codeTasks
:
make
(
map
[
common
.
Hash
]
*
codeTask
),
deliver
:
make
(
chan
*
stateReq
),
deliver
:
make
(
chan
*
stateReq
),
cancel
:
make
(
chan
struct
{}),
cancel
:
make
(
chan
struct
{}),
...
@@ -455,10 +456,11 @@ func (s *stateSync) assignTasks() {
...
@@ -455,10 +456,11 @@ func (s *stateSync) assignTasks() {
func
(
s
*
stateSync
)
fillTasks
(
n
int
,
req
*
stateReq
)
(
nodes
[]
common
.
Hash
,
paths
[]
trie
.
SyncPath
,
codes
[]
common
.
Hash
)
{
func
(
s
*
stateSync
)
fillTasks
(
n
int
,
req
*
stateReq
)
(
nodes
[]
common
.
Hash
,
paths
[]
trie
.
SyncPath
,
codes
[]
common
.
Hash
)
{
// Refill available tasks from the scheduler.
// Refill available tasks from the scheduler.
if
fill
:=
n
-
(
len
(
s
.
trieTasks
)
+
len
(
s
.
codeTasks
));
fill
>
0
{
if
fill
:=
n
-
(
len
(
s
.
trieTasks
)
+
len
(
s
.
codeTasks
));
fill
>
0
{
nodes
,
paths
,
codes
:=
s
.
sched
.
Missing
(
fill
)
paths
,
hashes
,
codes
:=
s
.
sched
.
Missing
(
fill
)
for
i
,
hash
:=
range
nodes
{
for
i
,
path
:=
range
paths
{
s
.
trieTasks
[
hash
]
=
&
trieTask
{
s
.
trieTasks
[
path
]
=
&
trieTask
{
path
:
paths
[
i
],
hash
:
hashes
[
i
],
path
:
trie
.
NewSyncPath
([]
byte
(
path
)),
attempts
:
make
(
map
[
string
]
struct
{}),
attempts
:
make
(
map
[
string
]
struct
{}),
}
}
}
}
...
@@ -474,7 +476,7 @@ func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths
...
@@ -474,7 +476,7 @@ func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths
paths
=
make
([]
trie
.
SyncPath
,
0
,
n
)
paths
=
make
([]
trie
.
SyncPath
,
0
,
n
)
codes
=
make
([]
common
.
Hash
,
0
,
n
)
codes
=
make
([]
common
.
Hash
,
0
,
n
)
req
.
trieTasks
=
make
(
map
[
common
.
Hash
]
*
trieTask
,
n
)
req
.
trieTasks
=
make
(
map
[
string
]
*
trieTask
,
n
)
req
.
codeTasks
=
make
(
map
[
common
.
Hash
]
*
codeTask
,
n
)
req
.
codeTasks
=
make
(
map
[
common
.
Hash
]
*
codeTask
,
n
)
for
hash
,
t
:=
range
s
.
codeTasks
{
for
hash
,
t
:=
range
s
.
codeTasks
{
...
@@ -492,7 +494,7 @@ func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths
...
@@ -492,7 +494,7 @@ func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths
req
.
codeTasks
[
hash
]
=
t
req
.
codeTasks
[
hash
]
=
t
delete
(
s
.
codeTasks
,
hash
)
delete
(
s
.
codeTasks
,
hash
)
}
}
for
has
h
,
t
:=
range
s
.
trieTasks
{
for
pat
h
,
t
:=
range
s
.
trieTasks
{
// Stop when we've gathered enough requests
// Stop when we've gathered enough requests
if
len
(
nodes
)
+
len
(
codes
)
==
n
{
if
len
(
nodes
)
+
len
(
codes
)
==
n
{
break
break
...
@@ -504,11 +506,11 @@ func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths
...
@@ -504,11 +506,11 @@ func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths
// Assign the request to this peer
// Assign the request to this peer
t
.
attempts
[
req
.
peer
.
id
]
=
struct
{}{}
t
.
attempts
[
req
.
peer
.
id
]
=
struct
{}{}
nodes
=
append
(
nodes
,
hash
)
nodes
=
append
(
nodes
,
t
.
hash
)
paths
=
append
(
paths
,
t
.
path
)
paths
=
append
(
paths
,
t
.
path
)
req
.
trieTasks
[
has
h
]
=
t
req
.
trieTasks
[
pat
h
]
=
t
delete
(
s
.
trieTasks
,
has
h
)
delete
(
s
.
trieTasks
,
pat
h
)
}
}
req
.
nItems
=
uint16
(
len
(
nodes
)
+
len
(
codes
))
req
.
nItems
=
uint16
(
len
(
nodes
)
+
len
(
codes
))
return
nodes
,
paths
,
codes
return
nodes
,
paths
,
codes
...
@@ -530,7 +532,7 @@ func (s *stateSync) process(req *stateReq) (int, error) {
...
@@ -530,7 +532,7 @@ func (s *stateSync) process(req *stateReq) (int, error) {
// Iterate over all the delivered data and inject one-by-one into the trie
// Iterate over all the delivered data and inject one-by-one into the trie
for
_
,
blob
:=
range
req
.
response
{
for
_
,
blob
:=
range
req
.
response
{
hash
,
err
:=
s
.
processNodeData
(
blob
)
hash
,
err
:=
s
.
processNodeData
(
req
.
trieTasks
,
req
.
codeTasks
,
blob
)
switch
err
{
switch
err
{
case
nil
:
case
nil
:
s
.
numUncommitted
++
s
.
numUncommitted
++
...
@@ -543,13 +545,10 @@ func (s *stateSync) process(req *stateReq) (int, error) {
...
@@ -543,13 +545,10 @@ func (s *stateSync) process(req *stateReq) (int, error) {
default
:
default
:
return
successful
,
fmt
.
Errorf
(
"invalid state node %s: %v"
,
hash
.
TerminalString
(),
err
)
return
successful
,
fmt
.
Errorf
(
"invalid state node %s: %v"
,
hash
.
TerminalString
(),
err
)
}
}
// Delete from both queues (one delivery is enough for the syncer)
delete
(
req
.
trieTasks
,
hash
)
delete
(
req
.
codeTasks
,
hash
)
}
}
// Put unfulfilled tasks back into the retry queue
// Put unfulfilled tasks back into the retry queue
npeers
:=
s
.
d
.
peers
.
Len
()
npeers
:=
s
.
d
.
peers
.
Len
()
for
has
h
,
task
:=
range
req
.
trieTasks
{
for
pat
h
,
task
:=
range
req
.
trieTasks
{
// If the node did deliver something, missing items may be due to a protocol
// If the node did deliver something, missing items may be due to a protocol
// limit or a previous timeout + delayed delivery. Both cases should permit
// limit or a previous timeout + delayed delivery. Both cases should permit
// the node to retry the missing items (to avoid single-peer stalls).
// the node to retry the missing items (to avoid single-peer stalls).
...
@@ -559,10 +558,10 @@ func (s *stateSync) process(req *stateReq) (int, error) {
...
@@ -559,10 +558,10 @@ func (s *stateSync) process(req *stateReq) (int, error) {
// If we've requested the node too many times already, it may be a malicious
// If we've requested the node too many times already, it may be a malicious
// sync where nobody has the right data. Abort.
// sync where nobody has the right data. Abort.
if
len
(
task
.
attempts
)
>=
npeers
{
if
len
(
task
.
attempts
)
>=
npeers
{
return
successful
,
fmt
.
Errorf
(
"trie node %s failed with all peers (%d tries, %d peers)"
,
hash
.
TerminalString
(),
len
(
task
.
attempts
),
npeers
)
return
successful
,
fmt
.
Errorf
(
"trie node %s failed with all peers (%d tries, %d peers)"
,
task
.
hash
.
TerminalString
(),
len
(
task
.
attempts
),
npeers
)
}
}
// Missing item, place into the retry queue.
// Missing item, place into the retry queue.
s
.
trieTasks
[
has
h
]
=
task
s
.
trieTasks
[
pat
h
]
=
task
}
}
for
hash
,
task
:=
range
req
.
codeTasks
{
for
hash
,
task
:=
range
req
.
codeTasks
{
// If the node did deliver something, missing items may be due to a protocol
// If the node did deliver something, missing items may be due to a protocol
...
@@ -585,13 +584,35 @@ func (s *stateSync) process(req *stateReq) (int, error) {
...
@@ -585,13 +584,35 @@ func (s *stateSync) process(req *stateReq) (int, error) {
// processNodeData tries to inject a trie node data blob delivered from a remote
// processNodeData tries to inject a trie node data blob delivered from a remote
// peer into the state trie, returning whether anything useful was written or any
// peer into the state trie, returning whether anything useful was written or any
// error occurred.
// error occurred.
func
(
s
*
stateSync
)
processNodeData
(
blob
[]
byte
)
(
common
.
Hash
,
error
)
{
//
res
:=
trie
.
SyncResult
{
Data
:
blob
}
// If multiple requests correspond to the same hash, this method will inject the
// blob as a result for the first one only, leaving the remaining duplicates to
// be fetched again.
func
(
s
*
stateSync
)
processNodeData
(
nodeTasks
map
[
string
]
*
trieTask
,
codeTasks
map
[
common
.
Hash
]
*
codeTask
,
blob
[]
byte
)
(
common
.
Hash
,
error
)
{
var
hash
common
.
Hash
s
.
keccak
.
Reset
()
s
.
keccak
.
Reset
()
s
.
keccak
.
Write
(
blob
)
s
.
keccak
.
Write
(
blob
)
s
.
keccak
.
Read
(
res
.
Hash
[
:
])
s
.
keccak
.
Read
(
hash
[
:
])
err
:=
s
.
sched
.
Process
(
res
)
return
res
.
Hash
,
err
if
_
,
present
:=
codeTasks
[
hash
];
present
{
err
:=
s
.
sched
.
ProcessCode
(
trie
.
CodeSyncResult
{
Hash
:
hash
,
Data
:
blob
,
})
delete
(
codeTasks
,
hash
)
return
hash
,
err
}
for
path
,
task
:=
range
nodeTasks
{
if
task
.
hash
==
hash
{
err
:=
s
.
sched
.
ProcessNode
(
trie
.
NodeSyncResult
{
Path
:
path
,
Data
:
blob
,
})
delete
(
nodeTasks
,
path
)
return
hash
,
err
}
}
return
common
.
Hash
{},
trie
.
ErrNotRequested
}
}
// updateStats bumps the various state sync progress counters and displays a log
// updateStats bumps the various state sync progress counters and displays a log
...
...
trie/committer.go
View file @
1657e439
...
@@ -33,6 +33,7 @@ type leaf struct {
...
@@ -33,6 +33,7 @@ type leaf struct {
size
int
// size of the rlp data (estimate)
size
int
// size of the rlp data (estimate)
hash
common
.
Hash
// hash of rlp data
hash
common
.
Hash
// hash of rlp data
node
node
// the node to commit
node
node
// the node to commit
path
[]
byte
// the path from the root node
}
}
// committer is a type used for the trie Commit operation. A committer has some
// committer is a type used for the trie Commit operation. A committer has some
...
@@ -69,7 +70,7 @@ func (c *committer) Commit(n node, db *Database) (hashNode, int, error) {
...
@@ -69,7 +70,7 @@ func (c *committer) Commit(n node, db *Database) (hashNode, int, error) {
if
db
==
nil
{
if
db
==
nil
{
return
nil
,
0
,
errors
.
New
(
"no db provided"
)
return
nil
,
0
,
errors
.
New
(
"no db provided"
)
}
}
h
,
committed
,
err
:=
c
.
commit
(
n
,
db
)
h
,
committed
,
err
:=
c
.
commit
(
n
il
,
n
,
db
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
}
}
...
@@ -77,7 +78,7 @@ func (c *committer) Commit(n node, db *Database) (hashNode, int, error) {
...
@@ -77,7 +78,7 @@ func (c *committer) Commit(n node, db *Database) (hashNode, int, error) {
}
}
// commit collapses a node down into a hash node and inserts it into the database
// commit collapses a node down into a hash node and inserts it into the database
func
(
c
*
committer
)
commit
(
n
node
,
db
*
Database
)
(
node
,
int
,
error
)
{
func
(
c
*
committer
)
commit
(
path
[]
byte
,
n
node
,
db
*
Database
)
(
node
,
int
,
error
)
{
// if this path is clean, use available cached data
// if this path is clean, use available cached data
hash
,
dirty
:=
n
.
cache
()
hash
,
dirty
:=
n
.
cache
()
if
hash
!=
nil
&&
!
dirty
{
if
hash
!=
nil
&&
!
dirty
{
...
@@ -93,7 +94,7 @@ func (c *committer) commit(n node, db *Database) (node, int, error) {
...
@@ -93,7 +94,7 @@ func (c *committer) commit(n node, db *Database) (node, int, error) {
// otherwise it can only be hashNode or valueNode.
// otherwise it can only be hashNode or valueNode.
var
childCommitted
int
var
childCommitted
int
if
_
,
ok
:=
cn
.
Val
.
(
*
fullNode
);
ok
{
if
_
,
ok
:=
cn
.
Val
.
(
*
fullNode
);
ok
{
childV
,
committed
,
err
:=
c
.
commit
(
cn
.
Val
,
db
)
childV
,
committed
,
err
:=
c
.
commit
(
append
(
path
,
cn
.
Key
...
),
cn
.
Val
,
db
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
}
}
...
@@ -101,20 +102,20 @@ func (c *committer) commit(n node, db *Database) (node, int, error) {
...
@@ -101,20 +102,20 @@ func (c *committer) commit(n node, db *Database) (node, int, error) {
}
}
// The key needs to be copied, since we're delivering it to database
// The key needs to be copied, since we're delivering it to database
collapsed
.
Key
=
hexToCompact
(
cn
.
Key
)
collapsed
.
Key
=
hexToCompact
(
cn
.
Key
)
hashedNode
:=
c
.
store
(
collapsed
,
db
)
hashedNode
:=
c
.
store
(
path
,
collapsed
,
db
)
if
hn
,
ok
:=
hashedNode
.
(
hashNode
);
ok
{
if
hn
,
ok
:=
hashedNode
.
(
hashNode
);
ok
{
return
hn
,
childCommitted
+
1
,
nil
return
hn
,
childCommitted
+
1
,
nil
}
}
return
collapsed
,
childCommitted
,
nil
return
collapsed
,
childCommitted
,
nil
case
*
fullNode
:
case
*
fullNode
:
hashedKids
,
childCommitted
,
err
:=
c
.
commitChildren
(
cn
,
db
)
hashedKids
,
childCommitted
,
err
:=
c
.
commitChildren
(
path
,
cn
,
db
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
}
}
collapsed
:=
cn
.
copy
()
collapsed
:=
cn
.
copy
()
collapsed
.
Children
=
hashedKids
collapsed
.
Children
=
hashedKids
hashedNode
:=
c
.
store
(
collapsed
,
db
)
hashedNode
:=
c
.
store
(
path
,
collapsed
,
db
)
if
hn
,
ok
:=
hashedNode
.
(
hashNode
);
ok
{
if
hn
,
ok
:=
hashedNode
.
(
hashNode
);
ok
{
return
hn
,
childCommitted
+
1
,
nil
return
hn
,
childCommitted
+
1
,
nil
}
}
...
@@ -128,7 +129,7 @@ func (c *committer) commit(n node, db *Database) (node, int, error) {
...
@@ -128,7 +129,7 @@ func (c *committer) commit(n node, db *Database) (node, int, error) {
}
}
// commitChildren commits the children of the given fullnode
// commitChildren commits the children of the given fullnode
func
(
c
*
committer
)
commitChildren
(
n
*
fullNode
,
db
*
Database
)
([
17
]
node
,
int
,
error
)
{
func
(
c
*
committer
)
commitChildren
(
path
[]
byte
,
n
*
fullNode
,
db
*
Database
)
([
17
]
node
,
int
,
error
)
{
var
(
var
(
committed
int
committed
int
children
[
17
]
node
children
[
17
]
node
...
@@ -148,7 +149,7 @@ func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, int, er
...
@@ -148,7 +149,7 @@ func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, int, er
// Commit the child recursively and store the "hashed" value.
// Commit the child recursively and store the "hashed" value.
// Note the returned node can be some embedded nodes, so it's
// Note the returned node can be some embedded nodes, so it's
// possible the type is not hashNode.
// possible the type is not hashNode.
hashed
,
childCommitted
,
err
:=
c
.
commit
(
child
,
db
)
hashed
,
childCommitted
,
err
:=
c
.
commit
(
append
(
path
,
byte
(
i
)),
child
,
db
)
if
err
!=
nil
{
if
err
!=
nil
{
return
children
,
0
,
err
return
children
,
0
,
err
}
}
...
@@ -165,7 +166,7 @@ func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, int, er
...
@@ -165,7 +166,7 @@ func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, int, er
// store hashes the node n and if we have a storage layer specified, it writes
// store hashes the node n and if we have a storage layer specified, it writes
// the key/value pair to it and tracks any node->child references as well as any
// the key/value pair to it and tracks any node->child references as well as any
// node->external trie references.
// node->external trie references.
func
(
c
*
committer
)
store
(
n
node
,
db
*
Database
)
node
{
func
(
c
*
committer
)
store
(
path
[]
byte
,
n
node
,
db
*
Database
)
node
{
// Larger nodes are replaced by their hash and stored in the database.
// Larger nodes are replaced by their hash and stored in the database.
var
(
var
(
hash
,
_
=
n
.
cache
()
hash
,
_
=
n
.
cache
()
...
@@ -189,6 +190,7 @@ func (c *committer) store(n node, db *Database) node {
...
@@ -189,6 +190,7 @@ func (c *committer) store(n node, db *Database) node {
size
:
size
,
size
:
size
,
hash
:
common
.
BytesToHash
(
hash
),
hash
:
common
.
BytesToHash
(
hash
),
node
:
n
,
node
:
n
,
path
:
path
,
}
}
}
else
if
db
!=
nil
{
}
else
if
db
!=
nil
{
// No leaf-callback used, but there's still a database. Do serial
// No leaf-callback used, but there's still a database. Do serial
...
@@ -213,13 +215,13 @@ func (c *committer) commitLoop(db *Database) {
...
@@ -213,13 +215,13 @@ func (c *committer) commitLoop(db *Database) {
switch
n
:=
n
.
(
type
)
{
switch
n
:=
n
.
(
type
)
{
case
*
shortNode
:
case
*
shortNode
:
if
child
,
ok
:=
n
.
Val
.
(
valueNode
);
ok
{
if
child
,
ok
:=
n
.
Val
.
(
valueNode
);
ok
{
c
.
onleaf
(
nil
,
nil
,
child
,
hash
)
c
.
onleaf
(
nil
,
nil
,
child
,
hash
,
nil
)
}
}
case
*
fullNode
:
case
*
fullNode
:
// For children in range [0, 15], it's impossible
// For children in range [0, 15], it's impossible
// to contain valueNode. Only check the 17th child.
// to contain valueNode. Only check the 17th child.
if
n
.
Children
[
16
]
!=
nil
{
if
n
.
Children
[
16
]
!=
nil
{
c
.
onleaf
(
nil
,
nil
,
n
.
Children
[
16
]
.
(
valueNode
),
hash
)
c
.
onleaf
(
nil
,
nil
,
n
.
Children
[
16
]
.
(
valueNode
),
hash
,
nil
)
}
}
}
}
}
}
...
...
trie/sync.go
View file @
1657e439
...
@@ -24,6 +24,7 @@ import (
...
@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
)
// ErrNotRequested is returned by the trie sync when it's requested to process a
// ErrNotRequested is returned by the trie sync when it's requested to process a
...
@@ -39,19 +40,6 @@ var ErrAlreadyProcessed = errors.New("already processed")
...
@@ -39,19 +40,6 @@ var ErrAlreadyProcessed = errors.New("already processed")
// memory if the node was configured with a significant number of peers.
// memory if the node was configured with a significant number of peers.
const
maxFetchesPerDepth
=
16384
const
maxFetchesPerDepth
=
16384
// request represents a scheduled or already in-flight state retrieval request.
type
request
struct
{
path
[]
byte
// Merkle path leading to this node for prioritization
hash
common
.
Hash
// Hash of the node data content to retrieve
data
[]
byte
// Data content of the node, cached until all subtrees complete
code
bool
// Whether this is a code entry
parents
[]
*
request
// Parent state nodes referencing this entry (notify all upon completion)
deps
int
// Number of dependencies before allowed to commit this node
callback
LeafCallback
// Callback to invoke if a leaf node it reached on this branch
}
// SyncPath is a path tuple identifying a particular trie node either in a single
// SyncPath is a path tuple identifying a particular trie node either in a single
// trie (account) or a layered trie (account -> storage).
// trie (account) or a layered trie (account -> storage).
//
//
...
@@ -85,30 +73,57 @@ func NewSyncPath(path []byte) SyncPath {
...
@@ -85,30 +73,57 @@ func NewSyncPath(path []byte) SyncPath {
return
SyncPath
{
hexToKeybytes
(
path
[
:
64
]),
hexToCompact
(
path
[
64
:
])}
return
SyncPath
{
hexToKeybytes
(
path
[
:
64
]),
hexToCompact
(
path
[
64
:
])}
}
}
// SyncResult is a response with requested data along with it's hash.
// nodeRequest represents a scheduled or already in-flight trie node retrieval request.
type
SyncResult
struct
{
type
nodeRequest
struct
{
Hash
common
.
Hash
// Hash of the originally unknown trie node
hash
common
.
Hash
// Hash of the trie node to retrieve
Data
[]
byte
// Data content of the retrieved node
path
[]
byte
// Merkle path leading to this node for prioritization
data
[]
byte
// Data content of the node, cached until all subtrees complete
parent
*
nodeRequest
// Parent state node referencing this entry
deps
int
// Number of dependencies before allowed to commit this node
callback
LeafCallback
// Callback to invoke if a leaf node it reached on this branch
}
// codeRequest represents a scheduled or already in-flight bytecode retrieval request.
type
codeRequest
struct
{
hash
common
.
Hash
// Hash of the contract bytecode to retrieve
path
[]
byte
// Merkle path leading to this node for prioritization
data
[]
byte
// Data content of the node, cached until all subtrees complete
parents
[]
*
nodeRequest
// Parent state nodes referencing this entry (notify all upon completion)
}
// NodeSyncResult is a response with requested trie node along with its node path.
type
NodeSyncResult
struct
{
Path
string
// Path of the originally unknown trie node
Data
[]
byte
// Data content of the retrieved trie node
}
// CodeSyncResult is a response with requested bytecode along with its hash.
type
CodeSyncResult
struct
{
Hash
common
.
Hash
// Hash the originally unknown bytecode
Data
[]
byte
// Data content of the retrieved bytecode
}
}
// syncMemBatch is an in-memory buffer of successfully downloaded but not yet
// syncMemBatch is an in-memory buffer of successfully downloaded but not yet
// persisted data items.
// persisted data items.
type
syncMemBatch
struct
{
type
syncMemBatch
struct
{
nodes
map
[
common
.
Hash
][]
byte
// In-memory membatch of recently completed nodes
nodes
map
[
string
][]
byte
// In-memory membatch of recently completed nodes
codes
map
[
common
.
Hash
][]
byte
// In-memory membatch of recently completed codes
hashes
map
[
string
]
common
.
Hash
// Hashes of recently completed nodes
codes
map
[
common
.
Hash
][]
byte
// In-memory membatch of recently completed codes
}
}
// newSyncMemBatch allocates a new memory-buffer for not-yet persisted trie nodes.
// newSyncMemBatch allocates a new memory-buffer for not-yet persisted trie nodes.
func
newSyncMemBatch
()
*
syncMemBatch
{
func
newSyncMemBatch
()
*
syncMemBatch
{
return
&
syncMemBatch
{
return
&
syncMemBatch
{
nodes
:
make
(
map
[
common
.
Hash
][]
byte
),
nodes
:
make
(
map
[
string
][]
byte
),
codes
:
make
(
map
[
common
.
Hash
][]
byte
),
hashes
:
make
(
map
[
string
]
common
.
Hash
),
codes
:
make
(
map
[
common
.
Hash
][]
byte
),
}
}
}
}
// hasNode reports the trie node with specific
has
h is already cached.
// hasNode reports the trie node with specific
pat
h is already cached.
func
(
batch
*
syncMemBatch
)
hasNode
(
hash
common
.
Hash
)
bool
{
func
(
batch
*
syncMemBatch
)
hasNode
(
path
[]
byte
)
bool
{
_
,
ok
:=
batch
.
nodes
[
hash
]
_
,
ok
:=
batch
.
nodes
[
string
(
path
)
]
return
ok
return
ok
}
}
...
@@ -122,12 +137,12 @@ func (batch *syncMemBatch) hasCode(hash common.Hash) bool {
...
@@ -122,12 +137,12 @@ func (batch *syncMemBatch) hasCode(hash common.Hash) bool {
// unknown trie hashes to retrieve, accepts node data associated with said hashes
// unknown trie hashes to retrieve, accepts node data associated with said hashes
// and reconstructs the trie step by step until all is done.
// and reconstructs the trie step by step until all is done.
type
Sync
struct
{
type
Sync
struct
{
database
ethdb
.
KeyValueReader
// Persistent database to check for existing entries
database
ethdb
.
KeyValueReader
// Persistent database to check for existing entries
membatch
*
syncMemBatch
// Memory buffer to avoid frequent database writes
membatch
*
syncMemBatch
// Memory buffer to avoid frequent database writes
nodeReqs
map
[
common
.
Hash
]
*
request
// Pending requests pertaining to a trie node has
h
nodeReqs
map
[
string
]
*
nodeRequest
// Pending requests pertaining to a trie node pat
h
codeReqs
map
[
common
.
Hash
]
*
r
equest
// Pending requests pertaining to a code hash
codeReqs
map
[
common
.
Hash
]
*
codeR
equest
// Pending requests pertaining to a code hash
queue
*
prque
.
Prque
// Priority queue with the pending requests
queue
*
prque
.
Prque
// Priority queue with the pending requests
fetches
map
[
int
]
int
// Number of active fetches per trie node depth
fetches
map
[
int
]
int
// Number of active fetches per trie node depth
}
}
// NewSync creates a new trie data download scheduler.
// NewSync creates a new trie data download scheduler.
...
@@ -135,51 +150,51 @@ func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallb
...
@@ -135,51 +150,51 @@ func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallb
ts
:=
&
Sync
{
ts
:=
&
Sync
{
database
:
database
,
database
:
database
,
membatch
:
newSyncMemBatch
(),
membatch
:
newSyncMemBatch
(),
nodeReqs
:
make
(
map
[
common
.
Hash
]
*
r
equest
),
nodeReqs
:
make
(
map
[
string
]
*
nodeR
equest
),
codeReqs
:
make
(
map
[
common
.
Hash
]
*
r
equest
),
codeReqs
:
make
(
map
[
common
.
Hash
]
*
codeR
equest
),
queue
:
prque
.
New
(
nil
),
queue
:
prque
.
New
(
nil
),
fetches
:
make
(
map
[
int
]
int
),
fetches
:
make
(
map
[
int
]
int
),
}
}
ts
.
AddSubTrie
(
root
,
nil
,
common
.
Hash
{},
callback
)
ts
.
AddSubTrie
(
root
,
nil
,
common
.
Hash
{},
nil
,
callback
)
return
ts
return
ts
}
}
// AddSubTrie registers a new trie to the sync code, rooted at the designated parent.
// AddSubTrie registers a new trie to the sync code, rooted at the designated
func
(
s
*
Sync
)
AddSubTrie
(
root
common
.
Hash
,
path
[]
byte
,
parent
common
.
Hash
,
callback
LeafCallback
)
{
// parent for completion tracking. The given path is a unique node path in
// hex format and contain all the parent path if it's layered trie node.
func
(
s
*
Sync
)
AddSubTrie
(
root
common
.
Hash
,
path
[]
byte
,
parent
common
.
Hash
,
parentPath
[]
byte
,
callback
LeafCallback
)
{
// Short circuit if the trie is empty or already known
// Short circuit if the trie is empty or already known
if
root
==
emptyRoot
{
if
root
==
emptyRoot
{
return
return
}
}
if
s
.
membatch
.
hasNode
(
root
)
{
if
s
.
membatch
.
hasNode
(
path
)
{
return
return
}
}
// If database says this is a duplicate, then at least the trie node is
// present, and we hold the assumption that it's NOT legacy contract code.
if
rawdb
.
HasTrieNode
(
s
.
database
,
root
)
{
if
rawdb
.
HasTrieNode
(
s
.
database
,
root
)
{
return
return
}
}
// Assemble the new sub-trie sync request
// Assemble the new sub-trie sync request
req
:=
&
request
{
req
:=
&
nodeRequest
{
path
:
path
,
hash
:
root
,
hash
:
root
,
path
:
path
,
callback
:
callback
,
callback
:
callback
,
}
}
// If this sub-trie has a designated parent, link them together
// If this sub-trie has a designated parent, link them together
if
parent
!=
(
common
.
Hash
{})
{
if
parent
!=
(
common
.
Hash
{})
{
ancestor
:=
s
.
nodeReqs
[
parent
]
ancestor
:=
s
.
nodeReqs
[
string
(
parentPath
)
]
if
ancestor
==
nil
{
if
ancestor
==
nil
{
panic
(
fmt
.
Sprintf
(
"sub-trie ancestor not found: %x"
,
parent
))
panic
(
fmt
.
Sprintf
(
"sub-trie ancestor not found: %x"
,
parent
))
}
}
ancestor
.
deps
++
ancestor
.
deps
++
req
.
parent
s
=
append
(
req
.
parents
,
ancestor
)
req
.
parent
=
ancestor
}
}
s
.
schedule
(
req
)
s
.
schedule
NodeRequest
(
req
)
}
}
// AddCodeEntry schedules the direct retrieval of a contract code that should not
// AddCodeEntry schedules the direct retrieval of a contract code that should not
// be interpreted as a trie node, but rather accepted and stored into the database
// be interpreted as a trie node, but rather accepted and stored into the database
// as is.
// as is.
func
(
s
*
Sync
)
AddCodeEntry
(
hash
common
.
Hash
,
path
[]
byte
,
parent
common
.
Hash
)
{
func
(
s
*
Sync
)
AddCodeEntry
(
hash
common
.
Hash
,
path
[]
byte
,
parent
common
.
Hash
,
parentPath
[]
byte
)
{
// Short circuit if the entry is empty or already known
// Short circuit if the entry is empty or already known
if
hash
==
emptyState
{
if
hash
==
emptyState
{
return
return
...
@@ -196,30 +211,29 @@ func (s *Sync) AddCodeEntry(hash common.Hash, path []byte, parent common.Hash) {
...
@@ -196,30 +211,29 @@ func (s *Sync) AddCodeEntry(hash common.Hash, path []byte, parent common.Hash) {
return
return
}
}
// Assemble the new sub-trie sync request
// Assemble the new sub-trie sync request
req
:=
&
r
equest
{
req
:=
&
codeR
equest
{
path
:
path
,
path
:
path
,
hash
:
hash
,
hash
:
hash
,
code
:
true
,
}
}
// If this sub-trie has a designated parent, link them together
// If this sub-trie has a designated parent, link them together
if
parent
!=
(
common
.
Hash
{})
{
if
parent
!=
(
common
.
Hash
{})
{
ancestor
:=
s
.
nodeReqs
[
parent
]
// the parent of codereq can ONLY be nodereq
ancestor
:=
s
.
nodeReqs
[
string
(
parentPath
)
]
// the parent of codereq can ONLY be nodereq
if
ancestor
==
nil
{
if
ancestor
==
nil
{
panic
(
fmt
.
Sprintf
(
"raw-entry ancestor not found: %x"
,
parent
))
panic
(
fmt
.
Sprintf
(
"raw-entry ancestor not found: %x"
,
parent
))
}
}
ancestor
.
deps
++
ancestor
.
deps
++
req
.
parents
=
append
(
req
.
parents
,
ancestor
)
req
.
parents
=
append
(
req
.
parents
,
ancestor
)
}
}
s
.
schedule
(
req
)
s
.
schedule
CodeRequest
(
req
)
}
}
// Missing retrieves the known missing nodes from the trie for retrieval. To aid
// Missing retrieves the known missing nodes from the trie for retrieval. To aid
// both eth/6x style fast sync and snap/1x style state sync, the paths of trie
// both eth/6x style fast sync and snap/1x style state sync, the paths of trie
// nodes are returned too, as well as separate hash list for codes.
// nodes are returned too, as well as separate hash list for codes.
func
(
s
*
Sync
)
Missing
(
max
int
)
(
nodes
[]
common
.
Hash
,
paths
[]
SyncPath
,
codes
[]
common
.
Hash
)
{
func
(
s
*
Sync
)
Missing
(
max
int
)
(
[]
string
,
[]
common
.
Hash
,
[]
common
.
Hash
)
{
var
(
var
(
nodePaths
[]
string
nodeHashes
[]
common
.
Hash
nodeHashes
[]
common
.
Hash
nodePaths
[]
SyncPath
codeHashes
[]
common
.
Hash
codeHashes
[]
common
.
Hash
)
)
for
!
s
.
queue
.
Empty
()
&&
(
max
==
0
||
len
(
nodeHashes
)
+
len
(
codeHashes
)
<
max
)
{
for
!
s
.
queue
.
Empty
()
&&
(
max
==
0
||
len
(
nodeHashes
)
+
len
(
codeHashes
)
<
max
)
{
...
@@ -235,62 +249,77 @@ func (s *Sync) Missing(max int) (nodes []common.Hash, paths []SyncPath, codes []
...
@@ -235,62 +249,77 @@ func (s *Sync) Missing(max int) (nodes []common.Hash, paths []SyncPath, codes []
s
.
queue
.
Pop
()
s
.
queue
.
Pop
()
s
.
fetches
[
depth
]
++
s
.
fetches
[
depth
]
++
hash
:=
item
.
(
common
.
Hash
)
switch
item
.
(
type
)
{
if
req
,
ok
:=
s
.
nodeReqs
[
hash
];
ok
{
case
common
.
Hash
:
nodeHashes
=
append
(
nodeHashes
,
hash
)
codeHashes
=
append
(
codeHashes
,
item
.
(
common
.
Hash
))
nodePaths
=
append
(
nodePaths
,
NewSyncPath
(
req
.
path
))
case
string
:
}
else
{
path
:=
item
.
(
string
)
codeHashes
=
append
(
codeHashes
,
hash
)
req
,
ok
:=
s
.
nodeReqs
[
path
]
if
!
ok
{
log
.
Error
(
"Missing node request"
,
"path"
,
path
)
continue
// System very wrong, shouldn't happen
}
nodePaths
=
append
(
nodePaths
,
path
)
nodeHashes
=
append
(
nodeHashes
,
req
.
hash
)
}
}
}
}
return
node
Hashes
,
nodePath
s
,
codeHashes
return
node
Paths
,
nodeHashe
s
,
codeHashes
}
}
// Process injects the received data for requested item. Note it can
// Process
Code
injects the received data for requested item. Note it can
// happpen that the single response commits two pending requests(e.g.
// happpen that the single response commits two pending requests(e.g.
// there are two requests one for code and one for node but the hash
// there are two requests one for code and one for node but the hash
// is same). In this case the second response for the same hash will
// is same). In this case the second response for the same hash will
// be treated as "non-requested" item or "already-processed" item but
// be treated as "non-requested" item or "already-processed" item but
// there is no downside.
// there is no downside.
func
(
s
*
Sync
)
Process
(
result
SyncResult
)
error
{
func
(
s
*
Sync
)
ProcessCode
(
result
CodeSyncResult
)
error
{
// If the item was not requested either for code or node, bail out
// If the code was not requested or it's already processed, bail out
if
s
.
nodeReqs
[
result
.
Hash
]
==
nil
&&
s
.
codeReqs
[
result
.
Hash
]
==
nil
{
req
:=
s
.
codeReqs
[
result
.
Hash
]
if
req
==
nil
{
return
ErrNotRequested
return
ErrNotRequested
}
}
// There is an pending code request for this data, commit directly
if
req
.
data
!=
nil
{
var
filled
bool
return
ErrAlreadyProcessed
if
req
:=
s
.
codeReqs
[
result
.
Hash
];
req
!=
nil
&&
req
.
data
==
nil
{
filled
=
true
req
.
data
=
result
.
Data
s
.
commit
(
req
)
}
}
// There is an pending node request for this data, fill it.
req
.
data
=
result
.
Data
if
req
:=
s
.
nodeReqs
[
result
.
Hash
];
req
!=
nil
&&
req
.
data
==
nil
{
return
s
.
commitCodeRequest
(
req
)
filled
=
true
}
// Decode the node data content and update the request
node
,
err
:=
decodeNode
(
result
.
Hash
[
:
],
result
.
Data
)
if
err
!=
nil
{
return
err
}
req
.
data
=
result
.
Data
// Create and schedule a request for all the children nodes
// ProcessNode injects the received data for requested item. Note it can
requests
,
err
:=
s
.
children
(
req
,
node
)
// happen that the single response commits two pending requests(e.g.
if
err
!=
nil
{
// there are two requests one for code and one for node but the hash
return
err
// is same). In this case the second response for the same hash will
}
// be treated as "non-requested" item or "already-processed" item but
if
len
(
requests
)
==
0
&&
req
.
deps
==
0
{
// there is no downside.
s
.
commit
(
req
)
func
(
s
*
Sync
)
ProcessNode
(
result
NodeSyncResult
)
error
{
}
else
{
// If the trie node was not requested or it's already processed, bail out
req
.
deps
+=
len
(
requests
)
req
:=
s
.
nodeReqs
[
result
.
Path
]
for
_
,
child
:=
range
requests
{
if
req
==
nil
{
s
.
schedule
(
child
)
return
ErrNotRequested
}
}
}
}
if
!
filled
{
if
req
.
data
!=
nil
{
return
ErrAlreadyProcessed
return
ErrAlreadyProcessed
}
}
// Decode the node data content and update the request
node
,
err
:=
decodeNode
(
req
.
hash
.
Bytes
(),
result
.
Data
)
if
err
!=
nil
{
return
err
}
req
.
data
=
result
.
Data
// Create and schedule a request for all the children nodes
requests
,
err
:=
s
.
children
(
req
,
node
)
if
err
!=
nil
{
return
err
}
if
len
(
requests
)
==
0
&&
req
.
deps
==
0
{
s
.
commitNodeRequest
(
req
)
}
else
{
req
.
deps
+=
len
(
requests
)
for
_
,
child
:=
range
requests
{
s
.
scheduleNodeRequest
(
child
)
}
}
return
nil
return
nil
}
}
...
@@ -298,11 +327,11 @@ func (s *Sync) Process(result SyncResult) error {
...
@@ -298,11 +327,11 @@ func (s *Sync) Process(result SyncResult) error {
// storage, returning any occurred error.
// storage, returning any occurred error.
func
(
s
*
Sync
)
Commit
(
dbw
ethdb
.
Batch
)
error
{
func
(
s
*
Sync
)
Commit
(
dbw
ethdb
.
Batch
)
error
{
// Dump the membatch into a database dbw
// Dump the membatch into a database dbw
for
key
,
value
:=
range
s
.
membatch
.
nodes
{
for
path
,
value
:=
range
s
.
membatch
.
nodes
{
rawdb
.
WriteTrieNode
(
dbw
,
key
,
value
)
rawdb
.
WriteTrieNode
(
dbw
,
s
.
membatch
.
hashes
[
path
]
,
value
)
}
}
for
key
,
value
:=
range
s
.
membatch
.
codes
{
for
hash
,
value
:=
range
s
.
membatch
.
codes
{
rawdb
.
WriteCode
(
dbw
,
key
,
value
)
rawdb
.
WriteCode
(
dbw
,
hash
,
value
)
}
}
// Drop the membatch data and return
// Drop the membatch data and return
s
.
membatch
=
newSyncMemBatch
()
s
.
membatch
=
newSyncMemBatch
()
...
@@ -317,23 +346,31 @@ func (s *Sync) Pending() int {
...
@@ -317,23 +346,31 @@ func (s *Sync) Pending() int {
// schedule inserts a new state retrieval request into the fetch queue. If there
// schedule inserts a new state retrieval request into the fetch queue. If there
// is already a pending request for this node, the new request will be discarded
// is already a pending request for this node, the new request will be discarded
// and only a parent reference added to the old one.
// and only a parent reference added to the old one.
func
(
s
*
Sync
)
schedule
(
req
*
request
)
{
func
(
s
*
Sync
)
scheduleNodeRequest
(
req
*
nodeRequest
)
{
var
reqset
=
s
.
nodeReqs
s
.
nodeReqs
[
string
(
req
.
path
)]
=
req
if
req
.
code
{
reqset
=
s
.
codeReqs
// Schedule the request for future retrieval. This queue is shared
// by both node requests and code requests.
prio
:=
int64
(
len
(
req
.
path
))
<<
56
// depth >= 128 will never happen, storage leaves will be included in their parents
for
i
:=
0
;
i
<
14
&&
i
<
len
(
req
.
path
);
i
++
{
prio
|=
int64
(
15
-
req
.
path
[
i
])
<<
(
52
-
i
*
4
)
// 15-nibble => lexicographic order
}
}
s
.
queue
.
Push
(
string
(
req
.
path
),
prio
)
}
// schedule inserts a new state retrieval request into the fetch queue. If there
// is already a pending request for this node, the new request will be discarded
// and only a parent reference added to the old one.
func
(
s
*
Sync
)
scheduleCodeRequest
(
req
*
codeRequest
)
{
// If we're already requesting this node, add a new reference and stop
// If we're already requesting this node, add a new reference and stop
if
old
,
ok
:=
reqset
[
req
.
hash
];
ok
{
if
old
,
ok
:=
s
.
codeReqs
[
req
.
hash
];
ok
{
old
.
parents
=
append
(
old
.
parents
,
req
.
parents
...
)
old
.
parents
=
append
(
old
.
parents
,
req
.
parents
...
)
return
return
}
}
reqset
[
req
.
hash
]
=
req
s
.
codeReqs
[
req
.
hash
]
=
req
// Schedule the request for future retrieval. This queue is shared
// Schedule the request for future retrieval. This queue is shared
// by both node requests and code requests. It can happen that there
// by both node requests and code requests.
// is a trie node and code has same hash. In this case two elements
// with same hash and same or different depth will be pushed. But it's
// ok the worst case is the second response will be treated as duplicated.
prio
:=
int64
(
len
(
req
.
path
))
<<
56
// depth >= 128 will never happen, storage leaves will be included in their parents
prio
:=
int64
(
len
(
req
.
path
))
<<
56
// depth >= 128 will never happen, storage leaves will be included in their parents
for
i
:=
0
;
i
<
14
&&
i
<
len
(
req
.
path
);
i
++
{
for
i
:=
0
;
i
<
14
&&
i
<
len
(
req
.
path
);
i
++
{
prio
|=
int64
(
15
-
req
.
path
[
i
])
<<
(
52
-
i
*
4
)
// 15-nibble => lexicographic order
prio
|=
int64
(
15
-
req
.
path
[
i
])
<<
(
52
-
i
*
4
)
// 15-nibble => lexicographic order
...
@@ -343,7 +380,7 @@ func (s *Sync) schedule(req *request) {
...
@@ -343,7 +380,7 @@ func (s *Sync) schedule(req *request) {
// children retrieves all the missing children of a state trie entry for future
// children retrieves all the missing children of a state trie entry for future
// retrieval scheduling.
// retrieval scheduling.
func
(
s
*
Sync
)
children
(
req
*
request
,
object
node
)
([]
*
r
equest
,
error
)
{
func
(
s
*
Sync
)
children
(
req
*
nodeRequest
,
object
node
)
([]
*
nodeR
equest
,
error
)
{
// Gather all the children of the node, irrelevant whether known or not
// Gather all the children of the node, irrelevant whether known or not
type
child
struct
{
type
child
struct
{
path
[]
byte
path
[]
byte
...
@@ -374,7 +411,7 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
...
@@ -374,7 +411,7 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
panic
(
fmt
.
Sprintf
(
"unknown node: %+v"
,
node
))
panic
(
fmt
.
Sprintf
(
"unknown node: %+v"
,
node
))
}
}
// Iterate over the children, and request all unknown ones
// Iterate over the children, and request all unknown ones
requests
:=
make
([]
*
r
equest
,
0
,
len
(
children
))
requests
:=
make
([]
*
nodeR
equest
,
0
,
len
(
children
))
for
_
,
child
:=
range
children
{
for
_
,
child
:=
range
children
{
// Notify any external watcher of a new key/value node
// Notify any external watcher of a new key/value node
if
req
.
callback
!=
nil
{
if
req
.
callback
!=
nil
{
...
@@ -386,7 +423,7 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
...
@@ -386,7 +423,7 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
paths
=
append
(
paths
,
hexToKeybytes
(
child
.
path
[
:
2
*
common
.
HashLength
]))
paths
=
append
(
paths
,
hexToKeybytes
(
child
.
path
[
:
2
*
common
.
HashLength
]))
paths
=
append
(
paths
,
hexToKeybytes
(
child
.
path
[
2
*
common
.
HashLength
:
]))
paths
=
append
(
paths
,
hexToKeybytes
(
child
.
path
[
2
*
common
.
HashLength
:
]))
}
}
if
err
:=
req
.
callback
(
paths
,
child
.
path
,
node
,
req
.
hash
);
err
!=
nil
{
if
err
:=
req
.
callback
(
paths
,
child
.
path
,
node
,
req
.
hash
,
req
.
path
);
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
}
}
...
@@ -394,20 +431,20 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
...
@@ -394,20 +431,20 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
// If the child references another node, resolve or schedule
// If the child references another node, resolve or schedule
if
node
,
ok
:=
(
child
.
node
)
.
(
hashNode
);
ok
{
if
node
,
ok
:=
(
child
.
node
)
.
(
hashNode
);
ok
{
// Try to resolve the node from the local database
// Try to resolve the node from the local database
hash
:=
common
.
BytesToHash
(
node
)
if
s
.
membatch
.
hasNode
(
child
.
path
)
{
if
s
.
membatch
.
hasNode
(
hash
)
{
continue
continue
}
}
// If database says duplicate, then at least the trie node is present
// If database says duplicate, then at least the trie node is present
// and we hold the assumption that it's NOT legacy contract code.
// and we hold the assumption that it's NOT legacy contract code.
if
rawdb
.
HasTrieNode
(
s
.
database
,
hash
)
{
chash
:=
common
.
BytesToHash
(
node
)
if
rawdb
.
HasTrieNode
(
s
.
database
,
chash
)
{
continue
continue
}
}
// Locally unknown node, schedule for retrieval
// Locally unknown node, schedule for retrieval
requests
=
append
(
requests
,
&
r
equest
{
requests
=
append
(
requests
,
&
nodeR
equest
{
path
:
child
.
path
,
path
:
child
.
path
,
hash
:
hash
,
hash
:
c
hash
,
parent
s
:
[]
*
request
{
req
}
,
parent
:
req
,
callback
:
req
.
callback
,
callback
:
req
.
callback
,
})
})
}
}
...
@@ -418,22 +455,40 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
...
@@ -418,22 +455,40 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
// commit finalizes a retrieval request and stores it into the membatch. If any
// commit finalizes a retrieval request and stores it into the membatch. If any
// of the referencing parent requests complete due to this commit, they are also
// of the referencing parent requests complete due to this commit, they are also
// committed themselves.
// committed themselves.
func
(
s
*
Sync
)
commit
(
req
*
request
)
(
err
error
)
{
func
(
s
*
Sync
)
commit
NodeRequest
(
req
*
nodeRequest
)
error
{
// Write the node content to the membatch
// Write the node content to the membatch
if
req
.
code
{
s
.
membatch
.
nodes
[
string
(
req
.
path
)]
=
req
.
data
s
.
membatch
.
codes
[
req
.
hash
]
=
req
.
data
s
.
membatch
.
hashes
[
string
(
req
.
path
)]
=
req
.
hash
delete
(
s
.
codeReqs
,
req
.
hash
)
s
.
fetches
[
len
(
req
.
path
)]
--
delete
(
s
.
nodeReqs
,
string
(
req
.
path
))
}
else
{
s
.
fetches
[
len
(
req
.
path
)]
--
s
.
membatch
.
nodes
[
req
.
hash
]
=
req
.
data
delete
(
s
.
nodeReqs
,
req
.
hash
)
// Check parent for completion
s
.
fetches
[
len
(
req
.
path
)]
--
if
req
.
parent
!=
nil
{
req
.
parent
.
deps
--
if
req
.
parent
.
deps
==
0
{
if
err
:=
s
.
commitNodeRequest
(
req
.
parent
);
err
!=
nil
{
return
err
}
}
}
}
return
nil
}
// commit finalizes a retrieval request and stores it into the membatch. If any
// of the referencing parent requests complete due to this commit, they are also
// committed themselves.
func
(
s
*
Sync
)
commitCodeRequest
(
req
*
codeRequest
)
error
{
// Write the node content to the membatch
s
.
membatch
.
codes
[
req
.
hash
]
=
req
.
data
delete
(
s
.
codeReqs
,
req
.
hash
)
s
.
fetches
[
len
(
req
.
path
)]
--
// Check all parents for completion
// Check all parents for completion
for
_
,
parent
:=
range
req
.
parents
{
for
_
,
parent
:=
range
req
.
parents
{
parent
.
deps
--
parent
.
deps
--
if
parent
.
deps
==
0
{
if
parent
.
deps
==
0
{
if
err
:=
s
.
commit
(
parent
);
err
!=
nil
{
if
err
:=
s
.
commit
NodeRequest
(
parent
);
err
!=
nil
{
return
err
return
err
}
}
}
}
...
...
trie/sync_test.go
View file @
1657e439
...
@@ -87,6 +87,13 @@ func checkTrieConsistency(db *Database, root common.Hash) error {
...
@@ -87,6 +87,13 @@ func checkTrieConsistency(db *Database, root common.Hash) error {
return
it
.
Error
()
return
it
.
Error
()
}
}
// trieElement represents the element in the state trie(bytecode or trie node).
type
trieElement
struct
{
path
string
hash
common
.
Hash
syncPath
SyncPath
}
// Tests that an empty trie is not scheduled for syncing.
// Tests that an empty trie is not scheduled for syncing.
func
TestEmptySync
(
t
*
testing
.
T
)
{
func
TestEmptySync
(
t
*
testing
.
T
)
{
dbA
:=
NewDatabase
(
memorydb
.
New
())
dbA
:=
NewDatabase
(
memorydb
.
New
())
...
@@ -96,8 +103,8 @@ func TestEmptySync(t *testing.T) {
...
@@ -96,8 +103,8 @@ func TestEmptySync(t *testing.T) {
for
i
,
trie
:=
range
[]
*
Trie
{
emptyA
,
emptyB
}
{
for
i
,
trie
:=
range
[]
*
Trie
{
emptyA
,
emptyB
}
{
sync
:=
NewSync
(
trie
.
Hash
(),
memorydb
.
New
(),
nil
)
sync
:=
NewSync
(
trie
.
Hash
(),
memorydb
.
New
(),
nil
)
if
nodes
,
paths
,
codes
:=
sync
.
Missing
(
1
);
len
(
nodes
)
!=
0
||
len
(
path
s
)
!=
0
||
len
(
codes
)
!=
0
{
if
paths
,
nodes
,
codes
:=
sync
.
Missing
(
1
);
len
(
paths
)
!=
0
||
len
(
node
s
)
!=
0
||
len
(
codes
)
!=
0
{
t
.
Errorf
(
"test %d: content requested for empty trie: %v, %v, %v"
,
i
,
nodes
,
path
s
,
codes
)
t
.
Errorf
(
"test %d: content requested for empty trie: %v, %v, %v"
,
i
,
paths
,
node
s
,
codes
)
}
}
}
}
}
}
...
@@ -118,35 +125,38 @@ func testIterativeSync(t *testing.T, count int, bypath bool) {
...
@@ -118,35 +125,38 @@ func testIterativeSync(t *testing.T, count int, bypath bool) {
triedb
:=
NewDatabase
(
diskdb
)
triedb
:=
NewDatabase
(
diskdb
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
nodes
,
paths
,
codes
:=
sched
.
Missing
(
count
)
// The code requests are ignored here since there is no code
var
(
// at the testing trie.
hashQueue
[]
common
.
Hash
paths
,
nodes
,
_
:=
sched
.
Missing
(
count
)
pathQueue
[]
SyncPath
var
elements
[]
trieElement
)
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
if
!
bypath
{
elements
=
append
(
elements
,
trieElement
{
hashQueue
=
append
(
append
(
hashQueue
[
:
0
],
nodes
...
),
codes
...
)
path
:
paths
[
i
],
}
else
{
hash
:
nodes
[
i
],
hashQueue
=
append
(
hashQueue
[
:
0
],
codes
...
)
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
pathQueue
=
append
(
pathQueue
[
:
0
],
paths
...
)
}
)
}
}
for
len
(
hashQueue
)
+
len
(
pathQueue
)
>
0
{
for
len
(
elements
)
>
0
{
results
:=
make
([]
SyncResult
,
len
(
hashQueue
)
+
len
(
pathQueue
))
results
:=
make
([]
NodeSyncResult
,
len
(
elements
))
for
i
,
hash
:=
range
hashQueue
{
if
!
bypath
{
data
,
err
:=
srcDb
.
Node
(
hash
)
for
i
,
element
:=
range
elements
{
if
err
!=
nil
{
data
,
err
:=
srcDb
.
Node
(
element
.
hash
)
t
.
Fatalf
(
"failed to retrieve node data for hash %x: %v"
,
hash
,
err
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for hash %x: %v"
,
element
.
hash
,
err
)
}
results
[
i
]
=
NodeSyncResult
{
element
.
path
,
data
}
}
}
results
[
i
]
=
SyncResult
{
hash
,
data
}
}
else
{
}
for
i
,
element
:=
range
elements
{
for
i
,
path
:=
range
pathQueue
{
data
,
_
,
err
:=
srcTrie
.
TryGetNode
(
element
.
syncPath
[
len
(
element
.
syncPath
)
-
1
])
data
,
_
,
err
:=
srcTrie
.
TryGetNode
(
path
[
0
])
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for path %x: %v"
,
element
.
path
,
err
)
t
.
Fatalf
(
"failed to retrieve node data for path %x: %v"
,
path
,
err
)
}
results
[
i
]
=
NodeSyncResult
{
element
.
path
,
data
}
}
}
results
[
len
(
hashQueue
)
+
i
]
=
SyncResult
{
crypto
.
Keccak256Hash
(
data
),
data
}
}
}
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
if
err
:=
sched
.
Process
Node
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
...
@@ -156,12 +166,14 @@ func testIterativeSync(t *testing.T, count int, bypath bool) {
...
@@ -156,12 +166,14 @@ func testIterativeSync(t *testing.T, count int, bypath bool) {
}
}
batch
.
Write
()
batch
.
Write
()
nodes
,
paths
,
codes
=
sched
.
Missing
(
count
)
paths
,
nodes
,
_
=
sched
.
Missing
(
count
)
if
!
bypath
{
elements
=
elements
[
:
0
]
hashQueue
=
append
(
append
(
hashQueue
[
:
0
],
nodes
...
),
codes
...
)
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
}
else
{
elements
=
append
(
elements
,
trieElement
{
hashQueue
=
append
(
hashQueue
[
:
0
],
codes
...
)
path
:
paths
[
i
],
pathQueue
=
append
(
pathQueue
[
:
0
],
paths
...
)
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
}
}
}
// Cross check that the two tries are in sync
// Cross check that the two tries are in sync
...
@@ -179,21 +191,29 @@ func TestIterativeDelayedSync(t *testing.T) {
...
@@ -179,21 +191,29 @@ func TestIterativeDelayedSync(t *testing.T) {
triedb
:=
NewDatabase
(
diskdb
)
triedb
:=
NewDatabase
(
diskdb
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
nodes
,
_
,
codes
:=
sched
.
Missing
(
10000
)
// The code requests are ignored here since there is no code
queue
:=
append
(
append
([]
common
.
Hash
{},
nodes
...
),
codes
...
)
// at the testing trie.
paths
,
nodes
,
_
:=
sched
.
Missing
(
10000
)
for
len
(
queue
)
>
0
{
var
elements
[]
trieElement
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
elements
=
append
(
elements
,
trieElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
for
len
(
elements
)
>
0
{
// Sync only half of the scheduled nodes
// Sync only half of the scheduled nodes
results
:=
make
([]
SyncResult
,
len
(
queue
)
/
2
+
1
)
results
:=
make
([]
NodeSyncResult
,
len
(
elements
)
/
2
+
1
)
for
i
,
hash
:=
range
queue
[
:
len
(
results
)]
{
for
i
,
element
:=
range
elements
[
:
len
(
results
)]
{
data
,
err
:=
srcDb
.
Node
(
hash
)
data
,
err
:=
srcDb
.
Node
(
element
.
hash
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
hash
,
err
)
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
element
.
hash
,
err
)
}
}
results
[
i
]
=
SyncResult
{
has
h
,
data
}
results
[
i
]
=
NodeSyncResult
{
element
.
pat
h
,
data
}
}
}
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
if
err
:=
sched
.
Process
Node
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
...
@@ -203,8 +223,15 @@ func TestIterativeDelayedSync(t *testing.T) {
...
@@ -203,8 +223,15 @@ func TestIterativeDelayedSync(t *testing.T) {
}
}
batch
.
Write
()
batch
.
Write
()
nodes
,
_
,
codes
=
sched
.
Missing
(
10000
)
paths
,
nodes
,
_
=
sched
.
Missing
(
10000
)
queue
=
append
(
append
(
queue
[
len
(
results
)
:
],
nodes
...
),
codes
...
)
elements
=
elements
[
len
(
results
)
:
]
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
elements
=
append
(
elements
,
trieElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
}
}
// Cross check that the two tries are in sync
// Cross check that the two tries are in sync
checkTrieContents
(
t
,
triedb
,
srcTrie
.
Hash
()
.
Bytes
(),
srcData
)
checkTrieContents
(
t
,
triedb
,
srcTrie
.
Hash
()
.
Bytes
(),
srcData
)
...
@@ -225,24 +252,30 @@ func testIterativeRandomSync(t *testing.T, count int) {
...
@@ -225,24 +252,30 @@ func testIterativeRandomSync(t *testing.T, count int) {
triedb
:=
NewDatabase
(
diskdb
)
triedb
:=
NewDatabase
(
diskdb
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
queue
:=
make
(
map
[
common
.
Hash
]
struct
{})
// The code requests are ignored here since there is no code
nodes
,
_
,
codes
:=
sched
.
Missing
(
count
)
// at the testing trie.
for
_
,
hash
:=
range
append
(
nodes
,
codes
...
)
{
paths
,
nodes
,
_
:=
sched
.
Missing
(
count
)
queue
[
hash
]
=
struct
{}{}
queue
:=
make
(
map
[
string
]
trieElement
)
for
i
,
path
:=
range
paths
{
queue
[
path
]
=
trieElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
}
}
}
for
len
(
queue
)
>
0
{
for
len
(
queue
)
>
0
{
// Fetch all the queued nodes in a random order
// Fetch all the queued nodes in a random order
results
:=
make
([]
SyncResult
,
0
,
len
(
queue
))
results
:=
make
([]
Node
SyncResult
,
0
,
len
(
queue
))
for
hash
:=
range
queue
{
for
path
,
element
:=
range
queue
{
data
,
err
:=
srcDb
.
Node
(
hash
)
data
,
err
:=
srcDb
.
Node
(
element
.
hash
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
hash
,
err
)
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
element
.
hash
,
err
)
}
}
results
=
append
(
results
,
SyncResult
{
has
h
,
data
})
results
=
append
(
results
,
NodeSyncResult
{
pat
h
,
data
})
}
}
// Feed the retrieved results back and queue new tasks
// Feed the retrieved results back and queue new tasks
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
if
err
:=
sched
.
Process
Node
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
...
@@ -252,10 +285,14 @@ func testIterativeRandomSync(t *testing.T, count int) {
...
@@ -252,10 +285,14 @@ func testIterativeRandomSync(t *testing.T, count int) {
}
}
batch
.
Write
()
batch
.
Write
()
queue
=
make
(
map
[
common
.
Hash
]
struct
{})
paths
,
nodes
,
_
=
sched
.
Missing
(
count
)
nodes
,
_
,
codes
=
sched
.
Missing
(
count
)
queue
=
make
(
map
[
string
]
trieElement
)
for
_
,
hash
:=
range
append
(
nodes
,
codes
...
)
{
for
i
,
path
:=
range
paths
{
queue
[
hash
]
=
struct
{}{}
queue
[
path
]
=
trieElement
{
path
:
path
,
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
path
)),
}
}
}
}
}
// Cross check that the two tries are in sync
// Cross check that the two tries are in sync
...
@@ -273,20 +310,26 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
...
@@ -273,20 +310,26 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
triedb
:=
NewDatabase
(
diskdb
)
triedb
:=
NewDatabase
(
diskdb
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
queue
:=
make
(
map
[
common
.
Hash
]
struct
{})
// The code requests are ignored here since there is no code
nodes
,
_
,
codes
:=
sched
.
Missing
(
10000
)
// at the testing trie.
for
_
,
hash
:=
range
append
(
nodes
,
codes
...
)
{
paths
,
nodes
,
_
:=
sched
.
Missing
(
10000
)
queue
[
hash
]
=
struct
{}{}
queue
:=
make
(
map
[
string
]
trieElement
)
for
i
,
path
:=
range
paths
{
queue
[
path
]
=
trieElement
{
path
:
path
,
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
path
)),
}
}
}
for
len
(
queue
)
>
0
{
for
len
(
queue
)
>
0
{
// Sync only half of the scheduled nodes, even those in random order
// Sync only half of the scheduled nodes, even those in random order
results
:=
make
([]
SyncResult
,
0
,
len
(
queue
)
/
2
+
1
)
results
:=
make
([]
Node
SyncResult
,
0
,
len
(
queue
)
/
2
+
1
)
for
hash
:=
range
queue
{
for
path
,
element
:=
range
queue
{
data
,
err
:=
srcDb
.
Node
(
hash
)
data
,
err
:=
srcDb
.
Node
(
element
.
hash
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
hash
,
err
)
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
element
.
hash
,
err
)
}
}
results
=
append
(
results
,
SyncResult
{
has
h
,
data
})
results
=
append
(
results
,
NodeSyncResult
{
pat
h
,
data
})
if
len
(
results
)
>=
cap
(
results
)
{
if
len
(
results
)
>=
cap
(
results
)
{
break
break
...
@@ -294,7 +337,7 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
...
@@ -294,7 +337,7 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
}
}
// Feed the retrieved results back and queue new tasks
// Feed the retrieved results back and queue new tasks
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
if
err
:=
sched
.
Process
Node
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
...
@@ -304,11 +347,15 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
...
@@ -304,11 +347,15 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
}
}
batch
.
Write
()
batch
.
Write
()
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
delete
(
queue
,
result
.
Hash
)
delete
(
queue
,
result
.
Path
)
}
}
nodes
,
_
,
codes
=
sched
.
Missing
(
10000
)
paths
,
nodes
,
_
=
sched
.
Missing
(
10000
)
for
_
,
hash
:=
range
append
(
nodes
,
codes
...
)
{
for
i
,
path
:=
range
paths
{
queue
[
hash
]
=
struct
{}{}
queue
[
path
]
=
trieElement
{
path
:
path
,
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
path
)),
}
}
}
}
}
// Cross check that the two tries are in sync
// Cross check that the two tries are in sync
...
@@ -326,26 +373,35 @@ func TestDuplicateAvoidanceSync(t *testing.T) {
...
@@ -326,26 +373,35 @@ func TestDuplicateAvoidanceSync(t *testing.T) {
triedb
:=
NewDatabase
(
diskdb
)
triedb
:=
NewDatabase
(
diskdb
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
nodes
,
_
,
codes
:=
sched
.
Missing
(
0
)
// The code requests are ignored here since there is no code
queue
:=
append
(
append
([]
common
.
Hash
{},
nodes
...
),
codes
...
)
// at the testing trie.
paths
,
nodes
,
_
:=
sched
.
Missing
(
0
)
var
elements
[]
trieElement
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
elements
=
append
(
elements
,
trieElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
requested
:=
make
(
map
[
common
.
Hash
]
struct
{})
requested
:=
make
(
map
[
common
.
Hash
]
struct
{})
for
len
(
queue
)
>
0
{
for
len
(
elements
)
>
0
{
results
:=
make
([]
SyncResult
,
len
(
queue
))
results
:=
make
([]
NodeSyncResult
,
len
(
elements
))
for
i
,
hash
:=
range
queue
{
for
i
,
element
:=
range
elements
{
data
,
err
:=
srcDb
.
Node
(
hash
)
data
,
err
:=
srcDb
.
Node
(
element
.
hash
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
hash
,
err
)
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
element
.
hash
,
err
)
}
}
if
_
,
ok
:=
requested
[
hash
];
ok
{
if
_
,
ok
:=
requested
[
element
.
hash
];
ok
{
t
.
Errorf
(
"hash %x already requested once"
,
hash
)
t
.
Errorf
(
"hash %x already requested once"
,
element
.
hash
)
}
}
requested
[
hash
]
=
struct
{}{}
requested
[
element
.
hash
]
=
struct
{}{}
results
[
i
]
=
SyncResult
{
has
h
,
data
}
results
[
i
]
=
NodeSyncResult
{
element
.
pat
h
,
data
}
}
}
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
if
err
:=
sched
.
Process
Node
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
...
@@ -355,8 +411,15 @@ func TestDuplicateAvoidanceSync(t *testing.T) {
...
@@ -355,8 +411,15 @@ func TestDuplicateAvoidanceSync(t *testing.T) {
}
}
batch
.
Write
()
batch
.
Write
()
nodes
,
_
,
codes
=
sched
.
Missing
(
0
)
paths
,
nodes
,
_
=
sched
.
Missing
(
0
)
queue
=
append
(
append
(
queue
[
:
0
],
nodes
...
),
codes
...
)
elements
=
elements
[
:
0
]
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
elements
=
append
(
elements
,
trieElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
}
}
// Cross check that the two tries are in sync
// Cross check that the two tries are in sync
checkTrieContents
(
t
,
triedb
,
srcTrie
.
Hash
()
.
Bytes
(),
srcData
)
checkTrieContents
(
t
,
triedb
,
srcTrie
.
Hash
()
.
Bytes
(),
srcData
)
...
@@ -373,23 +436,34 @@ func TestIncompleteSync(t *testing.T) {
...
@@ -373,23 +436,34 @@ func TestIncompleteSync(t *testing.T) {
triedb
:=
NewDatabase
(
diskdb
)
triedb
:=
NewDatabase
(
diskdb
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
var
added
[]
common
.
Hash
// The code requests are ignored here since there is no code
// at the testing trie.
nodes
,
_
,
codes
:=
sched
.
Missing
(
1
)
var
(
queue
:=
append
(
append
([]
common
.
Hash
{},
nodes
...
),
codes
...
)
added
[]
common
.
Hash
for
len
(
queue
)
>
0
{
elements
[]
trieElement
root
=
srcTrie
.
Hash
()
)
paths
,
nodes
,
_
:=
sched
.
Missing
(
1
)
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
elements
=
append
(
elements
,
trieElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
for
len
(
elements
)
>
0
{
// Fetch a batch of trie nodes
// Fetch a batch of trie nodes
results
:=
make
([]
SyncResult
,
len
(
queue
))
results
:=
make
([]
NodeSyncResult
,
len
(
elements
))
for
i
,
hash
:=
range
queue
{
for
i
,
element
:=
range
elements
{
data
,
err
:=
srcDb
.
Node
(
hash
)
data
,
err
:=
srcDb
.
Node
(
element
.
hash
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
hash
,
err
)
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
element
.
hash
,
err
)
}
}
results
[
i
]
=
SyncResult
{
has
h
,
data
}
results
[
i
]
=
NodeSyncResult
{
element
.
pat
h
,
data
}
}
}
// Process each of the trie nodes
// Process each of the trie nodes
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
if
err
:=
sched
.
Process
Node
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
...
@@ -398,27 +472,36 @@ func TestIncompleteSync(t *testing.T) {
...
@@ -398,27 +472,36 @@ func TestIncompleteSync(t *testing.T) {
t
.
Fatalf
(
"failed to commit data: %v"
,
err
)
t
.
Fatalf
(
"failed to commit data: %v"
,
err
)
}
}
batch
.
Write
()
batch
.
Write
()
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
added
=
append
(
added
,
result
.
Hash
)
hash
:=
crypto
.
Keccak256Hash
(
result
.
Data
)
if
hash
!=
root
{
added
=
append
(
added
,
hash
)
}
// Check that all known sub-tries in the synced trie are complete
// Check that all known sub-tries in the synced trie are complete
if
err
:=
checkTrieConsistency
(
triedb
,
result
.
H
ash
);
err
!=
nil
{
if
err
:=
checkTrieConsistency
(
triedb
,
h
ash
);
err
!=
nil
{
t
.
Fatalf
(
"trie inconsistent: %v"
,
err
)
t
.
Fatalf
(
"trie inconsistent: %v"
,
err
)
}
}
}
}
// Fetch the next batch to retrieve
// Fetch the next batch to retrieve
nodes
,
_
,
codes
=
sched
.
Missing
(
1
)
paths
,
nodes
,
_
=
sched
.
Missing
(
1
)
queue
=
append
(
append
(
queue
[
:
0
],
nodes
...
),
codes
...
)
elements
=
elements
[
:
0
]
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
elements
=
append
(
elements
,
trieElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
})
}
}
}
// Sanity check that removing any node from the database is detected
// Sanity check that removing any node from the database is detected
for
_
,
node
:=
range
added
[
1
:
]
{
for
_
,
hash
:=
range
added
{
key
:=
node
.
Bytes
()
value
,
_
:=
diskdb
.
Get
(
hash
.
Bytes
())
value
,
_
:=
diskdb
.
Get
(
key
)
diskdb
.
Delete
(
hash
.
Bytes
())
if
err
:=
checkTrieConsistency
(
triedb
,
root
);
err
==
nil
{
diskdb
.
Delete
(
key
)
t
.
Fatalf
(
"trie inconsistency not caught, missing: %x"
,
hash
)
if
err
:=
checkTrieConsistency
(
triedb
,
added
[
0
]);
err
==
nil
{
t
.
Fatalf
(
"trie inconsistency not caught, missing: %x"
,
key
)
}
}
diskdb
.
Put
(
key
,
value
)
diskdb
.
Put
(
hash
.
Bytes
()
,
value
)
}
}
}
}
...
@@ -433,21 +516,33 @@ func TestSyncOrdering(t *testing.T) {
...
@@ -433,21 +516,33 @@ func TestSyncOrdering(t *testing.T) {
triedb
:=
NewDatabase
(
diskdb
)
triedb
:=
NewDatabase
(
diskdb
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
sched
:=
NewSync
(
srcTrie
.
Hash
(),
diskdb
,
nil
)
nodes
,
paths
,
_
:=
sched
.
Missing
(
1
)
// The code requests are ignored here since there is no code
queue
:=
append
([]
common
.
Hash
{},
nodes
...
)
// at the testing trie.
reqs
:=
append
([]
SyncPath
{},
paths
...
)
var
(
reqs
[]
SyncPath
elements
[]
trieElement
)
paths
,
nodes
,
_
:=
sched
.
Missing
(
1
)
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
elements
=
append
(
elements
,
trieElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
})
reqs
=
append
(
reqs
,
NewSyncPath
([]
byte
(
paths
[
i
])))
}
for
len
(
queue
)
>
0
{
for
len
(
elements
)
>
0
{
results
:=
make
([]
SyncResult
,
len
(
queue
))
results
:=
make
([]
NodeSyncResult
,
len
(
elements
))
for
i
,
hash
:=
range
queue
{
for
i
,
element
:=
range
elements
{
data
,
err
:=
srcDb
.
Node
(
hash
)
data
,
err
:=
srcDb
.
Node
(
element
.
hash
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
hash
,
err
)
t
.
Fatalf
(
"failed to retrieve node data for %x: %v"
,
element
.
hash
,
err
)
}
}
results
[
i
]
=
SyncResult
{
has
h
,
data
}
results
[
i
]
=
NodeSyncResult
{
element
.
pat
h
,
data
}
}
}
for
_
,
result
:=
range
results
{
for
_
,
result
:=
range
results
{
if
err
:=
sched
.
Process
(
result
);
err
!=
nil
{
if
err
:=
sched
.
Process
Node
(
result
);
err
!=
nil
{
t
.
Fatalf
(
"failed to process result %v"
,
err
)
t
.
Fatalf
(
"failed to process result %v"
,
err
)
}
}
}
}
...
@@ -457,9 +552,16 @@ func TestSyncOrdering(t *testing.T) {
...
@@ -457,9 +552,16 @@ func TestSyncOrdering(t *testing.T) {
}
}
batch
.
Write
()
batch
.
Write
()
nodes
,
paths
,
_
=
sched
.
Missing
(
1
)
paths
,
nodes
,
_
=
sched
.
Missing
(
1
)
queue
=
append
(
queue
[
:
0
],
nodes
...
)
elements
=
elements
[
:
0
]
reqs
=
append
(
reqs
,
paths
...
)
for
i
:=
0
;
i
<
len
(
paths
);
i
++
{
elements
=
append
(
elements
,
trieElement
{
path
:
paths
[
i
],
hash
:
nodes
[
i
],
syncPath
:
NewSyncPath
([]
byte
(
paths
[
i
])),
})
reqs
=
append
(
reqs
,
NewSyncPath
([]
byte
(
paths
[
i
])))
}
}
}
// Cross check that the two tries are in sync
// Cross check that the two tries are in sync
checkTrieContents
(
t
,
triedb
,
srcTrie
.
Hash
()
.
Bytes
(),
srcData
)
checkTrieContents
(
t
,
triedb
,
srcTrie
.
Hash
()
.
Bytes
(),
srcData
)
...
...
trie/trie.go
View file @
1657e439
...
@@ -42,18 +42,18 @@ var (
...
@@ -42,18 +42,18 @@ var (
// LeafCallback is a callback type invoked when a trie operation reaches a leaf
// LeafCallback is a callback type invoked when a trie operation reaches a leaf
// node.
// node.
//
//
// The
path
s is a path tuple identifying a particular trie node either in a single
// The
key
s is a path tuple identifying a particular trie node either in a single
// trie (account) or a layered trie (account -> storage). Each
path
in the tuple
// trie (account) or a layered trie (account -> storage). Each
key
in the tuple
// is in the raw format(32 bytes).
// is in the raw format(32 bytes).
//
//
// The
hex
path is a composite hexary path identifying the trie node. All the key
// The path is a composite hexary path identifying the trie node. All the key
// bytes are converted to the hexary nibbles and composited with the parent path
// bytes are converted to the hexary nibbles and composited with the parent path
// if the trie node is in a layered trie.
// if the trie node is in a layered trie.
//
//
// It's used by state sync and commit to allow handling external references
// It's used by state sync and commit to allow handling external references
// between account and storage tries. And also it's used in the state healing
// between account and storage tries. And also it's used in the state healing
// for extracting the raw states(leaf nodes) with corresponding paths.
// for extracting the raw states(leaf nodes) with corresponding paths.
type
LeafCallback
func
(
paths
[][]
byte
,
hexpath
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
)
error
type
LeafCallback
func
(
keys
[][]
byte
,
path
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
,
parentPath
[]
byte
)
error
// Trie is a Merkle Patricia Trie.
// Trie is a Merkle Patricia Trie.
// The zero value is an empty trie with no database.
// The zero value is an empty trie with no database.
...
...
trie/trie_test.go
View file @
1657e439
...
@@ -625,7 +625,7 @@ func BenchmarkCommitAfterHash(b *testing.B) {
...
@@ -625,7 +625,7 @@ func BenchmarkCommitAfterHash(b *testing.B) {
benchmarkCommitAfterHash
(
b
,
nil
)
benchmarkCommitAfterHash
(
b
,
nil
)
})
})
var
a
types
.
StateAccount
var
a
types
.
StateAccount
onleaf
:=
func
(
paths
[][]
byte
,
hexpath
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
)
error
{
onleaf
:=
func
(
paths
[][]
byte
,
hexpath
[]
byte
,
leaf
[]
byte
,
parent
common
.
Hash
,
parentPath
[]
byte
)
error
{
rlp
.
DecodeBytes
(
leaf
,
&
a
)
rlp
.
DecodeBytes
(
leaf
,
&
a
)
return
nil
return
nil
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment