erigon-pulse/eth/downloader/peer.go
2015-04-18 20:35:49 +02:00

141 lines
2.4 KiB
Go

package downloader
import (
"errors"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"gopkg.in/fatih/set.v0"
)
const (
workingState = 2
idleState = 4
)
type hashFetcherFn func(common.Hash) error
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 {
peer.mu.RLock()
if peer.state == state {
peers = append(peers, peer)
}
peer.mu.RUnlock()
}
return peers
}
func (p peers) setState(id string, state int) {
if peer, exist := p[id]; exist {
peer.mu.Lock()
defer peer.mu.Unlock()
peer.state = state
}
}
func (p peers) getPeer(id string) *peer {
return p[id]
}
func (p peers) bestPeer() *peer {
var peer *peer
for _, cp := range p {
if peer == nil || cp.td.Cmp(peer.td) > 0 {
peer = cp
}
}
return peer
}
// peer represents an active peer
type peer struct {
state int // Peer state (working, idle)
rep int // TODO peer reputation
mu sync.RWMutex
id string
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,
requested: set.New(),
}
}
// fetch a chunk using the peer
func (p *peer) fetch(chunk *chunk) error {
p.mu.Lock()
defer p.mu.Unlock()
if p.state == workingState {
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
hashes, i := make([]common.Hash, chunk.hashes.Size()), 0
chunk.hashes.Each(func(v interface{}) bool {
hashes[i] = v.(common.Hash)
i++
return true
})
p.getBlocks(hashes)
return nil
}
// promote increases the peer's reputation
func (p *peer) promote() {
p.mu.Lock()
defer p.mu.Unlock()
p.rep++
}
// demote decreases the peer's reputation or leaves it at 0
func (p *peer) demote() {
p.mu.Lock()
defer p.mu.Unlock()
if p.rep > 1 {
p.rep -= 2
} else {
p.rep = 0
}
}
func (p *peer) reset() {
p.state = idleState
}