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
71aa5fe8
Commit
71aa5fe8
authored
Apr 18, 2015
by
obscuren
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'downloader-proto' into develop
parents
84f1af64
164b8788
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
790 additions
and
453 deletions
+790
-453
main.go
cmd/geth/main.go
+4
-2
flags.go
cmd/utils/flags.go
+9
-0
block_processor.go
core/block_processor.go
+2
-3
chain_manager.go
core/chain_manager.go
+2
-3
backend.go
eth/backend.go
+27
-33
downloader.go
eth/downloader/downloader.go
+133
-72
peer.go
eth/downloader/peer.go
+24
-1
queue.go
eth/downloader/queue.go
+21
-2
synchronous.go
eth/downloader/synchronous.go
+79
-0
handler.go
eth/handler.go
+334
-0
peer.go
eth/peer.go
+143
-0
protocol.go
eth/protocol.go
+9
-322
protocol_test.go
eth/protocol_test.go
+3
-15
No files found.
cmd/geth/main.go
View file @
71aa5fe8
...
...
@@ -31,6 +31,8 @@ import (
"strconv"
"time"
"path"
"github.com/codegangsta/cli"
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
...
...
@@ -42,13 +44,12 @@ import (
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger"
"github.com/peterh/liner"
"path"
)
import
_
"net/http/pprof"
const
(
ClientIdentifier
=
"Geth"
Version
=
"0.9.
9
"
Version
=
"0.9.
10
"
)
var
app
=
utils
.
NewApp
(
Version
,
"the go-ethereum command line interface"
)
...
...
@@ -217,6 +218,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
},
}
app
.
Flags
=
[]
cli
.
Flag
{
utils
.
IdentityFlag
,
utils
.
UnlockedAccountFlag
,
utils
.
PasswordFileFlag
,
utils
.
BootnodesFlag
,
...
...
cmd/utils/flags.go
View file @
71aa5fe8
...
...
@@ -89,6 +89,10 @@ var (
Usage
:
"Blockchain version"
,
Value
:
core
.
BlockChainVersion
,
}
IdentityFlag
=
cli
.
StringFlag
{
Name
:
"identity"
,
Usage
:
"node name"
,
}
// miner settings
MinerThreadsFlag
=
cli
.
IntFlag
{
...
...
@@ -242,6 +246,11 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
// Set the log dir
glog
.
SetLogDir
(
ctx
.
GlobalString
(
LogFileFlag
.
Name
))
customName
:=
ctx
.
GlobalString
(
IdentityFlag
.
Name
)
if
len
(
customName
)
>
0
{
clientID
+=
"/"
+
customName
}
return
&
eth
.
Config
{
Name
:
common
.
MakeName
(
clientID
,
version
),
DataDir
:
ctx
.
GlobalString
(
DataDirFlag
.
Name
),
...
...
core/block_processor.go
View file @
71aa5fe8
...
...
@@ -323,7 +323,7 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
}
uncles
.
Add
(
block
.
Hash
())
for
_
,
uncle
:=
range
block
.
Uncles
()
{
for
i
,
uncle
:=
range
block
.
Uncles
()
{
if
uncles
.
Has
(
uncle
.
Hash
())
{
// Error not unique
return
UncleError
(
"Uncle not unique"
)
...
...
@@ -340,9 +340,8 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
}
if
err
:=
sm
.
ValidateHeader
(
uncle
,
ancestorHeaders
[
uncle
.
ParentHash
]);
err
!=
nil
{
return
ValidationError
(
fmt
.
Sprintf
(
"
%v"
,
err
))
return
ValidationError
(
fmt
.
Sprintf
(
"
uncle[%d](%x) header invalid: %v"
,
i
,
uncle
.
Hash
()
.
Bytes
()[
:
4
]
,
err
))
}
}
return
nil
...
...
core/chain_manager.go
View file @
71aa5fe8
...
...
@@ -330,14 +330,13 @@ func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (
}
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
for
i
:=
uint64
(
0
);
i
<
max
;
i
++
{
parentHash
:=
block
.
Header
()
.
ParentHash
block
=
self
.
GetBlock
(
parentHash
)
block
=
self
.
GetBlock
(
block
.
ParentHash
())
if
block
==
nil
{
break
}
chain
=
append
(
chain
,
block
.
Hash
())
if
block
.
Header
()
.
Number
.
Cmp
(
common
.
Big0
)
<=
0
{
if
block
.
Number
()
.
Cmp
(
common
.
Big0
)
<=
0
{
break
}
}
...
...
eth/backend.go
View file @
71aa5fe8
...
...
@@ -10,12 +10,12 @@ import (
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/blockpool"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
...
...
@@ -130,15 +130,16 @@ type Ethereum struct {
blockProcessor
*
core
.
BlockProcessor
txPool
*
core
.
TxPool
chainManager
*
core
.
ChainManager
blockPool
*
blockpool
.
BlockPool
accountManager
*
accounts
.
Manager
whisper
*
whisper
.
Whisper
pow
*
ethash
.
Ethash
protocolManager
*
ProtocolManager
downloader
*
downloader
.
Downloader
net
*
p2p
.
Server
eventMux
*
event
.
TypeMux
txSub
event
.
Subscription
b
lockSub
event
.
Subscription
minedB
lockSub
event
.
Subscription
miner
*
miner
.
Miner
// logger logger.LogSystem
...
...
@@ -208,6 +209,7 @@ func New(config *Config) (*Ethereum, error) {
}
eth
.
chainManager
=
core
.
NewChainManager
(
blockDb
,
stateDb
,
eth
.
EventMux
())
eth
.
downloader
=
downloader
.
New
(
eth
.
chainManager
.
HasBlock
,
eth
.
chainManager
.
InsertChain
,
eth
.
chainManager
.
Td
)
eth
.
pow
=
ethash
.
New
(
eth
.
chainManager
)
eth
.
txPool
=
core
.
NewTxPool
(
eth
.
EventMux
(),
eth
.
chainManager
.
State
)
eth
.
blockProcessor
=
core
.
NewBlockProcessor
(
stateDb
,
extraDb
,
eth
.
pow
,
eth
.
txPool
,
eth
.
chainManager
,
eth
.
EventMux
())
...
...
@@ -215,19 +217,14 @@ func New(config *Config) (*Ethereum, error) {
eth
.
whisper
=
whisper
.
New
()
eth
.
shhVersionId
=
int
(
eth
.
whisper
.
Version
())
eth
.
miner
=
miner
.
New
(
eth
,
eth
.
pow
,
config
.
MinerThreads
)
hasBlock
:=
eth
.
chainManager
.
HasBlock
insertChain
:=
eth
.
chainManager
.
InsertChain
td
:=
eth
.
chainManager
.
Td
()
eth
.
blockPool
=
blockpool
.
New
(
hasBlock
,
insertChain
,
eth
.
pow
.
Verify
,
eth
.
EventMux
(),
td
)
eth
.
protocolManager
=
NewProtocolManager
(
config
.
ProtocolVersion
,
config
.
NetworkId
,
eth
.
txPool
,
eth
.
chainManager
,
eth
.
downloader
)
netprv
,
err
:=
config
.
nodeKey
()
if
err
!=
nil
{
return
nil
,
err
}
ethProto
:=
EthProtocol
(
config
.
ProtocolVersion
,
config
.
NetworkId
,
eth
.
txPool
,
eth
.
chainManager
,
eth
.
blockPool
)
protocols
:=
[]
p2p
.
Protocol
{
ethProto
}
protocols
:=
[]
p2p
.
Protocol
{
eth
.
protocolManager
.
SubProtocol
}
if
config
.
Shh
{
protocols
=
append
(
protocols
,
eth
.
whisper
.
Protocol
())
}
...
...
@@ -349,7 +346,6 @@ func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManag
func
(
s
*
Ethereum
)
ChainManager
()
*
core
.
ChainManager
{
return
s
.
chainManager
}
func
(
s
*
Ethereum
)
BlockProcessor
()
*
core
.
BlockProcessor
{
return
s
.
blockProcessor
}
func
(
s
*
Ethereum
)
TxPool
()
*
core
.
TxPool
{
return
s
.
txPool
}
func
(
s
*
Ethereum
)
BlockPool
()
*
blockpool
.
BlockPool
{
return
s
.
blockPool
}
func
(
s
*
Ethereum
)
Whisper
()
*
whisper
.
Whisper
{
return
s
.
whisper
}
func
(
s
*
Ethereum
)
EventMux
()
*
event
.
TypeMux
{
return
s
.
eventMux
}
func
(
s
*
Ethereum
)
BlockDb
()
common
.
Database
{
return
s
.
blockDb
}
...
...
@@ -363,6 +359,7 @@ func (s *Ethereum) ClientVersion() string { return s.clientVersio
func
(
s
*
Ethereum
)
EthVersion
()
int
{
return
s
.
ethVersionId
}
func
(
s
*
Ethereum
)
NetVersion
()
int
{
return
s
.
netVersionId
}
func
(
s
*
Ethereum
)
ShhVersion
()
int
{
return
s
.
shhVersionId
}
func
(
s
*
Ethereum
)
Downloader
()
*
downloader
.
Downloader
{
return
s
.
downloader
}
// Start the ethereum
func
(
s
*
Ethereum
)
Start
()
error
{
...
...
@@ -380,7 +377,6 @@ func (s *Ethereum) Start() error {
// Start services
s
.
txPool
.
Start
()
s
.
blockPool
.
Start
()
if
s
.
whisper
!=
nil
{
s
.
whisper
.
Start
()
...
...
@@ -391,8 +387,8 @@ func (s *Ethereum) Start() error {
go
s
.
txBroadcastLoop
()
// broadcast mined blocks
s
.
blockSub
=
s
.
eventMux
.
Subscribe
(
core
.
ChainHead
Event
{})
go
s
.
block
BroadcastLoop
()
s
.
minedBlockSub
=
s
.
eventMux
.
Subscribe
(
core
.
NewMinedBlock
Event
{})
go
s
.
mined
BroadcastLoop
()
glog
.
V
(
logger
.
Info
)
.
Infoln
(
"Server started"
)
return
nil
...
...
@@ -406,7 +402,6 @@ func (s *Ethereum) StartForTest() {
// Start services
s
.
txPool
.
Start
()
s
.
blockPool
.
Start
()
}
func
(
self
*
Ethereum
)
SuggestPeer
(
nodeURL
string
)
error
{
...
...
@@ -425,11 +420,10 @@ func (s *Ethereum) Stop() {
defer
s
.
extraDb
.
Close
()
s
.
txSub
.
Unsubscribe
()
// quits txBroadcastLoop
s
.
b
lockSub
.
Unsubscribe
()
// quits blockBroadcastLoop
s
.
minedB
lockSub
.
Unsubscribe
()
// quits blockBroadcastLoop
s
.
txPool
.
Stop
()
s
.
eventMux
.
Stop
()
s
.
blockPool
.
Stop
()
if
s
.
whisper
!=
nil
{
s
.
whisper
.
Stop
()
}
...
...
@@ -468,12 +462,12 @@ func (self *Ethereum) syncAccounts(tx *types.Transaction) {
}
}
func
(
self
*
Ethereum
)
block
BroadcastLoop
()
{
func
(
self
*
Ethereum
)
mined
BroadcastLoop
()
{
// automatically stops if unsubscribe
for
obj
:=
range
self
.
b
lockSub
.
Chan
()
{
for
obj
:=
range
self
.
minedB
lockSub
.
Chan
()
{
switch
ev
:=
obj
.
(
type
)
{
case
core
.
ChainHead
Event
:
self
.
net
.
BroadcastLimited
(
"eth"
,
NewBlockMsg
,
math
.
Sqrt
,
[]
interface
{}{
ev
.
Block
,
ev
.
Block
.
Td
}
)
case
core
.
NewMinedBlock
Event
:
self
.
protocolManager
.
BroadcastBlock
(
ev
.
Block
.
Hash
(),
ev
.
Block
)
}
}
}
...
...
eth/downloader/downloader.go
View file @
71aa5fe8
package
downloader
import
(
"errors"
"fmt"
"math"
"math/big"
"sync"
...
...
@@ -17,7 +19,20 @@ import (
const
(
maxBlockFetch
=
256
// Amount of max blocks to be fetched per chunk
minDesiredPeerCount
=
3
// Amount of peers desired to start syncing
minDesiredPeerCount
=
5
// Amount of peers desired to start syncing
peerCountTimeout
=
12
*
time
.
Second
// Amount of time it takes for the peer handler to ignore minDesiredPeerCount
blockTtl
=
15
*
time
.
Second
// The amount of time it takes for a block request to time out
hashTtl
=
20
*
time
.
Second
// The amount of time it takes for a hash request to time out
)
var
(
errLowTd
=
errors
.
New
(
"peer's TD is too low"
)
errBusy
=
errors
.
New
(
"busy"
)
errUnknownPeer
=
errors
.
New
(
"peer's unknown or unhealthy"
)
errBadPeer
=
errors
.
New
(
"action from bad peer ignored"
)
errTimeout
=
errors
.
New
(
"timeout"
)
errEmptyHashSet
=
errors
.
New
(
"empty hash set by peer"
)
errPeersUnavailable
=
errors
.
New
(
"no peers available or all peers tried for block download process"
)
)
type
hashCheckFn
func
(
common
.
Hash
)
bool
...
...
@@ -29,6 +44,7 @@ type Downloader struct {
mu
sync
.
RWMutex
queue
*
queue
peers
peers
activePeer
string
// Callbacks
hasBlock
hashCheckFn
...
...
@@ -43,7 +59,7 @@ type Downloader struct {
// Channels
newPeerCh
chan
*
peer
syncCh
chan
syncPack
H
ashCh
chan
[]
common
.
Hash
h
ashCh
chan
[]
common
.
Hash
blockCh
chan
blockPack
quit
chan
struct
{}
}
...
...
@@ -68,7 +84,7 @@ func New(hasBlock hashCheckFn, insertChain chainInsertFn, currentTd currentTdFn)
currentTd
:
currentTd
,
newPeerCh
:
make
(
chan
*
peer
,
1
),
syncCh
:
make
(
chan
syncPack
,
1
),
H
ashCh
:
make
(
chan
[]
common
.
Hash
,
1
),
h
ashCh
:
make
(
chan
[]
common
.
Hash
,
1
),
blockCh
:
make
(
chan
blockPack
,
1
),
quit
:
make
(
chan
struct
{}),
}
...
...
@@ -82,7 +98,7 @@ func (d *Downloader) RegisterPeer(id string, td *big.Int, hash common.Hash, getH
d
.
mu
.
Lock
()
defer
d
.
mu
.
Unlock
()
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Register peer"
,
id
)
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Register peer"
,
id
,
"TD ="
,
td
)
// Create a new peer and add it to the list of known peers
peer
:=
newPeer
(
id
,
td
,
hash
,
getHashes
,
getBlocks
)
...
...
@@ -94,6 +110,7 @@ func (d *Downloader) RegisterPeer(id string, td *big.Int, hash common.Hash, getH
return
nil
}
// UnregisterPeer unregister's a peer. This will prevent any action from the specified peer.
func
(
d
*
Downloader
)
UnregisterPeer
(
id
string
)
{
d
.
mu
.
Lock
()
defer
d
.
mu
.
Unlock
()
...
...
@@ -105,8 +122,7 @@ func (d *Downloader) UnregisterPeer(id string) {
func
(
d
*
Downloader
)
peerHandler
()
{
// itimer is used to determine when to start ignoring `minDesiredPeerCount`
//itimer := time.NewTicker(5 * time.Second)
itimer
:=
time
.
NewTimer
(
5
*
time
.
Second
)
itimer
:=
time
.
NewTimer
(
peerCountTimeout
)
out
:
for
{
select
{
...
...
@@ -116,11 +132,18 @@ out:
if
len
(
d
.
peers
)
<
minDesiredPeerCount
{
break
}
d
.
selectPeer
(
d
.
peers
.
bestPeer
())
case
<-
itimer
.
C
:
// The timer will make sure that the downloader keeps an active state
// in which it attempts to always check the network for highest td peers
// Either select the peer or restart the timer if no peers could
// be selected.
if
peer
:=
d
.
peers
.
bestPeer
();
peer
!=
nil
{
d
.
selectPeer
(
d
.
peers
.
bestPeer
())
}
else
{
itimer
.
Reset
(
5
*
time
.
Second
)
}
case
<-
d
.
quit
:
break
out
}
...
...
@@ -131,7 +154,7 @@ func (d *Downloader) selectPeer(p *peer) {
// Make sure it's doing neither. Once done we can restart the
// downloading process if the TD is higher. For now just get on
// with whatever is going on. This prevents unecessary switching.
if
!
(
d
.
isFetchingHashes
()
||
d
.
isDownloadingBlocks
()
||
d
.
isProcessing
()
)
{
if
!
d
.
isBusy
(
)
{
// selected peer must be better than our own
// XXX we also check the peer's recent hash to make sure we
// don't have it. Some peers report (i think) incorrect TD.
...
...
@@ -142,6 +165,7 @@ func (d *Downloader) selectPeer(p *peer) {
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"New peer with highest TD ="
,
p
.
td
)
d
.
syncCh
<-
syncPack
{
p
,
p
.
recentHash
,
false
}
}
}
func
(
d
*
Downloader
)
update
()
{
...
...
@@ -149,30 +173,13 @@ out:
for
{
select
{
case
sync
:=
<-
d
.
syncCh
:
selectedPeer
:=
sync
.
peer
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Synchronising with network using:"
,
selectedPeer
.
id
)
// Start the fetcher. This will block the update entirely
// interupts need to be send to the appropriate channels
// respectively.
if
err
:=
d
.
startFetchingHashes
(
selectedPeer
,
sync
.
hash
,
sync
.
ignoreInitial
);
err
!=
nil
{
// handle error
glog
.
V
(
logger
.
Debug
)
.
Infoln
(
"Error fetching hashes:"
,
err
)
// XXX Reset
var
peer
*
peer
=
sync
.
peer
err
:=
d
.
getFromPeer
(
peer
,
sync
.
hash
,
sync
.
ignoreInitial
)
if
err
!=
nil
{
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
err
)
break
}
// Start fetching blocks in paralel. The strategy is simple
// take any available peers, seserve a chunk for each peer available,
// let the peer deliver the chunkn and periodically check if a peer
// has timedout. When done downloading, process blocks.
if
err
:=
d
.
startFetchingBlocks
(
selectedPeer
);
err
!=
nil
{
glog
.
V
(
logger
.
Debug
)
.
Infoln
(
"Error downloading blocks:"
,
err
)
// XXX reset
break
}
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Sync completed"
)
d
.
process
()
case
<-
d
.
quit
:
break
out
...
...
@@ -182,6 +189,9 @@ out:
// XXX Make synchronous
func
(
d
*
Downloader
)
startFetchingHashes
(
p
*
peer
,
hash
common
.
Hash
,
ignoreInitial
bool
)
error
{
atomic
.
StoreInt32
(
&
d
.
fetchingHashes
,
1
)
defer
atomic
.
StoreInt32
(
&
d
.
fetchingHashes
,
0
)
glog
.
V
(
logger
.
Debug
)
.
Infof
(
"Downloading hashes (%x) from %s"
,
hash
.
Bytes
()[
:
4
],
p
.
id
)
start
:=
time
.
Now
()
...
...
@@ -192,15 +202,15 @@ func (d *Downloader) startFetchingHashes(p *peer, hash common.Hash, ignoreInitia
// Add the hash to the queue first
d
.
queue
.
hashPool
.
Add
(
hash
)
}
// Get the first batch of hashes
p
.
getHashes
(
hash
)
atomic
.
StoreInt32
(
&
d
.
fetchingHashes
,
1
)
failureResponse
:=
time
.
NewTimer
(
hashTtl
)
out
:
for
{
select
{
case
hashes
:=
<-
d
.
H
ashCh
:
case
hashes
:=
<-
d
.
h
ashCh
:
var
done
bool
// determines whether we're done fetching hashes (i.e. common hash found)
hashSet
:=
set
.
New
()
for
_
,
hash
:=
range
hashes
{
...
...
@@ -216,26 +226,36 @@ out:
d
.
queue
.
put
(
hashSet
)
// Add hashes to the chunk set
// Check if we're done fetching
if
!
done
&&
len
(
hashes
)
>
0
{
//fmt.Println("re-fetch. current =", d.queue.hashPool.Size())
if
len
(
hashes
)
==
0
{
// Make sure the peer actually gave you something valid
glog
.
V
(
logger
.
Debug
)
.
Infof
(
"Peer (%s) responded with empty hash set
\n
"
,
p
.
id
)
d
.
queue
.
reset
()
return
errEmptyHashSet
}
else
if
!
done
{
// Check if we're done fetching
// Get the next set of hashes
p
.
getHashes
(
hashes
[
len
(
hashes
)
-
1
])
atomic
.
StoreInt32
(
&
d
.
fetchingHashes
,
1
)
}
else
{
atomic
.
StoreInt32
(
&
d
.
fetchingHashes
,
0
)
}
else
{
// we're done
break
out
}
case
<-
failureResponse
.
C
:
glog
.
V
(
logger
.
Debug
)
.
Infof
(
"Peer (%s) didn't respond in time for hash request
\n
"
,
p
.
id
)
// TODO instead of reseting the queue select a new peer from which we can start downloading hashes.
// 1. check for peer's best hash to be included in the current hash set;
// 2. resume from last point (hashes[len(hashes)-1]) using the newly selected peer.
d
.
queue
.
reset
()
return
errTimeout
}
}
glog
.
V
(
logger
.
Detail
)
.
Infof
(
"Downloaded hashes (%d)
. Took
%v
\n
"
,
d
.
queue
.
hashPool
.
Size
(),
time
.
Since
(
start
))
glog
.
V
(
logger
.
Detail
)
.
Infof
(
"Downloaded hashes (%d)
in
%v
\n
"
,
d
.
queue
.
hashPool
.
Size
(),
time
.
Since
(
start
))
return
nil
}
func
(
d
*
Downloader
)
startFetchingBlocks
(
p
*
peer
)
error
{
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Downloading"
,
d
.
queue
.
hashPool
.
Size
(),
"block
s
"
)
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Downloading"
,
d
.
queue
.
hashPool
.
Size
(),
"block
(s)
"
)
atomic
.
StoreInt32
(
&
d
.
downloadingBlocks
,
1
)
defer
atomic
.
StoreInt32
(
&
d
.
downloadingBlocks
,
0
)
start
:=
time
.
Now
()
...
...
@@ -245,18 +265,18 @@ out:
for
{
select
{
case
blockPack
:=
<-
d
.
blockCh
:
// If the peer was previously banned and failed to deliver it's pack
// in a reasonable time frame, ignore it's message.
if
d
.
peers
[
blockPack
.
peerId
]
!=
nil
{
d
.
peers
[
blockPack
.
peerId
]
.
promote
()
d
.
queue
.
deliver
(
blockPack
.
peerId
,
blockPack
.
blocks
)
d
.
peers
.
setState
(
blockPack
.
peerId
,
idleState
)
}
case
<-
ticker
.
C
:
// If there are unrequested hashes left start fetching
// from the available peers.
if
d
.
queue
.
hashPool
.
Size
()
>
0
{
availablePeers
:=
d
.
peers
.
get
(
idleState
)
if
len
(
availablePeers
)
==
0
{
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"No peers available out of"
,
len
(
d
.
peers
))
}
for
_
,
peer
:=
range
availablePeers
{
// Get a possible chunk. If nil is returned no chunk
// could be returned due to no hashes available.
...
...
@@ -265,7 +285,6 @@ out:
continue
}
//fmt.Println("fetching for", peer.id)
// XXX make fetch blocking.
// Fetch the chunk and check for error. If the peer was somehow
// already fetching a chunk due to a bug, it will be returned to
...
...
@@ -276,13 +295,20 @@ out:
d
.
queue
.
put
(
chunk
.
hashes
)
}
}
atomic
.
StoreInt32
(
&
d
.
downloadingBlocks
,
1
)
// make sure that we have peers available for fetching. If all peers have been tried
// and all failed throw an error
if
len
(
d
.
queue
.
fetching
)
==
0
{
d
.
queue
.
reset
()
d
.
peers
.
reset
()
return
fmt
.
Errorf
(
"%v avaialable = %d. total = %d"
,
errPeersUnavailable
,
len
(
availablePeers
),
len
(
d
.
peers
))
}
}
else
if
len
(
d
.
queue
.
fetching
)
==
0
{
// When there are no more queue and no more `fetching`. We can
// safely assume we're done. Another part of the process will check
// for parent errors and will re-request anything that's missing
atomic
.
StoreInt32
(
&
d
.
downloadingBlocks
,
0
)
// Break out so that we can process with processing blocks
break
out
}
else
{
// Check for bad peers. Bad peers may indicate a peer not responding
...
...
@@ -293,10 +319,10 @@ out:
d
.
queue
.
mu
.
Lock
()
var
badPeers
[]
string
for
pid
,
chunk
:=
range
d
.
queue
.
fetching
{
if
time
.
Since
(
chunk
.
itime
)
>
5
*
time
.
Second
{
if
time
.
Since
(
chunk
.
itime
)
>
blockTtl
{
badPeers
=
append
(
badPeers
,
pid
)
// remove peer as good peer from peer list
d
.
UnregisterPeer
(
pid
)
//
d.UnregisterPeer(pid)
}
}
d
.
queue
.
mu
.
Unlock
()
...
...
@@ -313,26 +339,42 @@ out:
d
.
queue
.
deliver
(
pid
,
nil
)
if
peer
:=
d
.
peers
[
pid
];
peer
!=
nil
{
peer
.
demote
()
peer
.
reset
()
}
}
}
//fmt.Println(d.queue.hashPool.Size(), len(d.queue.fetching))
}
}
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Download blocks: done. Took"
,
time
.
Since
(
start
))
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Downloaded block(s) in"
,
time
.
Since
(
start
))
return
nil
}
func
(
d
*
Downloader
)
AddHashes
(
id
string
,
hashes
[]
common
.
Hash
)
error
{
// make sure that the hashes that are being added are actually from the peer
// that's the current active peer. hashes that have been received from other
// peers are dropped and ignored.
if
d
.
activePeer
!=
id
{
return
fmt
.
Errorf
(
"received hashes from %s while active peer is %s"
,
id
,
d
.
activePeer
)
}
d
.
hashCh
<-
hashes
return
nil
}
// Add an (unrequested) block to the downloader. This is usually done through the
// NewBlockMsg by the protocol handler.
func
(
d
*
Downloader
)
AddBlock
(
id
string
,
block
*
types
.
Block
,
td
*
big
.
Int
)
{
// Adding blocks is done synchronously. if there are missing blocks, blocks will be
// fetched first. If the downloader is busy or if some other processed failed an error
// will be returned.
func
(
d
*
Downloader
)
AddBlock
(
id
string
,
block
*
types
.
Block
,
td
*
big
.
Int
)
error
{
hash
:=
block
.
Hash
()
if
d
.
hasBlock
(
hash
)
{
return
return
fmt
.
Errorf
(
"known block %x"
,
hash
.
Bytes
()[
:
4
])
}
peer
:=
d
.
peers
.
getPeer
(
id
)
...
...
@@ -340,7 +382,7 @@ func (d *Downloader) AddBlock(id string, block *types.Block, td *big.Int) {
// and add the block. Otherwise just ignore it
if
peer
==
nil
{
glog
.
V
(
logger
.
Detail
)
.
Infof
(
"Ignored block from bad peer %s
\n
"
,
id
)
return
return
errBadPeer
}
peer
.
mu
.
Lock
()
...
...
@@ -353,17 +395,24 @@ func (d *Downloader) AddBlock(id string, block *types.Block, td *big.Int) {
d
.
queue
.
addBlock
(
id
,
block
,
td
)
// if neither go ahead to process
if
!
(
d
.
isFetchingHashes
()
||
d
.
isDownloadingBlocks
())
{
if
d
.
isBusy
()
{
return
errBusy
}
// Check if the parent of the received block is known.
// If the block is not know, request it otherwise, request.
phash
:=
block
.
ParentHash
()
if
!
d
.
hasBlock
(
phash
)
{
glog
.
V
(
logger
.
Detail
)
.
Infof
(
"Missing parent %x, requires fetching
\n
"
,
phash
.
Bytes
()[
:
4
])
d
.
syncCh
<-
syncPack
{
peer
,
peer
.
recentHash
,
true
}
}
else
{
d
.
process
()
// Get the missing hashes from the peer (synchronously)
err
:=
d
.
getFromPeer
(
peer
,
peer
.
recentHash
,
true
)
if
err
!=
nil
{
return
err
}
}
return
d
.
process
()
}
// Deliver a chunk to the downloader. This is usually done through the BlocksMsg by
...
...
@@ -383,8 +432,11 @@ func (d *Downloader) process() error {
// to a seperate goroutine where it periodically checks for linked pieces.
types
.
BlockBy
(
types
.
Number
)
.
Sort
(
d
.
queue
.
blocks
)
blocks
:=
d
.
queue
.
blocks
if
len
(
blocks
)
==
0
{
return
nil
}
glog
.
V
(
logger
.
Debug
)
.
Info
ln
(
"Inserting chain with"
,
len
(
blocks
),
"blocks"
)
glog
.
V
(
logger
.
Debug
)
.
Info
f
(
"Inserting chain with %d blocks (#%v - #%v)
\n
"
,
len
(
blocks
),
blocks
[
0
]
.
Number
(),
blocks
[
len
(
blocks
)
-
1
]
.
Number
()
)
var
err
error
// Loop untill we're out of blocks
...
...
@@ -408,6 +460,11 @@ func (d *Downloader) process() error {
}
}
break
}
else
if
err
!=
nil
{
// Reset chain completely. This needs much, much improvement.
// instead: check all blocks leading down to this block false block and remove it
blocks
=
nil
break
}
blocks
=
blocks
[
max
:
]
}
...
...
@@ -432,3 +489,7 @@ func (d *Downloader) isDownloadingBlocks() bool {
func
(
d
*
Downloader
)
isProcessing
()
bool
{
return
atomic
.
LoadInt32
(
&
d
.
processingBlocks
)
==
1
}
func
(
d
*
Downloader
)
isBusy
()
bool
{
return
d
.
isFetchingHashes
()
||
d
.
isDownloadingBlocks
()
||
d
.
isProcessing
()
}
eth/downloader/peer.go
View file @
71aa5fe8
...
...
@@ -6,6 +6,7 @@ import (
"sync"
"github.com/ethereum/go-ethereum/common"
"gopkg.in/fatih/set.v0"
)
const
(
...
...
@@ -19,6 +20,12 @@ type blockFetcherFn func([]common.Hash) error
// XXX make threadsafe!!!!
type
peers
map
[
string
]
*
peer
func
(
p
peers
)
reset
()
{
for
_
,
peer
:=
range
p
{
peer
.
reset
()
}
}
func
(
p
peers
)
get
(
state
int
)
[]
*
peer
{
var
peers
[]
*
peer
for
_
,
peer
:=
range
p
{
...
...
@@ -64,13 +71,23 @@ type peer struct {
td
*
big
.
Int
recentHash
common
.
Hash
requested
*
set
.
Set
getHashes
hashFetcherFn
getBlocks
blockFetcherFn
}
// create a new peer
func
newPeer
(
id
string
,
td
*
big
.
Int
,
hash
common
.
Hash
,
getHashes
hashFetcherFn
,
getBlocks
blockFetcherFn
)
*
peer
{
return
&
peer
{
id
:
id
,
td
:
td
,
recentHash
:
hash
,
getHashes
:
getHashes
,
getBlocks
:
getBlocks
,
state
:
idleState
}
return
&
peer
{
id
:
id
,
td
:
td
,
recentHash
:
hash
,
getHashes
:
getHashes
,
getBlocks
:
getBlocks
,
state
:
idleState
,
requested
:
set
.
New
(),
}
}
// fetch a chunk using the peer
...
...
@@ -82,6 +99,8 @@ func (p *peer) fetch(chunk *chunk) error {
return
errors
.
New
(
"peer already fetching chunk"
)
}
p
.
requested
.
Merge
(
chunk
.
hashes
)
// set working state
p
.
state
=
workingState
// convert the set to a fetchable slice
...
...
@@ -115,3 +134,7 @@ func (p *peer) demote() {
p
.
rep
=
0
}
}
func
(
p
*
peer
)
reset
()
{
p
.
state
=
idleState
}
eth/downloader/queue.go
View file @
71aa5fe8
...
...
@@ -31,6 +31,17 @@ func newqueue() *queue {
}
}
func
(
c
*
queue
)
reset
()
{
c
.
mu
.
Lock
()
defer
c
.
mu
.
Unlock
()
c
.
hashPool
.
Clear
()
c
.
fetchPool
.
Clear
()
c
.
blockHashes
.
Clear
()
c
.
blocks
=
nil
c
.
fetching
=
make
(
map
[
string
]
*
chunk
)
}
// reserve a `max` set of hashes for `p` peer.
func
(
c
*
queue
)
get
(
p
*
peer
,
max
int
)
*
chunk
{
c
.
mu
.
Lock
()
...
...
@@ -49,11 +60,19 @@ func (c *queue) get(p *peer, max int) *chunk {
return
false
}
// Skip any hashes that have previously been requested from the peer
if
!
p
.
requested
.
Has
(
v
)
{
hashes
.
Add
(
v
)
i
++
}
return
true
})
// if no hashes can be requested return a nil chunk
if
hashes
.
Size
()
==
0
{
return
nil
}
// remove the fetchable hashes from hash pool
c
.
hashPool
.
Separate
(
hashes
)
c
.
fetchPool
.
Merge
(
hashes
)
...
...
eth/downloader/synchronous.go
0 → 100644
View file @
71aa5fe8
package
downloader
import
(
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
)
// THIS IS PENDING AND TO DO CHANGES FOR MAKING THE DOWNLOADER SYNCHRONOUS
// SynchroniseWithPeer will select the peer and use it for synchronising. If an empty string is given
// it will use the best peer possible and synchronise if it's TD is higher than our own. If any of the
// checks fail an error will be returned. This method is synchronous
func
(
d
*
Downloader
)
SynchroniseWithPeer
(
id
string
)
(
types
.
Blocks
,
error
)
{
// Check if we're busy
if
d
.
isBusy
()
{
return
nil
,
errBusy
}
// Attempt to select a peer. This can either be nothing, which returns, best peer
// or selected peer. If no peer could be found an error will be returned
var
p
*
peer
if
len
(
id
)
==
0
{
p
=
d
.
peers
[
id
]
if
p
==
nil
{
return
nil
,
errUnknownPeer
}
}
else
{
p
=
d
.
peers
.
bestPeer
()
}
// Make sure our td is lower than the peer's td
if
p
.
td
.
Cmp
(
d
.
currentTd
())
<=
0
||
d
.
hasBlock
(
p
.
recentHash
)
{
return
nil
,
errLowTd
}
// Get the hash from the peer and initiate the downloading progress.
err
:=
d
.
getFromPeer
(
p
,
p
.
recentHash
,
false
)
if
err
!=
nil
{
return
nil
,
err
}
return
d
.
queue
.
blocks
,
nil
}
// Synchronise will synchronise using the best peer.
func
(
d
*
Downloader
)
Synchronise
()
(
types
.
Blocks
,
error
)
{
return
d
.
SynchroniseWithPeer
(
""
)
}
func
(
d
*
Downloader
)
getFromPeer
(
p
*
peer
,
hash
common
.
Hash
,
ignoreInitial
bool
)
error
{
d
.
activePeer
=
p
.
id
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Synchronising with the network using:"
,
p
.
id
)
// Start the fetcher. This will block the update entirely
// interupts need to be send to the appropriate channels
// respectively.
if
err
:=
d
.
startFetchingHashes
(
p
,
hash
,
ignoreInitial
);
err
!=
nil
{
// handle error
glog
.
V
(
logger
.
Debug
)
.
Infoln
(
"Error fetching hashes:"
,
err
)
// XXX Reset
return
err
}
// Start fetching blocks in paralel. The strategy is simple
// take any available peers, seserve a chunk for each peer available,
// let the peer deliver the chunkn and periodically check if a peer
// has timedout. When done downloading, process blocks.
if
err
:=
d
.
startFetchingBlocks
(
p
);
err
!=
nil
{
glog
.
V
(
logger
.
Debug
)
.
Infoln
(
"Error downloading blocks:"
,
err
)
// XXX reset
return
err
}
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Sync completed"
)
return
nil
}
eth/handler.go
0 → 100644
View file @
71aa5fe8
package
eth
// XXX Fair warning, most of the code is re-used from the old protocol. Please be aware that most of this will actually change
// The idea is that most of the calls within the protocol will become synchronous.
// Block downloading and block processing will be complete seperate processes
/*
# Possible scenarios
// Synching scenario
// Use the best peer to synchronise
blocks, err := pm.downloader.Synchronise()
if err != nil {
// handle
break
}
pm.chainman.InsertChain(blocks)
// Receiving block with known parent
if parent_exist {
if err := pm.chainman.InsertChain(block); err != nil {
// handle
break
}
pm.BroadcastBlock(block)
}
// Receiving block with unknown parent
blocks, err := pm.downloader.SynchroniseWithPeer(peer)
if err != nil {
// handle
break
}
pm.chainman.InsertChain(blocks)
*/
import
(
"fmt"
"math"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
)
func
errResp
(
code
errCode
,
format
string
,
v
...
interface
{})
error
{
return
fmt
.
Errorf
(
"%v - %v"
,
code
,
fmt
.
Sprintf
(
format
,
v
...
))
}
type
hashFetcherFn
func
(
common
.
Hash
)
error
type
blockFetcherFn
func
([]
common
.
Hash
)
error
// extProt is an interface which is passed around so we can expose GetHashes and GetBlock without exposing it to the rest of the protocol
// extProt is passed around to peers which require to GetHashes and GetBlocks
type
extProt
struct
{
getHashes
hashFetcherFn
getBlocks
blockFetcherFn
}
func
(
ep
extProt
)
GetHashes
(
hash
common
.
Hash
)
error
{
return
ep
.
getHashes
(
hash
)
}
func
(
ep
extProt
)
GetBlock
(
hashes
[]
common
.
Hash
)
error
{
return
ep
.
getBlocks
(
hashes
)
}
type
ProtocolManager
struct
{
protVer
,
netId
int
txpool
txPool
chainman
*
core
.
ChainManager
downloader
*
downloader
.
Downloader
pmu
sync
.
Mutex
peers
map
[
string
]
*
peer
SubProtocol
p2p
.
Protocol
}
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the ethereum network.
func
NewProtocolManager
(
protocolVersion
,
networkId
int
,
txpool
txPool
,
chainman
*
core
.
ChainManager
,
downloader
*
downloader
.
Downloader
)
*
ProtocolManager
{
manager
:=
&
ProtocolManager
{
txpool
:
txpool
,
chainman
:
chainman
,
downloader
:
downloader
,
peers
:
make
(
map
[
string
]
*
peer
),
}
manager
.
SubProtocol
=
p2p
.
Protocol
{
Name
:
"eth"
,
Version
:
uint
(
protocolVersion
),
Length
:
ProtocolLength
,
Run
:
func
(
p
*
p2p
.
Peer
,
rw
p2p
.
MsgReadWriter
)
error
{
peer
:=
manager
.
newPeer
(
protocolVersion
,
networkId
,
p
,
rw
)
err
:=
manager
.
handle
(
peer
)
//glog.V(logger.Detail).Infof("[%s]: %v\n", peer.id, err)
return
err
},
}
return
manager
}
func
(
pm
*
ProtocolManager
)
newPeer
(
pv
,
nv
int
,
p
*
p2p
.
Peer
,
rw
p2p
.
MsgReadWriter
)
*
peer
{
td
,
current
,
genesis
:=
pm
.
chainman
.
Status
()
return
newPeer
(
pv
,
nv
,
genesis
,
current
,
td
,
p
,
rw
)
}
func
(
pm
*
ProtocolManager
)
handle
(
p
*
peer
)
error
{
if
err
:=
p
.
handleStatus
();
err
!=
nil
{
return
err
}
pm
.
pmu
.
Lock
()
pm
.
peers
[
p
.
id
]
=
p
pm
.
pmu
.
Unlock
()
pm
.
downloader
.
RegisterPeer
(
p
.
id
,
p
.
td
,
p
.
currentHash
,
p
.
requestHashes
,
p
.
requestBlocks
)
defer
func
()
{
pm
.
pmu
.
Lock
()
defer
pm
.
pmu
.
Unlock
()
delete
(
pm
.
peers
,
p
.
id
)
pm
.
downloader
.
UnregisterPeer
(
p
.
id
)
}()
// propagate existing transactions. new transactions appearing
// after this will be sent via broadcasts.
if
err
:=
p
.
sendTransactions
(
pm
.
txpool
.
GetTransactions
());
err
!=
nil
{
return
err
}
// main loop. handle incoming messages.
for
{
if
err
:=
pm
.
handleMsg
(
p
);
err
!=
nil
{
return
err
}
}
return
nil
}
func
(
self
*
ProtocolManager
)
handleMsg
(
p
*
peer
)
error
{
msg
,
err
:=
p
.
rw
.
ReadMsg
()
if
err
!=
nil
{
return
err
}
if
msg
.
Size
>
ProtocolMaxMsgSize
{
return
errResp
(
ErrMsgTooLarge
,
"%v > %v"
,
msg
.
Size
,
ProtocolMaxMsgSize
)
}
// make sure that the payload has been fully consumed
defer
msg
.
Discard
()
switch
msg
.
Code
{
case
GetTxMsg
:
// ignore
case
StatusMsg
:
return
errResp
(
ErrExtraStatusMsg
,
"uncontrolled status message"
)
case
TxMsg
:
// TODO: rework using lazy RLP stream
var
txs
[]
*
types
.
Transaction
if
err
:=
msg
.
Decode
(
&
txs
);
err
!=
nil
{
return
errResp
(
ErrDecode
,
"msg %v: %v"
,
msg
,
err
)
}
for
i
,
tx
:=
range
txs
{
if
tx
==
nil
{
return
errResp
(
ErrDecode
,
"transaction %d is nil"
,
i
)
}
jsonlogger
.
LogJson
(
&
logger
.
EthTxReceived
{
TxHash
:
tx
.
Hash
()
.
Hex
(),
RemoteId
:
p
.
ID
()
.
String
(),
})
}
self
.
txpool
.
AddTransactions
(
txs
)
case
GetBlockHashesMsg
:
var
request
getBlockHashesMsgData
if
err
:=
msg
.
Decode
(
&
request
);
err
!=
nil
{
return
errResp
(
ErrDecode
,
"->msg %v: %v"
,
msg
,
err
)
}
if
request
.
Amount
>
maxHashes
{
request
.
Amount
=
maxHashes
}
hashes
:=
self
.
chainman
.
GetBlockHashesFromHash
(
request
.
Hash
,
request
.
Amount
)
if
glog
.
V
(
logger
.
Debug
)
{
if
len
(
hashes
)
==
0
{
glog
.
Infof
(
"invalid block hash %x"
,
request
.
Hash
.
Bytes
()[
:
4
])
}
}
// returns either requested hashes or nothing (i.e. not found)
return
p
.
sendBlockHashes
(
hashes
)
case
BlockHashesMsg
:
msgStream
:=
rlp
.
NewStream
(
msg
.
Payload
)
var
hashes
[]
common
.
Hash
if
err
:=
msgStream
.
Decode
(
&
hashes
);
err
!=
nil
{
break
}
err
:=
self
.
downloader
.
AddHashes
(
p
.
id
,
hashes
)
if
err
!=
nil
{
glog
.
V
(
logger
.
Debug
)
.
Infoln
(
err
)
}
case
GetBlocksMsg
:
msgStream
:=
rlp
.
NewStream
(
msg
.
Payload
)
if
_
,
err
:=
msgStream
.
List
();
err
!=
nil
{
return
err
}
var
blocks
[]
*
types
.
Block
var
i
int
for
{
i
++
var
hash
common
.
Hash
err
:=
msgStream
.
Decode
(
&
hash
)
if
err
==
rlp
.
EOL
{
break
}
else
if
err
!=
nil
{
return
errResp
(
ErrDecode
,
"msg %v: %v"
,
msg
,
err
)
}
block
:=
self
.
chainman
.
GetBlock
(
hash
)
if
block
!=
nil
{
blocks
=
append
(
blocks
,
block
)
}
if
i
==
maxBlocks
{
break
}
}
return
p
.
sendBlocks
(
blocks
)
case
BlocksMsg
:
msgStream
:=
rlp
.
NewStream
(
msg
.
Payload
)
var
blocks
[]
*
types
.
Block
if
err
:=
msgStream
.
Decode
(
&
blocks
);
err
!=
nil
{
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"Decode error"
,
err
)
blocks
=
nil
}
self
.
downloader
.
DeliverChunk
(
p
.
id
,
blocks
)
case
NewBlockMsg
:
var
request
newBlockMsgData
if
err
:=
msg
.
Decode
(
&
request
);
err
!=
nil
{
return
errResp
(
ErrDecode
,
"%v: %v"
,
msg
,
err
)
}
if
err
:=
request
.
Block
.
ValidateFields
();
err
!=
nil
{
return
errResp
(
ErrDecode
,
"block validation %v: %v"
,
msg
,
err
)
}
hash
:=
request
.
Block
.
Hash
()
// Add the block hash as a known hash to the peer. This will later be used to detirmine
// who should receive this.
p
.
blockHashes
.
Add
(
hash
)
_
,
chainHead
,
_
:=
self
.
chainman
.
Status
()
jsonlogger
.
LogJson
(
&
logger
.
EthChainReceivedNewBlock
{
BlockHash
:
hash
.
Hex
(),
BlockNumber
:
request
.
Block
.
Number
(),
// this surely must be zero
ChainHeadHash
:
chainHead
.
Hex
(),
BlockPrevHash
:
request
.
Block
.
ParentHash
()
.
Hex
(),
RemoteId
:
p
.
ID
()
.
String
(),
})
// Make sure the block isn't already known. If this is the case simply drop
// the message and move on. If the TD is < currentTd; drop it as well. If this
// chain at some point becomes canonical, the downloader will fetch it.
if
self
.
chainman
.
HasBlock
(
hash
)
{
break
}
/* XXX unsure about this
if self.chainman.Td().Cmp(request.TD) > 0 && new(big.Int).Add(request.Block.Number(), big.NewInt(7)).Cmp(self.chainman.CurrentBlock().Number()) < 0 {
glog.V(logger.Debug).Infoln("dropped block", request.Block.Number(), "due to low TD", request.TD)
break
}
*/
// Attempt to insert the newly received by checking if the parent exists.
// if the parent exists we process the block and propagate to our peers
// if the parent does not exists we delegate to the downloader.
// NOTE we can reduce chatter by dropping blocks with Td < currentTd
if
self
.
chainman
.
HasBlock
(
request
.
Block
.
ParentHash
())
{
if
err
:=
self
.
chainman
.
InsertChain
(
types
.
Blocks
{
request
.
Block
});
err
!=
nil
{
// handle error
return
nil
}
self
.
BroadcastBlock
(
hash
,
request
.
Block
)
//fmt.Println(request.Block.Hash().Hex(), "our calculated TD =", request.Block.Td, "their TD =", request.TD)
}
else
{
// adding blocks is synchronous
go
func
()
{
err
:=
self
.
downloader
.
AddBlock
(
p
.
id
,
request
.
Block
,
request
.
TD
)
if
err
!=
nil
{
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"downloader err:"
,
err
)
return
}
self
.
BroadcastBlock
(
hash
,
request
.
Block
)
//fmt.Println(request.Block.Hash().Hex(), "our calculated TD =", request.Block.Td, "their TD =", request.TD)
}()
}
default
:
return
errResp
(
ErrInvalidMsgCode
,
"%v"
,
msg
.
Code
)
}
return
nil
}
// BroadcastBlock will propagate the block to its connected peers. It will sort
// out which peers do not contain the block in their block set and will do a
// sqrt(peers) to determine the amount of peers we broadcast to.
func
(
pm
*
ProtocolManager
)
BroadcastBlock
(
hash
common
.
Hash
,
block
*
types
.
Block
)
{
pm
.
pmu
.
Lock
()
defer
pm
.
pmu
.
Unlock
()
// Find peers who don't know anything about the given hash. Peers that
// don't know about the hash will be a candidate for the broadcast loop
var
peers
[]
*
peer
for
_
,
peer
:=
range
pm
.
peers
{
if
!
peer
.
blockHashes
.
Has
(
hash
)
{
peers
=
append
(
peers
,
peer
)
}
}
// Broadcast block to peer set
peers
=
peers
[
:
int
(
math
.
Sqrt
(
float64
(
len
(
peers
))))]
for
_
,
peer
:=
range
peers
{
peer
.
sendNewBlock
(
block
)
}
glog
.
V
(
logger
.
Detail
)
.
Infoln
(
"broadcast block to"
,
len
(
peers
),
"peers"
)
}
eth/peer.go
0 → 100644
View file @
71aa5fe8
package
eth
import
(
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/p2p"
"gopkg.in/fatih/set.v0"
)
type
statusMsgData
struct
{
ProtocolVersion
uint32
NetworkId
uint32
TD
*
big
.
Int
CurrentBlock
common
.
Hash
GenesisBlock
common
.
Hash
}
type
getBlockHashesMsgData
struct
{
Hash
common
.
Hash
Amount
uint64
}
type
peer
struct
{
*
p2p
.
Peer
rw
p2p
.
MsgReadWriter
protv
,
netid
int
currentHash
common
.
Hash
id
string
td
*
big
.
Int
genesis
,
ourHash
common
.
Hash
ourTd
*
big
.
Int
txHashes
*
set
.
Set
blockHashes
*
set
.
Set
}
func
newPeer
(
protv
,
netid
int
,
genesis
,
currentHash
common
.
Hash
,
td
*
big
.
Int
,
p
*
p2p
.
Peer
,
rw
p2p
.
MsgReadWriter
)
*
peer
{
id
:=
p
.
ID
()
return
&
peer
{
Peer
:
p
,
rw
:
rw
,
genesis
:
genesis
,
ourHash
:
currentHash
,
ourTd
:
td
,
protv
:
protv
,
netid
:
netid
,
id
:
fmt
.
Sprintf
(
"%x"
,
id
[
:
8
]),
txHashes
:
set
.
New
(),
blockHashes
:
set
.
New
(),
}
}
// sendTransactions sends transactions to the peer and includes the hashes
// in it's tx hash set for future reference. The tx hash will allow the
// manager to check whether the peer has already received this particular
// transaction
func
(
p
*
peer
)
sendTransactions
(
txs
types
.
Transactions
)
error
{
for
_
,
tx
:=
range
txs
{
p
.
txHashes
.
Add
(
tx
.
Hash
())
}
return
p2p
.
Send
(
p
.
rw
,
TxMsg
,
txs
)
}
func
(
p
*
peer
)
sendBlockHashes
(
hashes
[]
common
.
Hash
)
error
{
return
p2p
.
Send
(
p
.
rw
,
BlockHashesMsg
,
hashes
)
}
func
(
p
*
peer
)
sendBlocks
(
blocks
[]
*
types
.
Block
)
error
{
return
p2p
.
Send
(
p
.
rw
,
BlocksMsg
,
blocks
)
}
func
(
p
*
peer
)
sendNewBlock
(
block
*
types
.
Block
)
error
{
p
.
blockHashes
.
Add
(
block
.
Hash
())
return
p2p
.
Send
(
p
.
rw
,
NewBlockMsg
,
[]
interface
{}{
block
,
block
.
Td
})
}
func
(
p
*
peer
)
requestHashes
(
from
common
.
Hash
)
error
{
p
.
Debugf
(
"fetching hashes (%d) %x...
\n
"
,
maxHashes
,
from
[
0
:
4
])
return
p2p
.
Send
(
p
.
rw
,
GetBlockHashesMsg
,
getBlockHashesMsgData
{
from
,
maxHashes
})
}
func
(
p
*
peer
)
requestBlocks
(
hashes
[]
common
.
Hash
)
error
{
p
.
Debugf
(
"fetching %v blocks"
,
len
(
hashes
))
return
p2p
.
Send
(
p
.
rw
,
GetBlocksMsg
,
hashes
)
}
func
(
p
*
peer
)
handleStatus
()
error
{
errc
:=
make
(
chan
error
,
1
)
go
func
()
{
errc
<-
p2p
.
Send
(
p
.
rw
,
StatusMsg
,
&
statusMsgData
{
ProtocolVersion
:
uint32
(
p
.
protv
),
NetworkId
:
uint32
(
p
.
netid
),
TD
:
p
.
ourTd
,
CurrentBlock
:
p
.
ourHash
,
GenesisBlock
:
p
.
genesis
,
})
}()
// read and handle remote status
msg
,
err
:=
p
.
rw
.
ReadMsg
()
if
err
!=
nil
{
return
err
}
if
msg
.
Code
!=
StatusMsg
{
return
errResp
(
ErrNoStatusMsg
,
"first msg has code %x (!= %x)"
,
msg
.
Code
,
StatusMsg
)
}
if
msg
.
Size
>
ProtocolMaxMsgSize
{
return
errResp
(
ErrMsgTooLarge
,
"%v > %v"
,
msg
.
Size
,
ProtocolMaxMsgSize
)
}
var
status
statusMsgData
if
err
:=
msg
.
Decode
(
&
status
);
err
!=
nil
{
return
errResp
(
ErrDecode
,
"msg %v: %v"
,
msg
,
err
)
}
if
status
.
GenesisBlock
!=
p
.
genesis
{
return
errResp
(
ErrGenesisBlockMismatch
,
"%x (!= %x)"
,
status
.
GenesisBlock
,
p
.
genesis
)
}
if
int
(
status
.
NetworkId
)
!=
p
.
netid
{
return
errResp
(
ErrNetworkIdMismatch
,
"%d (!= %d)"
,
status
.
NetworkId
,
p
.
netid
)
}
if
int
(
status
.
ProtocolVersion
)
!=
p
.
protv
{
return
errResp
(
ErrProtocolVersionMismatch
,
"%d (!= %d)"
,
status
.
ProtocolVersion
,
p
.
protv
)
}
// Set the total difficulty of the peer
p
.
td
=
status
.
TD
// set the best hash of the peer
p
.
currentHash
=
status
.
CurrentBlock
return
<-
errc
}
eth/protocol.go
View file @
71aa5fe8
package
eth
import
(
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/errs"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
)
const
(
...
...
@@ -18,8 +12,8 @@ const (
NetworkId
=
0
ProtocolLength
=
uint64
(
8
)
ProtocolMaxMsgSize
=
10
*
1024
*
1024
maxHashes
=
256
maxBlocks
=
64
maxHashes
=
512
maxBlocks
=
128
)
// eth protocol message codes
...
...
@@ -34,6 +28,8 @@ const (
NewBlockMsg
)
type
errCode
int
const
(
ErrMsgTooLarge
=
iota
ErrDecode
...
...
@@ -46,6 +42,11 @@ const (
ErrSuspendedPeer
)
func
(
e
errCode
)
String
()
string
{
return
errorToString
[
int
(
e
)]
}
// XXX change once legacy code is out
var
errorToString
=
map
[
int
]
string
{
ErrMsgTooLarge
:
"Message too long"
,
ErrDecode
:
"Invalid message"
,
...
...
@@ -58,20 +59,6 @@ var errorToString = map[int]string{
ErrSuspendedPeer
:
"Suspended peer"
,
}
// ethProtocol represents the ethereum wire protocol
// instance is running on each peer
type
ethProtocol
struct
{
txPool
txPool
chainManager
chainManager
blockPool
blockPool
peer
*
p2p
.
Peer
id
string
rw
p2p
.
MsgReadWriter
errors
*
errs
.
Errors
protocolVersion
int
networkId
int
}
// backend is the interface the ethereum protocol backend should implement
// used as an argument to EthProtocol
type
txPool
interface
{
...
...
@@ -85,308 +72,8 @@ type chainManager interface {
Status
()
(
td
*
big
.
Int
,
currentBlock
common
.
Hash
,
genesisBlock
common
.
Hash
)
}
type
blockPool
interface
{
AddBlockHashes
(
next
func
()
(
common
.
Hash
,
bool
),
peerId
string
)
AddBlock
(
block
*
types
.
Block
,
peerId
string
)
AddPeer
(
td
*
big
.
Int
,
currentBlock
common
.
Hash
,
peerId
string
,
requestHashes
func
(
common
.
Hash
)
error
,
requestBlocks
func
([]
common
.
Hash
)
error
,
peerError
func
(
*
errs
.
Error
))
(
best
bool
,
suspended
bool
)
RemovePeer
(
peerId
string
)
}
// message structs used for RLP serialization
type
newBlockMsgData
struct
{
Block
*
types
.
Block
TD
*
big
.
Int
}
type
getBlockHashesMsgData
struct
{
Hash
common
.
Hash
Amount
uint64
}
type
statusMsgData
struct
{
ProtocolVersion
uint32
NetworkId
uint32
TD
*
big
.
Int
CurrentBlock
common
.
Hash
GenesisBlock
common
.
Hash
}
// main entrypoint, wrappers starting a server running the eth protocol
// use this constructor to attach the protocol ("class") to server caps
// the Dev p2p layer then runs the protocol instance on each peer
func
EthProtocol
(
protocolVersion
,
networkId
int
,
txPool
txPool
,
chainManager
chainManager
,
blockPool
blockPool
)
p2p
.
Protocol
{
return
p2p
.
Protocol
{
Name
:
"eth"
,
Version
:
uint
(
protocolVersion
),
Length
:
ProtocolLength
,
Run
:
func
(
peer
*
p2p
.
Peer
,
rw
p2p
.
MsgReadWriter
)
error
{
return
runEthProtocol
(
protocolVersion
,
networkId
,
txPool
,
chainManager
,
blockPool
,
peer
,
rw
)
},
}
}
// the main loop that handles incoming messages
// note RemovePeer in the post-disconnect hook
func
runEthProtocol
(
protocolVersion
,
networkId
int
,
txPool
txPool
,
chainManager
chainManager
,
blockPool
blockPool
,
peer
*
p2p
.
Peer
,
rw
p2p
.
MsgReadWriter
)
(
err
error
)
{
id
:=
peer
.
ID
()
self
:=
&
ethProtocol
{
txPool
:
txPool
,
chainManager
:
chainManager
,
blockPool
:
blockPool
,
rw
:
rw
,
peer
:
peer
,
protocolVersion
:
protocolVersion
,
networkId
:
networkId
,
errors
:
&
errs
.
Errors
{
Package
:
"ETH"
,
Errors
:
errorToString
,
},
id
:
fmt
.
Sprintf
(
"%x"
,
id
[
:
8
]),
}
// handshake.
if
err
:=
self
.
handleStatus
();
err
!=
nil
{
return
err
}
defer
self
.
blockPool
.
RemovePeer
(
self
.
id
)
// propagate existing transactions. new transactions appearing
// after this will be sent via broadcasts.
if
err
:=
p2p
.
Send
(
rw
,
TxMsg
,
txPool
.
GetTransactions
());
err
!=
nil
{
return
err
}
// main loop. handle incoming messages.
for
{
if
err
:=
self
.
handle
();
err
!=
nil
{
return
err
}
}
}
func
(
self
*
ethProtocol
)
handle
()
error
{
msg
,
err
:=
self
.
rw
.
ReadMsg
()
if
err
!=
nil
{
return
err
}
if
msg
.
Size
>
ProtocolMaxMsgSize
{
return
self
.
protoError
(
ErrMsgTooLarge
,
"%v > %v"
,
msg
.
Size
,
ProtocolMaxMsgSize
)
}
// make sure that the payload has been fully consumed
defer
msg
.
Discard
()
switch
msg
.
Code
{
case
GetTxMsg
:
// ignore
case
StatusMsg
:
return
self
.
protoError
(
ErrExtraStatusMsg
,
""
)
case
TxMsg
:
// TODO: rework using lazy RLP stream
var
txs
[]
*
types
.
Transaction
if
err
:=
msg
.
Decode
(
&
txs
);
err
!=
nil
{
return
self
.
protoError
(
ErrDecode
,
"msg %v: %v"
,
msg
,
err
)
}
for
i
,
tx
:=
range
txs
{
if
tx
==
nil
{
return
self
.
protoError
(
ErrDecode
,
"transaction %d is nil"
,
i
)
}
jsonlogger
.
LogJson
(
&
logger
.
EthTxReceived
{
TxHash
:
tx
.
Hash
()
.
Hex
(),
RemoteId
:
self
.
peer
.
ID
()
.
String
(),
})
}
self
.
txPool
.
AddTransactions
(
txs
)
case
GetBlockHashesMsg
:
var
request
getBlockHashesMsgData
if
err
:=
msg
.
Decode
(
&
request
);
err
!=
nil
{
return
self
.
protoError
(
ErrDecode
,
"->msg %v: %v"
,
msg
,
err
)
}
if
request
.
Amount
>
maxHashes
{
request
.
Amount
=
maxHashes
}
hashes
:=
self
.
chainManager
.
GetBlockHashesFromHash
(
request
.
Hash
,
request
.
Amount
)
return
p2p
.
Send
(
self
.
rw
,
BlockHashesMsg
,
hashes
)
case
BlockHashesMsg
:
msgStream
:=
rlp
.
NewStream
(
msg
.
Payload
)
if
_
,
err
:=
msgStream
.
List
();
err
!=
nil
{
return
err
}
var
i
int
iter
:=
func
()
(
hash
common
.
Hash
,
ok
bool
)
{
err
:=
msgStream
.
Decode
(
&
hash
)
if
err
==
rlp
.
EOL
{
return
common
.
Hash
{},
false
}
else
if
err
!=
nil
{
self
.
protoError
(
ErrDecode
,
"msg %v: after %v hashes : %v"
,
msg
,
i
,
err
)
return
common
.
Hash
{},
false
}
i
++
return
hash
,
true
}
self
.
blockPool
.
AddBlockHashes
(
iter
,
self
.
id
)
case
GetBlocksMsg
:
msgStream
:=
rlp
.
NewStream
(
msg
.
Payload
)
if
_
,
err
:=
msgStream
.
List
();
err
!=
nil
{
return
err
}
var
blocks
[]
*
types
.
Block
var
i
int
for
{
i
++
var
hash
common
.
Hash
err
:=
msgStream
.
Decode
(
&
hash
)
if
err
==
rlp
.
EOL
{
break
}
else
if
err
!=
nil
{
return
self
.
protoError
(
ErrDecode
,
"msg %v: %v"
,
msg
,
err
)
}
block
:=
self
.
chainManager
.
GetBlock
(
hash
)
if
block
!=
nil
{
blocks
=
append
(
blocks
,
block
)
}
if
i
==
maxBlocks
{
break
}
}
return
p2p
.
Send
(
self
.
rw
,
BlocksMsg
,
blocks
)
case
BlocksMsg
:
msgStream
:=
rlp
.
NewStream
(
msg
.
Payload
)
if
_
,
err
:=
msgStream
.
List
();
err
!=
nil
{
return
err
}
for
{
var
block
types
.
Block
if
err
:=
msgStream
.
Decode
(
&
block
);
err
!=
nil
{
if
err
==
rlp
.
EOL
{
break
}
else
{
return
self
.
protoError
(
ErrDecode
,
"msg %v: %v"
,
msg
,
err
)
}
}
if
err
:=
block
.
ValidateFields
();
err
!=
nil
{
return
self
.
protoError
(
ErrDecode
,
"block validation %v: %v"
,
msg
,
err
)
}
self
.
blockPool
.
AddBlock
(
&
block
,
self
.
id
)
}
case
NewBlockMsg
:
var
request
newBlockMsgData
if
err
:=
msg
.
Decode
(
&
request
);
err
!=
nil
{
return
self
.
protoError
(
ErrDecode
,
"%v: %v"
,
msg
,
err
)
}
if
err
:=
request
.
Block
.
ValidateFields
();
err
!=
nil
{
return
self
.
protoError
(
ErrDecode
,
"block validation %v: %v"
,
msg
,
err
)
}
hash
:=
request
.
Block
.
Hash
()
_
,
chainHead
,
_
:=
self
.
chainManager
.
Status
()
jsonlogger
.
LogJson
(
&
logger
.
EthChainReceivedNewBlock
{
BlockHash
:
hash
.
Hex
(),
BlockNumber
:
request
.
Block
.
Number
(),
// this surely must be zero
ChainHeadHash
:
chainHead
.
Hex
(),
BlockPrevHash
:
request
.
Block
.
ParentHash
()
.
Hex
(),
RemoteId
:
self
.
peer
.
ID
()
.
String
(),
})
// to simplify backend interface adding a new block
// uses AddPeer followed by AddBlock only if peer is the best peer
// (or selected as new best peer)
if
_
,
suspended
:=
self
.
blockPool
.
AddPeer
(
request
.
TD
,
hash
,
self
.
id
,
self
.
requestBlockHashes
,
self
.
requestBlocks
,
self
.
protoErrorDisconnect
);
!
suspended
{
self
.
blockPool
.
AddBlock
(
request
.
Block
,
self
.
id
)
}
default
:
return
self
.
protoError
(
ErrInvalidMsgCode
,
"%v"
,
msg
.
Code
)
}
return
nil
}
func
(
self
*
ethProtocol
)
handleStatus
()
error
{
if
err
:=
self
.
sendStatus
();
err
!=
nil
{
return
err
}
// read and handle remote status
msg
,
err
:=
self
.
rw
.
ReadMsg
()
if
err
!=
nil
{
return
err
}
if
msg
.
Code
!=
StatusMsg
{
return
self
.
protoError
(
ErrNoStatusMsg
,
"first msg has code %x (!= %x)"
,
msg
.
Code
,
StatusMsg
)
}
if
msg
.
Size
>
ProtocolMaxMsgSize
{
return
self
.
protoError
(
ErrMsgTooLarge
,
"%v > %v"
,
msg
.
Size
,
ProtocolMaxMsgSize
)
}
var
status
statusMsgData
if
err
:=
msg
.
Decode
(
&
status
);
err
!=
nil
{
return
self
.
protoError
(
ErrDecode
,
"msg %v: %v"
,
msg
,
err
)
}
_
,
_
,
genesisBlock
:=
self
.
chainManager
.
Status
()
if
status
.
GenesisBlock
!=
genesisBlock
{
return
self
.
protoError
(
ErrGenesisBlockMismatch
,
"%x (!= %x)"
,
status
.
GenesisBlock
,
genesisBlock
)
}
if
int
(
status
.
NetworkId
)
!=
self
.
networkId
{
return
self
.
protoError
(
ErrNetworkIdMismatch
,
"%d (!= %d)"
,
status
.
NetworkId
,
self
.
networkId
)
}
if
int
(
status
.
ProtocolVersion
)
!=
self
.
protocolVersion
{
return
self
.
protoError
(
ErrProtocolVersionMismatch
,
"%d (!= %d)"
,
status
.
ProtocolVersion
,
self
.
protocolVersion
)
}
_
,
suspended
:=
self
.
blockPool
.
AddPeer
(
status
.
TD
,
status
.
CurrentBlock
,
self
.
id
,
self
.
requestBlockHashes
,
self
.
requestBlocks
,
self
.
protoErrorDisconnect
)
if
suspended
{
return
self
.
protoError
(
ErrSuspendedPeer
,
""
)
}
self
.
peer
.
Debugf
(
"Peer is [eth] capable (%d/%d). TD=%v H=%x
\n
"
,
status
.
ProtocolVersion
,
status
.
NetworkId
,
status
.
TD
,
status
.
CurrentBlock
[
:
4
])
return
nil
}
func
(
self
*
ethProtocol
)
requestBlockHashes
(
from
common
.
Hash
)
error
{
self
.
peer
.
Debugf
(
"fetching hashes (%d) %x...
\n
"
,
maxHashes
,
from
[
0
:
4
])
return
p2p
.
Send
(
self
.
rw
,
GetBlockHashesMsg
,
getBlockHashesMsgData
{
from
,
maxHashes
})
}
func
(
self
*
ethProtocol
)
requestBlocks
(
hashes
[]
common
.
Hash
)
error
{
self
.
peer
.
Debugf
(
"fetching %v blocks"
,
len
(
hashes
))
return
p2p
.
Send
(
self
.
rw
,
GetBlocksMsg
,
hashes
)
}
func
(
self
*
ethProtocol
)
protoError
(
code
int
,
format
string
,
params
...
interface
{})
(
err
*
errs
.
Error
)
{
err
=
self
.
errors
.
New
(
code
,
format
,
params
...
)
//err.Log(self.peer.Logger)
err
.
Log
(
glog
.
V
(
logger
.
Info
))
return
}
func
(
self
*
ethProtocol
)
sendStatus
()
error
{
td
,
currentBlock
,
genesisBlock
:=
self
.
chainManager
.
Status
()
return
p2p
.
Send
(
self
.
rw
,
StatusMsg
,
&
statusMsgData
{
ProtocolVersion
:
uint32
(
self
.
protocolVersion
),
NetworkId
:
uint32
(
self
.
networkId
),
TD
:
td
,
CurrentBlock
:
currentBlock
,
GenesisBlock
:
genesisBlock
,
})
}
func
(
self
*
ethProtocol
)
protoErrorDisconnect
(
err
*
errs
.
Error
)
{
err
.
Log
(
glog
.
V
(
logger
.
Info
))
if
err
.
Fatal
()
{
self
.
peer
.
Disconnect
(
p2p
.
DiscSubprotocolError
)
}
}
eth/protocol_test.go
View file @
71aa5fe8
package
eth
import
(
"log"
"math/big"
"os"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/errs"
ethlogger
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
)
/*
TODO All of these tests need to be re-written
var logsys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
...
...
@@ -398,3 +385,4 @@ func TestTransactionsMsg(t *testing.T) {
eth.checkError(ErrDecode, delay)
}
*/
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