-
Notifications
You must be signed in to change notification settings - Fork 5
/
Peer ID.go
308 lines (247 loc) · 10.2 KB
/
Peer ID.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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/*
File Username: Peer ID.go
Copyright: 2021 Peernet s.r.o.
Author: Peter Kleissner
*/
package core
import (
"encoding/hex"
"errors"
"math/rand"
"net"
"os"
"sync"
"time"
"github.com/PeernetOfficial/core/btcec"
"github.com/PeernetOfficial/core/dht"
"github.com/PeernetOfficial/core/protocol"
)
func (backend *Backend) initPeerID() {
backend.PeerList = make(map[[btcec.PubKeyBytesLenCompressed]byte]*PeerInfo)
backend.nodeList = make(map[[protocol.HashSize]byte]*PeerInfo)
// load existing key from config, if available
if len(backend.Config.PrivateKey) > 0 {
configPK, err := hex.DecodeString(backend.Config.PrivateKey)
if err == nil {
backend.PeerPrivateKey, backend.PeerPublicKey = btcec.PrivKeyFromBytes(btcec.S256(), configPK)
backend.nodeID = protocol.PublicKey2NodeID(backend.PeerPublicKey)
if backend.Config.AutoUpdateSeedList {
backend.configUpdateSeedList()
}
return
}
backend.LogError("initPeerID", "private key in config is corrupted! Error: %s\n", err.Error())
os.Exit(ExitPrivateKeyCorrupt)
}
// if the peer ID is empty, create a new user public-private key pair
var err error
backend.PeerPrivateKey, backend.PeerPublicKey, err = Secp256k1NewPrivateKey()
if err != nil {
backend.LogError("initPeerID", "generating public-private key pairs: %s\n", err.Error())
os.Exit(ExitPrivateKeyCreate)
}
backend.nodeID = protocol.PublicKey2NodeID(backend.PeerPublicKey)
// save the newly generated private key into the config
backend.Config.PrivateKey = hex.EncodeToString(backend.PeerPrivateKey.Serialize())
backend.SaveConfig()
}
// Secp256k1NewPrivateKey creates a new public-private key pair
func Secp256k1NewPrivateKey() (privateKey *btcec.PrivateKey, publicKey *btcec.PublicKey, err error) {
key, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
return nil, nil, err
}
return key, (*btcec.PublicKey)(&key.PublicKey), nil
}
// ExportPrivateKey returns the peers public and private key
func (backend *Backend) ExportPrivateKey() (privateKey *btcec.PrivateKey, publicKey *btcec.PublicKey) {
return backend.PeerPrivateKey, backend.PeerPublicKey
}
// SelfNodeID returns the node ID used for DHT
func (backend *Backend) SelfNodeID() []byte {
return backend.nodeID
}
// SelfUserAgent returns the User Agent
func (backend *Backend) SelfUserAgent() string {
return backend.userAgent
}
// PeerInfo stores information about a single remote peer
type PeerInfo struct {
PublicKey *btcec.PublicKey // Public key
NodeID []byte // Node ID in Kademlia network = blake3(Public Key).
connectionActive []*Connection // List of active established connections to the peer.
connectionInactive []*Connection // List of former connections that are no longer valid. They may be removed after a while.
connectionLatest *Connection // Latest valid connection.
sync.RWMutex // Mutex for access to list of connections.
messageSequence uint32 // Sequence number. Increased with every message.
IsRootPeer bool // Whether the peer is a trusted root peer.
UserAgent string // User Agent reported by remote peer. Empty if no Announcement/Response message was yet received.
Features uint8 // Feature bit array. 0 = IPv4_LISTEN, 1 = IPv6_LISTEN, 1 = FIREWALL
isVirtual bool // Whether it is a virtual peer for establishing a connection.
targetAddresses []*peerAddress // Virtual peer: Addresses to send any replies.
traversePeer *PeerInfo // Virtual peer: Same field as in connection.
BlockchainHeight uint64 // Blockchain height
BlockchainVersion uint64 // Blockchain version
blockchainLastRefresh time.Time // Last refresh of the blockchain info.
// statistics
StatsPacketSent uint64 // Count of packets sent
StatsPacketReceived uint64 // Count of packets received
Backend *Backend
}
type peerAddress struct {
IP net.IP
Port uint16
PortInternal uint16
}
// PeerlistAdd adds a new peer to the peer list. It does not validate the peer info. If the peer is already added, it does nothing. Connections must be live.
func (backend *Backend) PeerlistAdd(PublicKey *btcec.PublicKey, connections ...*Connection) (peer *PeerInfo, added bool) {
if len(connections) == 0 {
return nil, false
}
publicKeyCompressed := publicKey2Compressed(PublicKey)
backend.peerlistMutex.Lock()
defer backend.peerlistMutex.Unlock()
peer, ok := backend.PeerList[publicKeyCompressed]
if ok {
return peer, false
}
peer = &PeerInfo{Backend: backend, PublicKey: PublicKey, connectionActive: connections, connectionLatest: connections[0], NodeID: protocol.PublicKey2NodeID(PublicKey), messageSequence: rand.Uint32()}
_, peer.IsRootPeer = rootPeers[publicKeyCompressed]
backend.PeerList[publicKeyCompressed] = peer
// also add to mirrored nodeList
var nodeID [protocol.HashSize]byte
copy(nodeID[:], peer.NodeID)
backend.nodeList[nodeID] = peer
// add to Kademlia
backend.nodesDHT.AddNode(&dht.Node{ID: peer.NodeID, Info: peer})
// TODO: If the node isn't added to Kademlia, it should be either added temporarily to the PeerList with an expiration, or to a temp list, or not at all.
// send to all channels non-blocking
for _, monitor := range backend.peerMonitor {
select {
case monitor <- peer:
default:
}
}
backend.Filters.NewPeer(peer, connections[0])
backend.Filters.NewPeerConnection(peer, connections[0])
return peer, true
}
// PeerlistRemove removes a peer from the peer list.
func (backend *Backend) PeerlistRemove(peer *PeerInfo) {
backend.peerlistMutex.Lock()
defer backend.peerlistMutex.Unlock()
// remove from Kademlia
backend.nodesDHT.RemoveNode(peer.NodeID)
delete(backend.PeerList, publicKey2Compressed(peer.PublicKey))
var nodeID [protocol.HashSize]byte
copy(nodeID[:], peer.NodeID)
delete(backend.nodeList, nodeID)
}
// PeerlistGet returns the full peer list
func (backend *Backend) PeerlistGet() (peers []*PeerInfo) {
backend.peerlistMutex.RLock()
defer backend.peerlistMutex.RUnlock()
for _, peer := range backend.PeerList {
peers = append(peers, peer)
}
return peers
}
// PeerlistLookup returns the peer from the list with the public key
func (backend *Backend) PeerlistLookup(publicKey *btcec.PublicKey) (peer *PeerInfo) {
backend.peerlistMutex.RLock()
defer backend.peerlistMutex.RUnlock()
return backend.PeerList[publicKey2Compressed(publicKey)]
}
// NodelistLookup returns the peer from the list with the node ID
func (backend *Backend) NodelistLookup(nodeID []byte) (peer *PeerInfo) {
backend.peerlistMutex.RLock()
defer backend.peerlistMutex.RUnlock()
var nodeID2 [protocol.HashSize]byte
copy(nodeID2[:], nodeID)
return backend.nodeList[nodeID2]
}
// PeerlistCount returns the current count of peers in the peer list
func (backend *Backend) PeerlistCount() (count int) {
backend.peerlistMutex.RLock()
defer backend.peerlistMutex.RUnlock()
return len(backend.PeerList)
}
func publicKey2Compressed(publicKey *btcec.PublicKey) [btcec.PubKeyBytesLenCompressed]byte {
var key [btcec.PubKeyBytesLenCompressed]byte
copy(key[:], publicKey.SerializeCompressed())
return key
}
// records2Nodes translates infoPeer structures to nodes reported by the peer. If the reported nodes are not in the peer table, it will create temporary PeerInfo structures.
// LastContact is passed on in the Node.LastSeen field.
func (peerSource *PeerInfo) records2Nodes(records []protocol.PeerRecord) (nodes []*dht.Node) {
for _, record := range records {
if peerSource.Backend.isReturnedPeerBadQuality(&record) {
continue
}
var peer *PeerInfo
if record.PublicKey.IsEqual(peerSource.PublicKey) {
// Special case if peer that stores info = sender. In that case IP:Port in the record would be empty anyway.
peer = peerSource
} else if peer = peerSource.Backend.PeerlistLookup(record.PublicKey); peer == nil {
// Create temporary peer which is not added to the global list and not added to Kademlia.
// traversePeer is set to the peer who provided the node information.
addresses := peerRecordToAddresses(&record)
if len(addresses) == 0 {
continue
}
peer = &PeerInfo{Backend: peerSource.Backend, PublicKey: record.PublicKey, connectionActive: nil, connectionLatest: nil, NodeID: protocol.PublicKey2NodeID(record.PublicKey), messageSequence: rand.Uint32(), isVirtual: true, targetAddresses: addresses, traversePeer: peerSource, Features: record.Features}
}
nodes = append(nodes, &dht.Node{ID: peer.NodeID, LastSeen: record.LastContactT, Info: peer})
}
return
}
// selfPeerRecord returns self as peer record
func (backend *Backend) selfPeerRecord() (result protocol.PeerRecord) {
return protocol.PeerRecord{
PublicKey: backend.PeerPublicKey,
NodeID: backend.nodeID,
//IP: network.address.IP,
//Port: uint16(network.address.Port),
LastContact: 0,
Features: backend.FeatureSupport(),
}
}
// registerPeerMonitor registers a channel to receive all new peers
func (backend *Backend) registerPeerMonitor(channel chan<- *PeerInfo) {
backend.peerlistMutex.Lock()
defer backend.peerlistMutex.Unlock()
backend.peerMonitor = append(backend.peerMonitor, channel)
}
// unregisterPeerMonitor unregisters a channel
func (backend *Backend) unregisterPeerMonitor(channel chan<- *PeerInfo) {
backend.peerlistMutex.Lock()
defer backend.peerlistMutex.Unlock()
for n, channel2 := range backend.peerMonitor {
if channel == channel2 {
peerMonitorNew := backend.peerMonitor[:n]
if n < len(backend.peerMonitor)-1 {
peerMonitorNew = append(peerMonitorNew, backend.peerMonitor[n+1:]...)
}
backend.peerMonitor = peerMonitorNew
break
}
}
}
// DeleteAccount deletes the account
func (backend *Backend) DeleteAccount() {
// delete the blockchain
backend.UserBlockchain.DeleteBlockchain()
// delete the warehouse
backend.UserWarehouse.DeleteWarehouse()
// delete the private key
backend.Config.PrivateKey = ""
backend.SaveConfig()
}
// PublicKeyFromPeerID decodes the peer ID (hex encoded) into a public key.
func PublicKeyFromPeerID(peerID string) (publicKey *btcec.PublicKey, err error) {
hash, err := hex.DecodeString(peerID)
if err != nil || len(hash) != 33 {
return nil, errors.New("invalid peer ID length")
}
return btcec.ParsePubKey(hash, btcec.S256())
}