-
Notifications
You must be signed in to change notification settings - Fork 92
/
peer.go
238 lines (205 loc) · 6.31 KB
/
peer.go
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// Copyright 2017 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.
package p2p
import (
"fmt"
"math/rand"
"net"
"time"
log "github.com/sirupsen/logrus"
)
var peerLogger = packageLogger.WithField("subpack", "peer")
// Data structures and functions related to peers (eg other nodes in the network)
// Keep a short history of messages
type Last100 struct {
Msgs map[[32]byte]bool // Look up messages by hash
MsgOrder [100][32]byte // keep a list of the order they were added
N int
}
func (l *Last100) Add(hash [32]byte) {
if l.Msgs == nil {
l.Msgs = make(map[[32]byte]bool, 0)
}
previous := l.MsgOrder[l.N] // get the oldest message
delete(l.Msgs, previous) // Delete it for the map
l.MsgOrder[l.N] = hash // replace it with the new message
l.Msgs[hash] = true // Add new the message to the map
l.N = (l.N + 1) % 100 // move and wrap the index
}
//Check if we have a message in the short history
func (l *Last100) Get(hash [32]byte) bool {
_, exists := l.Msgs[hash]
return exists
}
type Peer struct {
QualityScore int32 // 0 is neutral quality, negative is a bad peer.
Address string // Must be in form of x.x.x.x
Port string // Must be in form of xxxx
NodeID uint64 // a nonce to distinguish multiple nodes behind one IP address
Hash string // This is more of a connection ID than hash right now.
Location uint32 // IP address as an int.
Network NetworkID // The network this peer reference lives on.
Type uint8
Connections int // Number of successful connections.
LastContact time.Time // Keep track of how long ago we talked to the peer.
Source map[string]time.Time // source where we heard from the peer.
// logging
logger *log.Entry
PrevMsgs Last100 `json:"-"`
}
const (
RegularPeer uint8 = iota
SpecialPeerConfig // special peer defined in the config file
SpecialPeerCmdLine // special peer defined via the cmd line params
)
func (p *Peer) Init(address string, port string, quality int32, peerType uint8, connections int) *Peer {
p.logger = peerLogger.WithFields(log.Fields{
"address": address,
"port": port,
"peerType": peerType,
})
if net.ParseIP(address) == nil {
ipAddress, err := net.LookupHost(address)
if err != nil {
p.logger.Errorf("Init: LookupHost(%v) failed. %v ", address, err)
// is there a way to abandon this peer at this point? -- clay
} else {
address = ipAddress[0]
}
}
p.Address = address
p.Port = port
p.QualityScore = quality
p.generatePeerHash()
p.Type = peerType
p.Location = p.LocationFromAddress()
p.Source = map[string]time.Time{}
p.Network = CurrentNetwork
return p
}
func (p *Peer) generatePeerHash() {
p.Hash = fmt.Sprintf("%s:%s %x", p.Address, p.Port, rand.Int63())
}
func (p *Peer) AddressPort() string {
return p.Address + ":" + p.Port
}
func (p *Peer) PeerIdent() string {
return p.Hash[0:12] + "-" + p.Address + ":" + p.Port
}
func (p *Peer) PeerFixedIdent() string {
address := fmt.Sprintf("%16s", p.Address)
return p.Hash[0:12] + "-" + address + ":" + p.Port
}
func (p *Peer) PeerLogFields() log.Fields {
return log.Fields{
"address": p.Address,
"port": p.Port,
"peer_type": p.PeerTypeString(),
}
}
// gets the last source where this peer was seen
func (p *Peer) LastSource() (result string) {
var maxTime time.Time
for source, lastSeen := range p.Source {
if lastSeen.After(maxTime) {
maxTime = lastSeen
result = source
}
}
return
}
// TODO Hadn't considered IPV6 address support.
// TODO Need to audit all the net code to check IPv6 addresses
// Here's an IPv6 conversion:
// Ref: http://stackoverflow.com/questions/23297141/golang-net-ip-to-ipv6-from-mysql-as-decimal39-0-conversion
// func ipv6ToInt(IPv6Addr net.IP) *big.Int {
// IPv6Int := big.NewInt(0)
// IPv6Int.SetBytes(IPv6Addr)
// return IPv6Int
// }
// Problem is we're working with string addresses, may never have made a connection.
// TODO - we might have a DNS address, not iP address and need to resolve it!
// locationFromAddress converts the peers address into a uint32 "location" numeric
func (p *Peer) LocationFromAddress() (location uint32) {
location = 0
// Split the IPv4 octets
ip := net.ParseIP(p.Address)
if ip == nil {
ipAddress, err := net.LookupHost(p.Address)
if err != nil {
p.logger.Debugf("LocationFromAddress(%v) failed. %v ", p.Address, err)
p.logger.Debugf("Invalid Peer Address: %v", p.Address)
p.logger.Debugf("Peer: %s has Location: %d", p.Hash, location)
return 0 // We use location on 0 to say invalid
}
p.Address = ipAddress[0]
ip = net.ParseIP(p.Address)
}
if len(ip) == 16 { // If we got back an IP6 (16 byte) address, use the last 4 byte
ip = ip[12:]
}
// Turn into uint32
location += uint32(ip[0]) << 24
location += uint32(ip[1]) << 16
location += uint32(ip[2]) << 8
location += uint32(ip[3])
p.logger.Debugf("Peer: %s has Location: %d", p.Hash, location)
return location
}
func (p *Peer) IsSamePeerAs(netAddress net.Addr) bool {
address, _, err := net.SplitHostPort(netAddress.String())
if err != nil {
return false
}
return address == p.Address
}
// merit increases a peers reputation
func (p *Peer) merit() {
if 2147483000 > p.QualityScore {
p.QualityScore++
}
}
// demerit decreases a peers reputation
func (p *Peer) demerit() {
if -2147483000 < p.QualityScore {
//p.QualityScore--
}
}
func (p *Peer) IsSpecial() bool {
return p.Type == SpecialPeerConfig || p.Type == SpecialPeerCmdLine
}
func (p *Peer) PeerTypeString() string {
switch p.Type {
case RegularPeer:
return "regular"
case SpecialPeerConfig:
return "special_config"
case SpecialPeerCmdLine:
return "special_cmdline"
default:
return "unknown"
}
}
// sort.Sort interface implementation
type PeerQualitySort []Peer
func (p PeerQualitySort) Len() int {
return len(p)
}
func (p PeerQualitySort) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
func (p PeerQualitySort) Less(i, j int) bool {
return p[i].QualityScore < p[j].QualityScore
}
// sort.Sort interface implementation
type PeerDistanceSort []Peer
func (p PeerDistanceSort) Len() int {
return len(p)
}
func (p PeerDistanceSort) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
func (p PeerDistanceSort) Less(i, j int) bool {
return p[i].Location < p[j].Location
}