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
0fc71877
Commit
0fc71877
authored
Jun 11, 2015
by
Péter Szilágyi
Browse files
Options
Downloads
Patches
Plain Diff
eth/downloader: add valid peer during attacks (check interference)
parent
80833f81
Branches
Branches containing commit
Tags
Tags containing commit
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
eth/downloader/downloader_test.go
+140
-93
140 additions, 93 deletions
eth/downloader/downloader_test.go
with
140 additions
and
93 deletions
eth/downloader/downloader_test.go
+
140
−
93
View file @
0fc71877
...
@@ -14,23 +14,29 @@ import (
...
@@ -14,23 +14,29 @@ import (
)
)
var
(
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
}
knownHash
=
common
.
Hash
{
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
}
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
}
unknownHash
=
common
.
Hash
{
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
}
bannedHash
=
common
.
Hash
{
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
}
bannedHash
=
common
.
Hash
{
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
}
genesis
=
createBlock
(
1
,
common
.
Hash
{},
knownHash
)
genesis
=
createBlock
(
1
,
common
.
Hash
{},
knownHash
)
)
)
func
createHashes
(
start
,
amount
int
)
(
hashes
[]
common
.
Hash
)
{
// idCounter is used by the createHashes method the generate deterministic but unique hashes
var
idCounter
=
int64
(
2
)
// #1 is the genesis block
// createHashes generates a batch of hashes rooted at a specific point in the chain.
func
createHashes
(
amount
int
,
root
common
.
Hash
)
(
hashes
[]
common
.
Hash
)
{
hashes
=
make
([]
common
.
Hash
,
amount
+
1
)
hashes
=
make
([]
common
.
Hash
,
amount
+
1
)
hashes
[
len
(
hashes
)
-
1
]
=
knownHash
hashes
[
len
(
hashes
)
-
1
]
=
root
for
i
:=
range
hashes
[
:
len
(
hashes
)
-
1
]
{
for
i
:=
0
;
i
<
len
(
hashes
)
-
1
;
i
++
{
binary
.
BigEndian
.
PutUint64
(
hashes
[
i
][
:
8
],
uint64
(
start
+
i
+
2
))
binary
.
BigEndian
.
PutUint64
(
hashes
[
i
][
:
8
],
uint64
(
idCounter
))
idCounter
++
}
}
return
return
}
}
// createBlock assembles a new block at the given chain height.
func
createBlock
(
i
int
,
parent
,
hash
common
.
Hash
)
*
types
.
Block
{
func
createBlock
(
i
int
,
parent
,
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
)
...
@@ -39,6 +45,11 @@ func createBlock(i int, parent, hash common.Hash) *types.Block {
...
@@ -39,6 +45,11 @@ func createBlock(i int, parent, hash common.Hash) *types.Block {
return
block
return
block
}
}
// copyBlock makes a deep copy of a block suitable for local modifications.
func
copyBlock
(
block
*
types
.
Block
)
*
types
.
Block
{
return
createBlock
(
int
(
block
.
Number
()
.
Int64
()),
block
.
ParentHeaderHash
,
block
.
HeaderHash
)
}
func
createBlocksFromHashes
(
hashes
[]
common
.
Hash
)
map
[
common
.
Hash
]
*
types
.
Block
{
func
createBlocksFromHashes
(
hashes
[]
common
.
Hash
)
map
[
common
.
Hash
]
*
types
.
Block
{
blocks
:=
make
(
map
[
common
.
Hash
]
*
types
.
Block
)
blocks
:=
make
(
map
[
common
.
Hash
]
*
types
.
Block
)
for
i
:=
0
;
i
<
len
(
hashes
);
i
++
{
for
i
:=
0
;
i
<
len
(
hashes
);
i
++
{
...
@@ -76,10 +87,15 @@ func newTester() *downloadTester {
...
@@ -76,10 +87,15 @@ func newTester() *downloadTester {
return
tester
return
tester
}
}
// sync starts synchronizing with a remote peer, blocking until it completes.
func
(
dl
*
downloadTester
)
sync
(
id
string
)
error
{
return
dl
.
downloader
.
synchronise
(
id
,
dl
.
peerHashes
[
id
][
0
])
}
// syncTake is starts synchronising with a remote peer, but concurrently it also
// syncTake is starts synchronising with a remote peer, but concurrently it also
// starts fetching blocks that the downloader retrieved. IT blocks until both go
// starts fetching blocks that the downloader retrieved. IT blocks until both go
// routines terminate.
// routines terminate.
func
(
dl
*
downloadTester
)
syncTake
(
peerI
d
string
,
head
common
.
Hash
)
([]
*
Block
,
error
)
{
func
(
dl
*
downloadTester
)
syncTake
(
i
d
string
)
([]
*
Block
,
error
)
{
// Start a block collector to take blocks as they become available
// Start a block collector to take blocks as they become available
done
:=
make
(
chan
struct
{})
done
:=
make
(
chan
struct
{})
took
:=
[]
*
Block
{}
took
:=
[]
*
Block
{}
...
@@ -102,7 +118,7 @@ func (dl *downloadTester) syncTake(peerId string, head common.Hash) ([]*Block, e
...
@@ -102,7 +118,7 @@ func (dl *downloadTester) syncTake(peerId string, head common.Hash) ([]*Block, e
done
<-
struct
{}{}
done
<-
struct
{}{}
}()
}()
// Start the downloading, sync the taker and return
// Start the downloading, sync the taker and return
err
:=
dl
.
downloader
.
synchronise
(
peerId
,
hea
d
)
err
:=
dl
.
sync
(
i
d
)
done
<-
struct
{}{}
done
<-
struct
{}{}
<-
done
<-
done
...
@@ -124,9 +140,14 @@ func (dl *downloadTester) getBlock(hash common.Hash) *types.Block {
...
@@ -124,9 +140,14 @@ func (dl *downloadTester) getBlock(hash common.Hash) *types.Block {
func
(
dl
*
downloadTester
)
newPeer
(
id
string
,
hashes
[]
common
.
Hash
,
blocks
map
[
common
.
Hash
]
*
types
.
Block
)
error
{
func
(
dl
*
downloadTester
)
newPeer
(
id
string
,
hashes
[]
common
.
Hash
,
blocks
map
[
common
.
Hash
]
*
types
.
Block
)
error
{
err
:=
dl
.
downloader
.
RegisterPeer
(
id
,
hashes
[
0
],
dl
.
peerGetHashesFn
(
id
),
dl
.
peerGetBlocksFn
(
id
))
err
:=
dl
.
downloader
.
RegisterPeer
(
id
,
hashes
[
0
],
dl
.
peerGetHashesFn
(
id
),
dl
.
peerGetBlocksFn
(
id
))
if
err
==
nil
{
if
err
==
nil
{
// Assign the owned hashes and blocks to the peer
// Assign the owned hashes and blocks to the peer (deep copy)
dl
.
peerHashes
[
id
]
=
hashes
dl
.
peerHashes
[
id
]
=
make
([]
common
.
Hash
,
len
(
hashes
))
dl
.
peerBlocks
[
id
]
=
blocks
copy
(
dl
.
peerHashes
[
id
],
hashes
)
dl
.
peerBlocks
[
id
]
=
make
(
map
[
common
.
Hash
]
*
types
.
Block
)
for
hash
,
block
:=
range
blocks
{
dl
.
peerBlocks
[
id
][
hash
]
=
copyBlock
(
block
)
}
}
}
return
err
return
err
}
}
...
@@ -192,14 +213,14 @@ func (dl *downloadTester) peerGetBlocksFn(id string) func([]common.Hash) error {
...
@@ -192,14 +213,14 @@ func (dl *downloadTester) peerGetBlocksFn(id string) func([]common.Hash) error {
func
TestSynchronisation
(
t
*
testing
.
T
)
{
func
TestSynchronisation
(
t
*
testing
.
T
)
{
// Create a small enough block chain to download and the tester
// Create a small enough block chain to download and the tester
targetBlocks
:=
blockCacheLimit
-
15
targetBlocks
:=
blockCacheLimit
-
15
hashes
:=
createHashes
(
0
,
targetBlocks
)
hashes
:=
createHashes
(
targetBlocks
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
tester
:=
newTester
()
tester
:=
newTester
()
tester
.
newPeer
(
"peer"
,
hashes
,
blocks
)
tester
.
newPeer
(
"peer"
,
hashes
,
blocks
)
// Synchronise with the peer and make sure all blocks were retrieved
// Synchronise with the peer and make sure all blocks were retrieved
if
err
:=
tester
.
downloader
.
synchronise
(
"peer"
,
hashes
[
0
]
);
err
!=
nil
{
if
err
:=
tester
.
sync
(
"peer"
);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
if
queued
:=
len
(
tester
.
downloader
.
queue
.
blockPool
);
queued
!=
targetBlocks
{
if
queued
:=
len
(
tester
.
downloader
.
queue
.
blockPool
);
queued
!=
targetBlocks
{
...
@@ -211,14 +232,14 @@ func TestSynchronisation(t *testing.T) {
...
@@ -211,14 +232,14 @@ func TestSynchronisation(t *testing.T) {
func
TestBlockTaking
(
t
*
testing
.
T
)
{
func
TestBlockTaking
(
t
*
testing
.
T
)
{
// Create a small enough block chain to download and the tester
// Create a small enough block chain to download and the tester
targetBlocks
:=
blockCacheLimit
-
15
targetBlocks
:=
blockCacheLimit
-
15
hashes
:=
createHashes
(
0
,
targetBlocks
)
hashes
:=
createHashes
(
targetBlocks
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
tester
:=
newTester
()
tester
:=
newTester
()
tester
.
newPeer
(
"peer"
,
hashes
,
blocks
)
tester
.
newPeer
(
"peer"
,
hashes
,
blocks
)
// Synchronise with the peer and test block retrieval
// Synchronise with the peer and test block retrieval
if
err
:=
tester
.
downloader
.
synchronise
(
"peer"
,
hashes
[
0
]
);
err
!=
nil
{
if
err
:=
tester
.
sync
(
"peer"
);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
if
took
:=
tester
.
downloader
.
TakeBlocks
();
len
(
took
)
!=
targetBlocks
{
if
took
:=
tester
.
downloader
.
TakeBlocks
();
len
(
took
)
!=
targetBlocks
{
...
@@ -243,14 +264,14 @@ func TestInactiveDownloader(t *testing.T) {
...
@@ -243,14 +264,14 @@ func TestInactiveDownloader(t *testing.T) {
func
TestCancel
(
t
*
testing
.
T
)
{
func
TestCancel
(
t
*
testing
.
T
)
{
// Create a small enough block chain to download and the tester
// Create a small enough block chain to download and the tester
targetBlocks
:=
blockCacheLimit
-
15
targetBlocks
:=
blockCacheLimit
-
15
hashes
:=
createHashes
(
0
,
targetBlocks
)
hashes
:=
createHashes
(
targetBlocks
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
tester
:=
newTester
()
tester
:=
newTester
()
tester
.
newPeer
(
"peer"
,
hashes
,
blocks
)
tester
.
newPeer
(
"peer"
,
hashes
,
blocks
)
// Synchronise with the peer, but cancel afterwards
// Synchronise with the peer, but cancel afterwards
if
err
:=
tester
.
downloader
.
synchronise
(
"peer"
,
hashes
[
0
]
);
err
!=
nil
{
if
err
:=
tester
.
sync
(
"peer"
);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
if
!
tester
.
downloader
.
Cancel
()
{
if
!
tester
.
downloader
.
Cancel
()
{
...
@@ -271,7 +292,7 @@ func TestCancel(t *testing.T) {
...
@@ -271,7 +292,7 @@ func TestCancel(t *testing.T) {
func
TestThrottling
(
t
*
testing
.
T
)
{
func
TestThrottling
(
t
*
testing
.
T
)
{
// Create a long block chain to download and the tester
// Create a long block chain to download and the tester
targetBlocks
:=
8
*
blockCacheLimit
targetBlocks
:=
8
*
blockCacheLimit
hashes
:=
createHashes
(
0
,
targetBlocks
)
hashes
:=
createHashes
(
targetBlocks
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
tester
:=
newTester
()
tester
:=
newTester
()
...
@@ -280,7 +301,7 @@ func TestThrottling(t *testing.T) {
...
@@ -280,7 +301,7 @@ func TestThrottling(t *testing.T) {
// Start a synchronisation concurrently
// Start a synchronisation concurrently
errc
:=
make
(
chan
error
)
errc
:=
make
(
chan
error
)
go
func
()
{
go
func
()
{
errc
<-
tester
.
downloader
.
synchronise
(
"peer"
,
hashes
[
0
]
)
errc
<-
tester
.
sync
(
"peer"
)
}()
}()
// Iteratively take some blocks, always checking the retrieval count
// Iteratively take some blocks, always checking the retrieval count
for
total
:=
0
;
total
<
targetBlocks
;
{
for
total
:=
0
;
total
<
targetBlocks
;
{
...
@@ -309,17 +330,20 @@ func TestThrottling(t *testing.T) {
...
@@ -309,17 +330,20 @@ func TestThrottling(t *testing.T) {
// Tests that if a peer returns an invalid chain with a block pointing to a non-
// Tests that if a peer returns an invalid chain with a block pointing to a non-
// existing parent, it is correctly detected and handled.
// existing parent, it is correctly detected and handled.
func
TestNonExistingParentAttack
(
t
*
testing
.
T
)
{
func
TestNonExistingParentAttack
(
t
*
testing
.
T
)
{
tester
:=
newTester
()
// Forge a single-link chain with a forged header
// Forge a single-link chain with a forged header
hashes
:=
createHashes
(
0
,
1
)
hashes
:=
createHashes
(
1
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
forged
:=
blocks
[
hashes
[
0
]]
hashes
=
createHashes
(
1
,
knownHash
)
forged
.
ParentHeaderHash
=
unknownHash
blocks
=
createBlocksFromHashes
(
hashes
)
blocks
[
hashes
[
0
]]
.
ParentHeaderHash
=
unknownHash
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
// Try and sync with the malicious node and check that it fails
// Try and sync with the malicious node and check that it fails
tester
:=
newTester
()
if
err
:=
tester
.
sync
(
"attack"
);
err
!=
nil
{
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
if
err
:=
tester
.
downloader
.
synchronise
(
"attack"
,
hashes
[
0
]);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
bs
:=
tester
.
downloader
.
TakeBlocks
()
bs
:=
tester
.
downloader
.
TakeBlocks
()
...
@@ -331,10 +355,8 @@ func TestNonExistingParentAttack(t *testing.T) {
...
@@ -331,10 +355,8 @@ func TestNonExistingParentAttack(t *testing.T) {
}
}
tester
.
downloader
.
Cancel
()
tester
.
downloader
.
Cancel
()
// Reconstruct a valid chain, and try to synchronize with it
// Try to synchronize with the valid chain and make sure it succeeds
forged
.
ParentHeaderHash
=
knownHash
if
err
:=
tester
.
sync
(
"valid"
);
err
!=
nil
{
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
if
err
:=
tester
.
downloader
.
synchronise
(
"valid"
,
hashes
[
0
]);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
bs
=
tester
.
downloader
.
TakeBlocks
()
bs
=
tester
.
downloader
.
TakeBlocks
()
...
@@ -348,21 +370,20 @@ func TestNonExistingParentAttack(t *testing.T) {
...
@@ -348,21 +370,20 @@ func TestNonExistingParentAttack(t *testing.T) {
// Tests that if a malicious peers keeps sending us repeating hashes, we don't
// Tests that if a malicious peers keeps sending us repeating hashes, we don't
// loop indefinitely.
// loop indefinitely.
func
TestRepeatingHashAttack
(
t
*
testing
.
T
)
{
func
TestRepeatingHashAttack
(
t
*
testing
.
T
)
{
// TODO: Is this thing valid??
tester
:=
newTester
()
// Create a valid chain, but drop the last link
// Create a valid chain, but drop the last link
hashes
:=
createHashes
(
0
,
blockCacheLimit
)
hashes
:=
createHashes
(
blockCacheLimit
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
forged
:=
hashes
[
:
len
(
hashes
)
-
1
]
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
tester
.
newPeer
(
"attack"
,
hashes
[
:
len
(
hashes
)
-
1
],
blocks
)
// Try and sync with the malicious node
// Try and sync with the malicious node
tester
:=
newTester
()
tester
.
newPeer
(
"attack"
,
forged
,
blocks
)
errc
:=
make
(
chan
error
)
errc
:=
make
(
chan
error
)
go
func
()
{
go
func
()
{
errc
<-
tester
.
downloader
.
synchronise
(
"attack"
,
hashes
[
0
]
)
errc
<-
tester
.
sync
(
"attack"
)
}()
}()
// Make sure that syncing returns and does so with a failure
// Make sure that syncing returns and does so with a failure
select
{
select
{
case
<-
time
.
After
(
time
.
Second
)
:
case
<-
time
.
After
(
time
.
Second
)
:
...
@@ -373,8 +394,7 @@ func TestRepeatingHashAttack(t *testing.T) {
...
@@ -373,8 +394,7 @@ func TestRepeatingHashAttack(t *testing.T) {
}
}
}
}
// Ensure that a valid chain can still pass sync
// Ensure that a valid chain can still pass sync
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
if
err
:=
tester
.
sync
(
"valid"
);
err
!=
nil
{
if
err
:=
tester
.
downloader
.
synchronise
(
"valid"
,
hashes
[
0
]);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
}
}
...
@@ -382,23 +402,22 @@ func TestRepeatingHashAttack(t *testing.T) {
...
@@ -382,23 +402,22 @@ func TestRepeatingHashAttack(t *testing.T) {
// Tests that if a malicious peers returns a non-existent block hash, it should
// Tests that if a malicious peers returns a non-existent block hash, it should
// eventually time out and the sync reattempted.
// eventually time out and the sync reattempted.
func
TestNonExistingBlockAttack
(
t
*
testing
.
T
)
{
func
TestNonExistingBlockAttack
(
t
*
testing
.
T
)
{
tester
:=
newTester
()
// Create a valid chain, but forge the last link
// Create a valid chain, but forge the last link
hashes
:=
createHashes
(
0
,
blockCacheLimit
)
hashes
:=
createHashes
(
blockCacheLimit
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
origin
:=
hashes
[
len
(
hashes
)
/
2
]
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
hashes
[
len
(
hashes
)
/
2
]
=
unknownHash
hashes
[
len
(
hashes
)
/
2
]
=
unknownHash
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
// Try and sync with the malicious node and check that it fails
// Try and sync with the malicious node and check that it fails
tester
:=
newTester
()
if
err
:=
tester
.
sync
(
"attack"
);
err
!=
errPeersUnavailable
{
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
if
err
:=
tester
.
downloader
.
synchronise
(
"attack"
,
hashes
[
0
]);
err
!=
errPeersUnavailable
{
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errPeersUnavailable
)
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errPeersUnavailable
)
}
}
// Ensure that a valid chain can still pass sync
// Ensure that a valid chain can still pass sync
hashes
[
len
(
hashes
)
/
2
]
=
origin
if
err
:=
tester
.
sync
(
"valid"
);
err
!=
nil
{
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
if
err
:=
tester
.
downloader
.
synchronise
(
"valid"
,
hashes
[
0
]);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
}
}
...
@@ -406,29 +425,28 @@ func TestNonExistingBlockAttack(t *testing.T) {
...
@@ -406,29 +425,28 @@ func TestNonExistingBlockAttack(t *testing.T) {
// Tests that if a malicious peer is returning hashes in a weird order, that the
// Tests that if a malicious peer is returning hashes in a weird order, that the
// sync throttler doesn't choke on them waiting for the valid blocks.
// sync throttler doesn't choke on them waiting for the valid blocks.
func
TestInvalidHashOrderAttack
(
t
*
testing
.
T
)
{
func
TestInvalidHashOrderAttack
(
t
*
testing
.
T
)
{
tester
:=
newTester
()
// Create a valid long chain, but reverse some hashes within
// Create a valid long chain, but reverse some hashes within
hashes
:=
createHashes
(
0
,
4
*
blockCacheLimit
)
hashes
:=
createHashes
(
4
*
blockCacheLimit
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
chunk1
:=
make
([]
common
.
Hash
,
blockCacheLimit
)
chunk1
:=
make
([]
common
.
Hash
,
blockCacheLimit
)
chunk2
:=
make
([]
common
.
Hash
,
blockCacheLimit
)
chunk2
:=
make
([]
common
.
Hash
,
blockCacheLimit
)
copy
(
chunk1
,
hashes
[
blockCacheLimit
:
2
*
blockCacheLimit
])
copy
(
chunk1
,
hashes
[
blockCacheLimit
:
2
*
blockCacheLimit
])
copy
(
chunk2
,
hashes
[
2
*
blockCacheLimit
:
3
*
blockCacheLimit
])
copy
(
chunk2
,
hashes
[
2
*
blockCacheLimit
:
3
*
blockCacheLimit
])
reverse
:=
make
([]
common
.
Hash
,
len
(
hashes
))
copy
(
hashes
[
2
*
blockCacheLimit
:
],
chunk1
)
copy
(
reverse
,
hashes
)
copy
(
hashes
[
blockCacheLimit
:
],
chunk2
)
copy
(
reverse
[
2
*
blockCacheLimit
:
],
chunk1
)
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
copy
(
reverse
[
blockCacheLimit
:
],
chunk2
)
// Try and sync with the malicious node and check that it fails
// Try and sync with the malicious node and check that it fails
tester
:=
newTester
()
if
_
,
err
:=
tester
.
syncTake
(
"attack"
);
err
!=
errInvalidChain
{
tester
.
newPeer
(
"attack"
,
reverse
,
blocks
)
if
_
,
err
:=
tester
.
syncTake
(
"attack"
,
reverse
[
0
]);
err
!=
errInvalidChain
{
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errInvalidChain
)
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errInvalidChain
)
}
}
// Ensure that a valid chain can still pass sync
// Ensure that a valid chain can still pass sync
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
if
_
,
err
:=
tester
.
syncTake
(
"valid"
);
err
!=
nil
{
if
_
,
err
:=
tester
.
syncTake
(
"valid"
,
hashes
[
0
]);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
}
}
...
@@ -436,18 +454,25 @@ func TestInvalidHashOrderAttack(t *testing.T) {
...
@@ -436,18 +454,25 @@ func TestInvalidHashOrderAttack(t *testing.T) {
// Tests that if a malicious peer makes up a random hash chain and tries to push
// Tests that if a malicious peer makes up a random hash chain and tries to push
// indefinitely, it actually gets caught with it.
// indefinitely, it actually gets caught with it.
func
TestMadeupHashChainAttack
(
t
*
testing
.
T
)
{
func
TestMadeupHashChainAttack
(
t
*
testing
.
T
)
{
tester
:=
newTester
()
blockSoftTTL
=
100
*
time
.
Millisecond
blockSoftTTL
=
100
*
time
.
Millisecond
crossCheckCycle
=
25
*
time
.
Millisecond
crossCheckCycle
=
25
*
time
.
Millisecond
// Create a long chain of hashes without backing blocks
// Create a long chain of hashes without backing blocks
hashes
:=
createHashes
(
0
,
1024
*
blockCacheLimit
)
hashes
:=
createHashes
(
4
*
blockCacheLimit
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
tester
.
newPeer
(
"attack"
,
createHashes
(
1024
*
blockCacheLimit
,
knownHash
),
nil
)
// Try and sync with the malicious node and check that it fails
// Try and sync with the malicious node and check that it fails
tester
:=
newTester
()
if
_
,
err
:=
tester
.
syncTake
(
"attack"
);
err
!=
errCrossCheckFailed
{
tester
.
newPeer
(
"attack"
,
hashes
,
nil
)
if
_
,
err
:=
tester
.
syncTake
(
"attack"
,
hashes
[
0
]);
err
!=
errCrossCheckFailed
{
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errCrossCheckFailed
)
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errCrossCheckFailed
)
}
}
// Ensure that a valid chain can still pass sync
if
_
,
err
:=
tester
.
syncTake
(
"valid"
);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
}
// Tests that if a malicious peer makes up a random hash chain, and tries to push
// Tests that if a malicious peer makes up a random hash chain, and tries to push
...
@@ -456,13 +481,13 @@ func TestMadeupHashChainAttack(t *testing.T) {
...
@@ -456,13 +481,13 @@ func TestMadeupHashChainAttack(t *testing.T) {
// one by one prevents reliable block/parent verification.
// one by one prevents reliable block/parent verification.
func
TestMadeupHashChainDrippingAttack
(
t
*
testing
.
T
)
{
func
TestMadeupHashChainDrippingAttack
(
t
*
testing
.
T
)
{
// Create a random chain of hashes to drip
// Create a random chain of hashes to drip
hashes
:=
createHashes
(
0
,
16
*
blockCacheLimit
)
hashes
:=
createHashes
(
16
*
blockCacheLimit
,
knownHash
)
tester
:=
newTester
()
tester
:=
newTester
()
// Try and sync with the attacker, one hash at a time
// Try and sync with the attacker, one hash at a time
tester
.
maxHashFetch
=
1
tester
.
maxHashFetch
=
1
tester
.
newPeer
(
"attack"
,
hashes
,
nil
)
tester
.
newPeer
(
"attack"
,
hashes
,
nil
)
if
_
,
err
:=
tester
.
syncTake
(
"attack"
,
hashes
[
0
]
);
err
!=
errStallingPeer
{
if
_
,
err
:=
tester
.
syncTake
(
"attack"
);
err
!=
errStallingPeer
{
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errStallingPeer
)
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errStallingPeer
)
}
}
}
}
...
@@ -477,7 +502,7 @@ func TestMadeupBlockChainAttack(t *testing.T) {
...
@@ -477,7 +502,7 @@ func TestMadeupBlockChainAttack(t *testing.T) {
crossCheckCycle
=
25
*
time
.
Millisecond
crossCheckCycle
=
25
*
time
.
Millisecond
// Create a long chain of blocks and simulate an invalid chain by dropping every second
// Create a long chain of blocks and simulate an invalid chain by dropping every second
hashes
:=
createHashes
(
0
,
16
*
blockCacheLimit
)
hashes
:=
createHashes
(
16
*
blockCacheLimit
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
gapped
:=
make
([]
common
.
Hash
,
len
(
hashes
)
/
2
)
gapped
:=
make
([]
common
.
Hash
,
len
(
hashes
)
/
2
)
...
@@ -487,7 +512,7 @@ func TestMadeupBlockChainAttack(t *testing.T) {
...
@@ -487,7 +512,7 @@ func TestMadeupBlockChainAttack(t *testing.T) {
// Try and sync with the malicious node and check that it fails
// Try and sync with the malicious node and check that it fails
tester
:=
newTester
()
tester
:=
newTester
()
tester
.
newPeer
(
"attack"
,
gapped
,
blocks
)
tester
.
newPeer
(
"attack"
,
gapped
,
blocks
)
if
_
,
err
:=
tester
.
syncTake
(
"attack"
,
gapped
[
0
]
);
err
!=
errCrossCheckFailed
{
if
_
,
err
:=
tester
.
syncTake
(
"attack"
);
err
!=
errCrossCheckFailed
{
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errCrossCheckFailed
)
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errCrossCheckFailed
)
}
}
// Ensure that a valid chain can still pass sync
// Ensure that a valid chain can still pass sync
...
@@ -495,7 +520,7 @@ func TestMadeupBlockChainAttack(t *testing.T) {
...
@@ -495,7 +520,7 @@ func TestMadeupBlockChainAttack(t *testing.T) {
crossCheckCycle
=
defaultCrossCheckCycle
crossCheckCycle
=
defaultCrossCheckCycle
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
if
_
,
err
:=
tester
.
syncTake
(
"valid"
,
hashes
[
0
]
);
err
!=
nil
{
if
_
,
err
:=
tester
.
syncTake
(
"valid"
);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
}
}
...
@@ -504,6 +529,8 @@ func TestMadeupBlockChainAttack(t *testing.T) {
...
@@ -504,6 +529,8 @@ func TestMadeupBlockChainAttack(t *testing.T) {
// attacker make up a valid hashes for random blocks, but also forges the block
// attacker make up a valid hashes for random blocks, but also forges the block
// parents to point to existing hashes.
// parents to point to existing hashes.
func
TestMadeupParentBlockChainAttack
(
t
*
testing
.
T
)
{
func
TestMadeupParentBlockChainAttack
(
t
*
testing
.
T
)
{
tester
:=
newTester
()
defaultBlockTTL
:=
blockSoftTTL
defaultBlockTTL
:=
blockSoftTTL
defaultCrossCheckCycle
:=
crossCheckCycle
defaultCrossCheckCycle
:=
crossCheckCycle
...
@@ -511,24 +538,24 @@ func TestMadeupParentBlockChainAttack(t *testing.T) {
...
@@ -511,24 +538,24 @@ func TestMadeupParentBlockChainAttack(t *testing.T) {
crossCheckCycle
=
25
*
time
.
Millisecond
crossCheckCycle
=
25
*
time
.
Millisecond
// Create a long chain of blocks and simulate an invalid chain by dropping every second
// Create a long chain of blocks and simulate an invalid chain by dropping every second
hashes
:=
createHashes
(
0
,
16
*
blockCacheLimit
)
hashes
:=
createHashes
(
16
*
blockCacheLimit
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
forges
:=
createBlocksFromHashes
(
hashes
)
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
for
hash
,
block
:=
range
forges
{
block
.
ParentHeaderHash
=
hash
// Simulate pointing to already known hash
for
_
,
block
:=
range
blocks
{
block
.
ParentHeaderHash
=
knownHash
// Simulate pointing to already known hash
}
}
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
// Try and sync with the malicious node and check that it fails
// Try and sync with the malicious node and check that it fails
tester
:=
newTester
()
if
_
,
err
:=
tester
.
syncTake
(
"attack"
);
err
!=
errCrossCheckFailed
{
tester
.
newPeer
(
"attack"
,
hashes
,
forges
)
if
_
,
err
:=
tester
.
syncTake
(
"attack"
,
hashes
[
0
]);
err
!=
errCrossCheckFailed
{
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errCrossCheckFailed
)
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errCrossCheckFailed
)
}
}
// Ensure that a valid chain can still pass sync
// Ensure that a valid chain can still pass sync
blockSoftTTL
=
defaultBlockTTL
blockSoftTTL
=
defaultBlockTTL
crossCheckCycle
=
defaultCrossCheckCycle
crossCheckCycle
=
defaultCrossCheckCycle
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
if
_
,
err
:=
tester
.
syncTake
(
"valid"
);
err
!=
nil
{
if
_
,
err
:=
tester
.
syncTake
(
"valid"
,
hashes
[
0
]);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
}
}
...
@@ -537,22 +564,25 @@ func TestMadeupParentBlockChainAttack(t *testing.T) {
...
@@ -537,22 +564,25 @@ func TestMadeupParentBlockChainAttack(t *testing.T) {
// the downloader, it will not keep refetching the same chain indefinitely, but
// the downloader, it will not keep refetching the same chain indefinitely, but
// gradually block pieces of it, until it's head is also blocked.
// gradually block pieces of it, until it's head is also blocked.
func
TestBannedChainStarvationAttack
(
t
*
testing
.
T
)
{
func
TestBannedChainStarvationAttack
(
t
*
testing
.
T
)
{
// Construct a valid chain, but ban one of the hashes in it
hashes
:=
createHashes
(
0
,
8
*
blockCacheLimit
)
hashes
[
len
(
hashes
)
/
2
+
23
]
=
bannedHash
// weird index to have non multiple of ban chunk size
blocks
:=
createBlocksFromHashes
(
hashes
)
// Create the tester and ban the selected hash
// Create the tester and ban the selected hash
tester
:=
newTester
()
tester
:=
newTester
()
tester
.
downloader
.
banned
.
Add
(
bannedHash
)
tester
.
downloader
.
banned
.
Add
(
bannedHash
)
// Construct a valid chain, for it and ban the fork
hashes
:=
createHashes
(
8
*
blockCacheLimit
,
knownHash
)
blocks
:=
createBlocksFromHashes
(
hashes
)
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
fork
:=
len
(
hashes
)
/
2
-
23
hashes
=
append
(
createHashes
(
4
*
blockCacheLimit
,
bannedHash
),
hashes
[
fork
:
]
...
)
blocks
=
createBlocksFromHashes
(
hashes
)
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
// Iteratively try to sync, and verify that the banned hash list grows until
// Iteratively try to sync, and verify that the banned hash list grows until
// the head of the invalid chain is blocked too.
// the head of the invalid chain is blocked too.
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
for
banned
:=
tester
.
downloader
.
banned
.
Size
();
;
{
for
banned
:=
tester
.
downloader
.
banned
.
Size
();
;
{
// Try to sync with the attacker, check hash chain failure
// Try to sync with the attacker, check hash chain failure
if
_
,
err
:=
tester
.
syncTake
(
"attack"
,
hashes
[
0
]
);
err
!=
errInvalidChain
{
if
_
,
err
:=
tester
.
syncTake
(
"attack"
);
err
!=
errInvalidChain
{
if
tester
.
downloader
.
banned
.
Has
(
hashes
[
0
])
&&
err
==
errBannedHead
{
if
tester
.
downloader
.
banned
.
Has
(
hashes
[
0
])
&&
err
==
errBannedHead
{
break
break
}
}
...
@@ -569,35 +599,45 @@ func TestBannedChainStarvationAttack(t *testing.T) {
...
@@ -569,35 +599,45 @@ func TestBannedChainStarvationAttack(t *testing.T) {
if
err
:=
tester
.
newPeer
(
"new attacker"
,
hashes
,
blocks
);
err
!=
errBannedHead
{
if
err
:=
tester
.
newPeer
(
"new attacker"
,
hashes
,
blocks
);
err
!=
errBannedHead
{
t
.
Fatalf
(
"peer registration mismatch: have %v, want %v"
,
err
,
errBannedHead
)
t
.
Fatalf
(
"peer registration mismatch: have %v, want %v"
,
err
,
errBannedHead
)
}
}
if
peer
:=
tester
.
downloader
.
peers
.
Peer
(
"ne
t
attacker"
);
peer
!=
nil
{
if
peer
:=
tester
.
downloader
.
peers
.
Peer
(
"ne
w
attacker"
);
peer
!=
nil
{
t
.
Fatalf
(
"banned attacker registered: %v"
,
peer
)
t
.
Fatalf
(
"banned attacker registered: %v"
,
peer
)
}
}
// Ensure that a valid chain can still pass sync
if
_
,
err
:=
tester
.
syncTake
(
"valid"
);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
}
// Tests that if a peer sends excessively many/large invalid chains that are
// Tests that if a peer sends excessively many/large invalid chains that are
// gradually banned, it will have an upper limit on the consumed memory and also
// gradually banned, it will have an upper limit on the consumed memory and also
// the origin bad hashes will not be evacuated.
// the origin bad hashes will not be evacuated.
func
TestBannedChainMemoryExhaustionAttack
(
t
*
testing
.
T
)
{
func
TestBannedChainMemoryExhaustionAttack
(
t
*
testing
.
T
)
{
// Create the tester and ban the selected hash
tester
:=
newTester
()
tester
.
downloader
.
banned
.
Add
(
bannedHash
)
// Reduce the test size a bit
// Reduce the test size a bit
defaultMaxBlockFetch
:=
MaxBlockFetch
defaultMaxBannedHashes
:=
maxBannedHashes
MaxBlockFetch
=
4
MaxBlockFetch
=
4
maxBannedHashes
=
256
maxBannedHashes
=
256
// Construct a banned chain with more chunks than the ban limit
// Construct a banned chain with more chunks than the ban limit
hashes
:=
createHashes
(
0
,
maxBannedHashes
*
MaxBlockFetch
)
hashes
:=
createHashes
(
8
*
blockCacheLimit
,
knownHash
)
hashes
[
len
(
hashes
)
-
1
]
=
bannedHash
// weird index to have non multiple of ban chunk size
blocks
:=
createBlocksFromHashes
(
hashes
)
blocks
:=
createBlocksFromHashes
(
hashes
)
tester
.
newPeer
(
"valid"
,
hashes
,
blocks
)
// Create the tester and ban the selected hash
fork
:=
len
(
hashes
)
/
2
-
23
tester
:=
newTester
()
hashes
=
append
(
createHashes
(
maxBannedHashes
*
MaxBlockFetch
,
bannedHash
),
hashes
[
fork
:
]
...
)
tester
.
downloader
.
banned
.
Add
(
bannedHash
)
blocks
=
createBlocksFromHashes
(
hashes
)
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
// Iteratively try to sync, and verify that the banned hash list grows until
// Iteratively try to sync, and verify that the banned hash list grows until
// the head of the invalid chain is blocked too.
// the head of the invalid chain is blocked too.
tester
.
newPeer
(
"attack"
,
hashes
,
blocks
)
for
{
for
{
// Try to sync with the attacker, check hash chain failure
// Try to sync with the attacker, check hash chain failure
if
_
,
err
:=
tester
.
syncTake
(
"attack"
,
hashes
[
0
]
);
err
!=
errInvalidChain
{
if
_
,
err
:=
tester
.
syncTake
(
"attack"
);
err
!=
errInvalidChain
{
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errInvalidChain
)
t
.
Fatalf
(
"synchronisation error mismatch: have %v, want %v"
,
err
,
errInvalidChain
)
}
}
// Short circuit if the entire chain was banned
// Short circuit if the entire chain was banned
...
@@ -614,6 +654,13 @@ func TestBannedChainMemoryExhaustionAttack(t *testing.T) {
...
@@ -614,6 +654,13 @@ func TestBannedChainMemoryExhaustionAttack(t *testing.T) {
}
}
}
}
}
}
// Ensure that a valid chain can still pass sync
MaxBlockFetch
=
defaultMaxBlockFetch
maxBannedHashes
=
defaultMaxBannedHashes
if
_
,
err
:=
tester
.
syncTake
(
"valid"
);
err
!=
nil
{
t
.
Fatalf
(
"failed to synchronise blocks: %v"
,
err
)
}
}
}
// Tests that misbehaving peers are disconnected, whilst behaving ones are not.
// Tests that misbehaving peers are disconnected, whilst behaving ones are not.
...
...
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