good morning!!!!
Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
B
bor
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Harbor Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
open
bor
Commits
a4246c2d
Commit
a4246c2d
authored
May 14, 2015
by
Péter Szilágyi
Browse files
Options
Downloads
Patches
Plain Diff
eth, eth/downloader: handle a potential unknown parent attack
parent
7cb0e242
No related branches found
No related tags found
No related merge requests found
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
eth/downloader/downloader.go
+12
-8
12 additions, 8 deletions
eth/downloader/downloader.go
eth/downloader/downloader_test.go
+83
-17
83 additions, 17 deletions
eth/downloader/downloader_test.go
eth/sync.go
+17
-6
17 additions, 6 deletions
eth/sync.go
with
112 additions
and
31 deletions
eth/downloader/downloader.go
+
12
−
8
View file @
a4246c2d
...
@@ -37,6 +37,7 @@ var (
...
@@ -37,6 +37,7 @@ var (
errCancelHashFetch
=
errors
.
New
(
"hash fetching cancelled (requested)"
)
errCancelHashFetch
=
errors
.
New
(
"hash fetching cancelled (requested)"
)
errCancelBlockFetch
=
errors
.
New
(
"block downloading cancelled (requested)"
)
errCancelBlockFetch
=
errors
.
New
(
"block downloading cancelled (requested)"
)
errNoSyncActive
=
errors
.
New
(
"no sync active"
)
errNoSyncActive
=
errors
.
New
(
"no sync active"
)
ErrUnknownParent
=
errors
.
New
(
"block has unknown parent"
)
)
)
type
hashCheckFn
func
(
common
.
Hash
)
bool
type
hashCheckFn
func
(
common
.
Hash
)
bool
...
@@ -142,16 +143,19 @@ func (d *Downloader) Synchronise(id string, hash common.Hash) error {
...
@@ -142,16 +143,19 @@ func (d *Downloader) Synchronise(id string, hash common.Hash) error {
return
d
.
syncWithPeer
(
p
,
hash
)
return
d
.
syncWithPeer
(
p
,
hash
)
}
}
// TakeBlocks takes blocks from the queue and yields them to the blockTaker handler
// TakeBlocks takes blocks from the queue and yields them to the caller.
// it's possible it yields no blocks
func
(
d
*
Downloader
)
TakeBlocks
()
(
types
.
Blocks
,
error
)
{
func
(
d
*
Downloader
)
TakeBlocks
()
types
.
Blocks
{
// If the head block is missing, no blocks are ready
// Check that there are blocks available and its parents are known
head
:=
d
.
queue
.
GetHeadBlock
()
head
:=
d
.
queue
.
GetHeadBlock
()
if
head
==
nil
||
!
d
.
hasBlock
(
head
.
ParentHash
())
{
if
head
==
nil
{
return
nil
return
nil
,
nil
}
// If the parent hash of the head is unknown, notify the caller
if
!
d
.
hasBlock
(
head
.
ParentHash
())
{
return
nil
,
ErrUnknownParent
}
}
//
R
etrieve a full batch of blocks
//
Otherwise r
etrieve a full batch of blocks
return
d
.
queue
.
TakeBlocks
(
head
)
return
d
.
queue
.
TakeBlocks
(
head
)
,
nil
}
}
func
(
d
*
Downloader
)
Has
(
hash
common
.
Hash
)
bool
{
func
(
d
*
Downloader
)
Has
(
hash
common
.
Hash
)
bool
{
...
...
This diff is collapsed.
Click to expand it.
eth/downloader/downloader_test.go
+
83
−
17
View file @
a4246c2d
...
@@ -10,7 +10,10 @@ import (
...
@@ -10,7 +10,10 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
)
)
var
knownHash
=
common
.
Hash
{
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
}
var
(
knownHash
=
common
.
Hash
{
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
}
unknownHash
=
common
.
Hash
{
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
,
9
}
)
func
createHashes
(
start
,
amount
int
)
(
hashes
[]
common
.
Hash
)
{
func
createHashes
(
start
,
amount
int
)
(
hashes
[]
common
.
Hash
)
{
hashes
=
make
([]
common
.
Hash
,
amount
+
1
)
hashes
=
make
([]
common
.
Hash
,
amount
+
1
)
...
@@ -27,7 +30,7 @@ func createBlock(i int, prevHash, hash common.Hash) *types.Block {
...
@@ -27,7 +30,7 @@ func createBlock(i int, prevHash, hash common.Hash) *types.Block {
header
:=
&
types
.
Header
{
Number
:
big
.
NewInt
(
int64
(
i
))}
header
:=
&
types
.
Header
{
Number
:
big
.
NewInt
(
int64
(
i
))}
block
:=
types
.
NewBlockWithHeader
(
header
)
block
:=
types
.
NewBlockWithHeader
(
header
)
block
.
HeaderHash
=
hash
block
.
HeaderHash
=
hash
block
.
ParentHeaderHash
=
known
Hash
block
.
ParentHeaderHash
=
prev
Hash
return
block
return
block
}
}
...
@@ -43,8 +46,11 @@ func createBlocksFromHashes(hashes []common.Hash) map[common.Hash]*types.Block {
...
@@ -43,8 +46,11 @@ func createBlocksFromHashes(hashes []common.Hash) map[common.Hash]*types.Block {
type
downloadTester
struct
{
type
downloadTester
struct
{
downloader
*
Downloader
downloader
*
Downloader
hashes
[]
common
.
Hash
blocks
map
[
common
.
Hash
]
*
types
.
Block
hashes
[]
common
.
Hash
// Chain of hashes simulating
blocks
map
[
common
.
Hash
]
*
types
.
Block
// Blocks associated with the hashes
chain
[]
common
.
Hash
// Block-chain being constructed
t
*
testing
.
T
t
*
testing
.
T
pcount
int
pcount
int
done
chan
bool
done
chan
bool
...
@@ -52,7 +58,15 @@ type downloadTester struct {
...
@@ -52,7 +58,15 @@ type downloadTester struct {
}
}
func
newTester
(
t
*
testing
.
T
,
hashes
[]
common
.
Hash
,
blocks
map
[
common
.
Hash
]
*
types
.
Block
)
*
downloadTester
{
func
newTester
(
t
*
testing
.
T
,
hashes
[]
common
.
Hash
,
blocks
map
[
common
.
Hash
]
*
types
.
Block
)
*
downloadTester
{
tester
:=
&
downloadTester
{
t
:
t
,
hashes
:
hashes
,
blocks
:
blocks
,
done
:
make
(
chan
bool
)}
tester
:=
&
downloadTester
{
t
:
t
,
hashes
:
hashes
,
blocks
:
blocks
,
chain
:
[]
common
.
Hash
{
knownHash
},
done
:
make
(
chan
bool
),
}
downloader
:=
New
(
tester
.
hasBlock
,
tester
.
getBlock
)
downloader
:=
New
(
tester
.
hasBlock
,
tester
.
getBlock
)
tester
.
downloader
=
downloader
tester
.
downloader
=
downloader
...
@@ -64,10 +78,18 @@ func (dl *downloadTester) sync(peerId string, hash common.Hash) error {
...
@@ -64,10 +78,18 @@ func (dl *downloadTester) sync(peerId string, hash common.Hash) error {
return
dl
.
downloader
.
Synchronise
(
peerId
,
hash
)
return
dl
.
downloader
.
Synchronise
(
peerId
,
hash
)
}
}
func
(
dl
*
downloadTester
)
insertBlocks
(
blocks
types
.
Blocks
)
{
for
_
,
block
:=
range
blocks
{
dl
.
chain
=
append
(
dl
.
chain
,
block
.
Hash
())
}
}
func
(
dl
*
downloadTester
)
hasBlock
(
hash
common
.
Hash
)
bool
{
func
(
dl
*
downloadTester
)
hasBlock
(
hash
common
.
Hash
)
bool
{
if
knownHash
==
hash
{
for
_
,
h
:=
range
dl
.
chain
{
if
h
==
hash
{
return
true
return
true
}
}
}
return
false
return
false
}
}
...
@@ -175,10 +197,12 @@ func TestTaking(t *testing.T) {
...
@@ -175,10 +197,12 @@ func TestTaking(t *testing.T) {
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Error
(
"download error"
,
err
)
t
.
Error
(
"download error"
,
err
)
}
}
bs
,
err
:=
tester
.
downloader
.
TakeBlocks
()
bs1
:=
tester
.
downloader
.
TakeBlocks
()
if
err
!=
nil
{
if
len
(
bs1
)
!=
1000
{
t
.
Fatalf
(
"failed to take blocks: %v"
,
err
)
t
.
Error
(
"expected to take 1000, got"
,
len
(
bs1
))
}
if
len
(
bs
)
!=
targetBlocks
{
t
.
Error
(
"retrieved block mismatch: have %v, want %v"
,
len
(
bs
),
targetBlocks
)
}
}
}
}
...
@@ -248,17 +272,18 @@ func TestThrottling(t *testing.T) {
...
@@ -248,17 +272,18 @@ func TestThrottling(t *testing.T) {
done
:=
make
(
chan
struct
{})
done
:=
make
(
chan
struct
{})
took
:=
[]
*
types
.
Block
{}
took
:=
[]
*
types
.
Block
{}
go
func
()
{
go
func
()
{
for
{
for
running
:=
true
;
running
;
{
select
{
select
{
case
<-
done
:
case
<-
done
:
took
=
append
(
took
,
tester
.
downloader
.
TakeBlocks
()
...
)
running
=
false
done
<-
struct
{}{}
return
default
:
default
:
took
=
append
(
took
,
tester
.
downloader
.
TakeBlocks
()
...
)
time
.
Sleep
(
time
.
Millisecond
)
time
.
Sleep
(
time
.
Millisecond
)
}
}
// Take a batch of blocks and accumulate
blocks
,
_
:=
tester
.
downloader
.
TakeBlocks
()
took
=
append
(
took
,
blocks
...
)
}
}
done
<-
struct
{}{}
}()
}()
// Synchronise the two threads and verify
// Synchronise the two threads and verify
...
@@ -273,3 +298,44 @@ func TestThrottling(t *testing.T) {
...
@@ -273,3 +298,44 @@ func TestThrottling(t *testing.T) {
t
.
Fatalf
(
"downloaded block mismatch: have %v, want %v"
,
len
(
took
),
targetBlocks
)
t
.
Fatalf
(
"downloaded block mismatch: have %v, want %v"
,
len
(
took
),
targetBlocks
)
}
}
}
}
// Tests that if a peer returns an invalid chain with a block pointing to a non-
// existing parent, it is correctly detected and handled.
func
TestNonExistingParentAttack
(
t
*
testing
.
T
)
{
// Forge a single-link chain with a forged header
hashes
:=
createHashes
(
0
,
1
)
blocks
:=
createBlocksFromHashes
(
hashes
)
forged
:=
blocks
[
hashes
[
0
]]
forged
.
ParentHeaderHash
=
unknownHash
// Try and sync with the malicious node and check that it fails
tester
:=
newTester
(
t
,
hashes
,
blocks
)
tester
.
newPeer
(
"attack"
,
big
.
NewInt
(
10000
),
hashes
[
0
])
if
err
:=
tester
.
sync
(
"attack"
,
hashes
[
0
]);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
bs
,
err
:=
tester
.
downloader
.
TakeBlocks
()
if
err
!=
ErrUnknownParent
{
t
.
Fatalf
(
"take error mismatch: have %v, want %v"
,
err
,
ErrUnknownParent
)
}
if
len
(
bs
)
!=
0
{
t
.
Error
(
"retrieved block mismatch: have %v, want %v"
,
len
(
bs
),
0
)
}
// Cancel the download due to the parent attack
tester
.
downloader
.
Cancel
()
// Reconstruct a valid chain, and try to synchronize with it
forged
.
ParentHeaderHash
=
knownHash
tester
.
newPeer
(
"valid"
,
big
.
NewInt
(
20000
),
hashes
[
0
])
if
err
:=
tester
.
sync
(
"valid"
,
hashes
[
0
]);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
bs
,
err
=
tester
.
downloader
.
TakeBlocks
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to retrieve blocks: %v"
,
err
)
}
if
len
(
bs
)
!=
1
{
t
.
Error
(
"retrieved block mismatch: have %v, want %v"
,
len
(
bs
),
1
)
}
}
This diff is collapsed.
Click to expand it.
eth/sync.go
+
17
−
6
View file @
a4246c2d
...
@@ -2,6 +2,7 @@ package eth
...
@@ -2,6 +2,7 @@ package eth
import
(
import
(
"math"
"math"
"sync/atomic"
"time"
"time"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/downloader"
...
@@ -14,6 +15,7 @@ import (
...
@@ -14,6 +15,7 @@ import (
func
(
pm
*
ProtocolManager
)
update
()
{
func
(
pm
*
ProtocolManager
)
update
()
{
forceSync
:=
time
.
Tick
(
forceSyncCycle
)
forceSync
:=
time
.
Tick
(
forceSyncCycle
)
blockProc
:=
time
.
Tick
(
blockProcCycle
)
blockProc
:=
time
.
Tick
(
blockProcCycle
)
blockProcPend
:=
int32
(
0
)
for
{
for
{
select
{
select
{
...
@@ -36,7 +38,14 @@ func (pm *ProtocolManager) update() {
...
@@ -36,7 +38,14 @@ func (pm *ProtocolManager) update() {
}
}
case
<-
blockProc
:
case
<-
blockProc
:
// Try to pull some blocks from the downloaded
// Try to pull some blocks from the downloaded
go
pm
.
processBlocks
()
if
atomic
.
CompareAndSwapInt32
(
&
blockProcPend
,
0
,
1
)
{
go
func
()
{
if
err
:=
pm
.
processBlocks
();
err
!=
nil
{
pm
.
downloader
.
Cancel
()
}
atomic
.
StoreInt32
(
&
blockProcPend
,
0
)
}()
}
case
<-
pm
.
quitSync
:
case
<-
pm
.
quitSync
:
return
return
...
@@ -52,8 +61,12 @@ func (pm *ProtocolManager) processBlocks() error {
...
@@ -52,8 +61,12 @@ func (pm *ProtocolManager) processBlocks() error {
pm
.
wg
.
Add
(
1
)
pm
.
wg
.
Add
(
1
)
defer
pm
.
wg
.
Done
()
defer
pm
.
wg
.
Done
()
// Take a batch of blocks (will return nil if a previous batch has not reached the chain yet)
// Take a batch of blocks, but abort if there's an invalid head or if the chain's empty
blocks
:=
pm
.
downloader
.
TakeBlocks
()
blocks
,
err
:=
pm
.
downloader
.
TakeBlocks
()
if
err
!=
nil
{
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"Block processing failed: %v"
,
err
)
return
err
}
if
len
(
blocks
)
==
0
{
if
len
(
blocks
)
==
0
{
return
nil
return
nil
}
}
...
@@ -63,9 +76,7 @@ func (pm *ProtocolManager) processBlocks() error {
...
@@ -63,9 +76,7 @@ func (pm *ProtocolManager) processBlocks() error {
max
:=
int
(
math
.
Min
(
float64
(
len
(
blocks
)),
float64
(
blockProcAmount
)))
max
:=
int
(
math
.
Min
(
float64
(
len
(
blocks
)),
float64
(
blockProcAmount
)))
_
,
err
:=
pm
.
chainman
.
InsertChain
(
blocks
[
:
max
])
_
,
err
:=
pm
.
chainman
.
InsertChain
(
blocks
[
:
max
])
if
err
!=
nil
{
if
err
!=
nil
{
// cancel download process
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"Block insertion failed: %v"
,
err
)
pm
.
downloader
.
Cancel
()
return
err
return
err
}
}
blocks
=
blocks
[
max
:
]
blocks
=
blocks
[
max
:
]
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment