aboutsummaryrefslogtreecommitdiffstats
path: root/eth/downloader/peer.go
blob: 4cd306a053b2161961381e634a70105b7e0b188e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package downloader

import (
    "errors"
    "math/big"
    "sync"

    "github.com/ethereum/go-ethereum/common"
)

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) 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

    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}
}

// 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")
    }

    // 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
    }
}