Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
whqwjb
go-ethereum
提交
a4246c2d
G
go-ethereum
项目概览
whqwjb
/
go-ethereum
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
go-ethereum
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
a4246c2d
编写于
5月 14, 2015
作者:
P
Péter Szilágyi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
eth, eth/downloader: handle a potential unknown parent attack
上级
7cb0e242
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
112 addition
and
31 deletion
+112
-31
eth/downloader/downloader.go
eth/downloader/downloader.go
+12
-8
eth/downloader/downloader_test.go
eth/downloader/downloader_test.go
+83
-17
eth/sync.go
eth/sync.go
+17
-6
未找到文件。
eth/downloader/downloader.go
浏览文件 @
a4246c2d
...
...
@@ -37,6 +37,7 @@ var (
errCancelHashFetch
=
errors
.
New
(
"hash fetching cancelled (requested)"
)
errCancelBlockFetch
=
errors
.
New
(
"block downloading cancelled (requested)"
)
errNoSyncActive
=
errors
.
New
(
"no sync active"
)
ErrUnknownParent
=
errors
.
New
(
"block has unknown parent"
)
)
type
hashCheckFn
func
(
common
.
Hash
)
bool
...
...
@@ -142,16 +143,19 @@ func (d *Downloader) Synchronise(id string, hash common.Hash) error {
return
d
.
syncWithPeer
(
p
,
hash
)
}
// TakeBlocks takes blocks from the queue and yields them to the blockTaker handler
// it's possible it yields no blocks
func
(
d
*
Downloader
)
TakeBlocks
()
types
.
Blocks
{
// Check that there are blocks available and its parents are known
// TakeBlocks takes blocks from the queue and yields them to the caller.
func
(
d
*
Downloader
)
TakeBlocks
()
(
types
.
Blocks
,
error
)
{
// If the head block is missing, no blocks are ready
head
:=
d
.
queue
.
GetHeadBlock
()
if
head
==
nil
||
!
d
.
hasBlock
(
head
.
ParentHash
())
{
return
nil
if
head
==
nil
{
return
nil
,
nil
}
// Retrieve a full batch of blocks
return
d
.
queue
.
TakeBlocks
(
head
)
// If the parent hash of the head is unknown, notify the caller
if
!
d
.
hasBlock
(
head
.
ParentHash
())
{
return
nil
,
ErrUnknownParent
}
// Otherwise retrieve a full batch of blocks
return
d
.
queue
.
TakeBlocks
(
head
),
nil
}
func
(
d
*
Downloader
)
Has
(
hash
common
.
Hash
)
bool
{
...
...
eth/downloader/downloader_test.go
浏览文件 @
a4246c2d
...
...
@@ -10,7 +10,10 @@ import (
"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
)
{
hashes
=
make
([]
common
.
Hash
,
amount
+
1
)
...
...
@@ -27,7 +30,7 @@ func createBlock(i int, prevHash, hash common.Hash) *types.Block {
header
:=
&
types
.
Header
{
Number
:
big
.
NewInt
(
int64
(
i
))}
block
:=
types
.
NewBlockWithHeader
(
header
)
block
.
HeaderHash
=
hash
block
.
ParentHeaderHash
=
known
Hash
block
.
ParentHeaderHash
=
prev
Hash
return
block
}
...
...
@@ -42,9 +45,12 @@ func createBlocksFromHashes(hashes []common.Hash) map[common.Hash]*types.Block {
}
type
downloadTester
struct
{
downloader
*
Downloader
hashes
[]
common
.
Hash
blocks
map
[
common
.
Hash
]
*
types
.
Block
downloader
*
Downloader
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
pcount
int
done
chan
bool
...
...
@@ -52,7 +58,15 @@ type downloadTester struct {
}
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
)
tester
.
downloader
=
downloader
...
...
@@ -64,9 +78,17 @@ func (dl *downloadTester) sync(peerId string, hash common.Hash) error {
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
{
if
knownHash
==
hash
{
return
true
for
_
,
h
:=
range
dl
.
chain
{
if
h
==
hash
{
return
true
}
}
return
false
}
...
...
@@ -175,10 +197,12 @@ func TestTaking(t *testing.T) {
if
err
!=
nil
{
t
.
Error
(
"download error"
,
err
)
}
bs1
:=
tester
.
downloader
.
TakeBlocks
()
if
len
(
bs1
)
!=
1000
{
t
.
Error
(
"expected to take 1000, got"
,
len
(
bs1
))
bs
,
err
:=
tester
.
downloader
.
TakeBlocks
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to take blocks: %v"
,
err
)
}
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) {
done
:=
make
(
chan
struct
{})
took
:=
[]
*
types
.
Block
{}
go
func
()
{
for
{
for
running
:=
true
;
running
;
{
select
{
case
<-
done
:
took
=
append
(
took
,
tester
.
downloader
.
TakeBlocks
()
...
)
done
<-
struct
{}{}
return
running
=
false
default
:
took
=
append
(
took
,
tester
.
downloader
.
TakeBlocks
()
...
)
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
...
...
@@ -273,3 +298,44 @@ func TestThrottling(t *testing.T) {
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
)
}
}
eth/sync.go
浏览文件 @
a4246c2d
...
...
@@ -2,6 +2,7 @@ package eth
import
(
"math"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/eth/downloader"
...
...
@@ -14,6 +15,7 @@ import (
func
(
pm
*
ProtocolManager
)
update
()
{
forceSync
:=
time
.
Tick
(
forceSyncCycle
)
blockProc
:=
time
.
Tick
(
blockProcCycle
)
blockProcPend
:=
int32
(
0
)
for
{
select
{
...
...
@@ -36,7 +38,14 @@ func (pm *ProtocolManager) update() {
}
case
<-
blockProc
:
// 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
:
return
...
...
@@ -52,8 +61,12 @@ func (pm *ProtocolManager) processBlocks() error {
pm
.
wg
.
Add
(
1
)
defer
pm
.
wg
.
Done
()
// Take a batch of blocks (will return nil if a previous batch has not reached the chain yet)
blocks
:=
pm
.
downloader
.
TakeBlocks
()
// Take a batch of blocks, but abort if there's an invalid head or if the chain's empty
blocks
,
err
:=
pm
.
downloader
.
TakeBlocks
()
if
err
!=
nil
{
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"Block processing failed: %v"
,
err
)
return
err
}
if
len
(
blocks
)
==
0
{
return
nil
}
...
...
@@ -63,9 +76,7 @@ func (pm *ProtocolManager) processBlocks() error {
max
:=
int
(
math
.
Min
(
float64
(
len
(
blocks
)),
float64
(
blockProcAmount
)))
_
,
err
:=
pm
.
chainman
.
InsertChain
(
blocks
[
:
max
])
if
err
!=
nil
{
// cancel download process
pm
.
downloader
.
Cancel
()
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"Block insertion failed: %v"
,
err
)
return
err
}
blocks
=
blocks
[
max
:
]
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录