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
Expand all
Hide 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"
...
...
@@ -127,19 +127,20 @@ type Ethereum struct {
//*** SERVICES ***
// State manager for processing new blocks and managing the over all states
blockProcessor
*
core
.
BlockProcessor
txPool
*
core
.
TxPool
chainManager
*
core
.
ChainManager
blockPool
*
blockpool
.
BlockPool
accountManager
*
accounts
.
Manager
whisper
*
whisper
.
Whisper
pow
*
ethash
.
Ethash
net
*
p2p
.
Server
eventMux
*
event
.
TypeMux
txSub
event
.
Subscription
blockSub
event
.
Subscription
miner
*
miner
.
Miner
blockProcessor
*
core
.
BlockProcessor
txPool
*
core
.
TxPool
chainManager
*
core
.
ChainManager
accountManager
*
accounts
.
Manager
whisper
*
whisper
.
Whisper
pow
*
ethash
.
Ethash
protocolManager
*
ProtocolManager
downloader
*
downloader
.
Downloader
net
*
p2p
.
Server
eventMux
*
event
.
TypeMux
txSub
event
.
Subscription
minedBlockSub
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
{
...
...
@@ -424,12 +419,11 @@ func (s *Ethereum) Stop() {
defer
s
.
stateDb
.
Close
()
defer
s
.
extraDb
.
Close
()
s
.
txSub
.
Unsubscribe
()
// quits txBroadcastLoop
s
.
b
lockSub
.
Unsubscribe
()
// quits blockBroadcastLoop
s
.
txSub
.
Unsubscribe
()
// quits txBroadcastLoop
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
This diff is collapsed.
Click to expand it.
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
}
hashes
.
Add
(
v
)
i
++
// 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
This diff is collapsed.
Click to expand it.
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