Class: Ciri::P2P::PeerStore
- Inherits:
-
Object
- Object
- Ciri::P2P::PeerStore
show all
- Includes:
- Behaviours, Status
- Defined in:
- lib/ciri/p2p/peer_store.rb
Overview
PeerStore store information of all peers we have seen TODO rewrite with a database(sqlite) Support score peers
Defined Under Namespace
Modules: Behaviours, Status
Constant Summary
collapse
- PEER_LAST_SEEN_VALID =
consider peer is valid if we seen it within 12 hours
12 * 3600
- PING_EXPIRATION_IN =
allow ping within 10 minutes
10 * 60
- PEER_INITIAL_SCORE =
100
- DEFAULT_SCORE_SCHEMA =
{
INVALID_DATA => -50,
CONNECT => 10,
PING => 5,
FAILED_TO_PING => -10,
FAILED_TO_CONNECT => -10,
UNEXPECT_DISCONNECT => -20,
}
Constants included
from Status
Status::CONNECTED, Status::DISCONNECTED, Status::UNKNOWN
Constants included
from Behaviours
Behaviours::CONNECT, Behaviours::FAILED_TO_CONNECT, Behaviours::FAILED_TO_PING, Behaviours::INVALID_DATA, Behaviours::PING, Behaviours::UNEXPECT_DISCONNECT
Instance Method Summary
collapse
-
#add_bootnode(node) ⇒ Object
-
#add_node(node) ⇒ Object
-
#add_node_addresses(raw_node_id, addresses) ⇒ Object
-
#ban_peer(raw_node_id, now: Time.now, timeout_secs: 600) ⇒ Object
-
#find_attempt_peers(count) ⇒ Object
TODO find high scoring peers.
-
#find_bootnodes(count) ⇒ Object
TODO find high scoring peers, use bootnodes as fallback.
-
#get_node_addresses(raw_node_id) ⇒ Object
-
#has_ban?(raw_node_id, now: Time.now) ⇒ Boolean
-
#has_ping?(raw_node_id, ping_hash, expires_in: PING_EXPIRATION_IN) ⇒ Boolean
-
#has_seen?(raw_node_id, expires_in: PEER_LAST_SEEN_VALID) ⇒ Boolean
-
#initialize(score_schema: {}) ⇒ PeerStore
constructor
A new instance of PeerStore.
-
#peer_status(raw_node_id) ⇒ Object
-
#report_peer(raw_node_id, behaviour) ⇒ Object
-
#update_last_seen(raw_node_id, at: Time.now.to_i) ⇒ Object
-
#update_peer_status(raw_node_id, status) ⇒ Object
-
#update_ping(raw_node_id, ping_hash, ping_at: Time.now.to_i) ⇒ Object
Constructor Details
#initialize(score_schema: {}) ⇒ PeerStore
Returns a new instance of PeerStore.
68
69
70
71
72
73
74
75
|
# File 'lib/ciri/p2p/peer_store.rb', line 68
def initialize(score_schema:{})
@peers_ping_records = {}
@peers_seen_records = {}
@peers = {}
@bootnodes = []
@ban_peers = {}
@score_schema = DEFAULT_SCORE_SCHEMA.merge(score_schema)
end
|
Instance Method Details
#add_bootnode(node) ⇒ Object
104
105
106
107
|
# File 'lib/ciri/p2p/peer_store.rb', line 104
def add_bootnode(node)
@bootnodes << node
add_node(node)
end
|
#add_node(node) ⇒ Object
162
163
164
|
# File 'lib/ciri/p2p/peer_store.rb', line 162
def add_node(node)
@peers[node.raw_node_id] = {node: node, score: PEER_INITIAL_SCORE, status: Status::UNKNOWN}
end
|
#add_node_addresses(raw_node_id, addresses) ⇒ Object
149
150
151
152
153
154
155
|
# File 'lib/ciri/p2p/peer_store.rb', line 149
def add_node_addresses(raw_node_id, addresses)
node_info = @peers[raw_node_id]
node = node_info && node_info[:node]
if node
node.addresses = (node.addresses + addresses).uniq
end
end
|
#ban_peer(raw_node_id, now: Time.now, timeout_secs: 600) ⇒ Object
119
120
121
|
# File 'lib/ciri/p2p/peer_store.rb', line 119
def ban_peer(raw_node_id, now: Time.now, timeout_secs:600)
@ban_peers[raw_node_id] = {ban_at: now, timeout_secs: timeout_secs}
end
|
#find_attempt_peers(count) ⇒ Object
TODO find high scoring peers
138
139
140
141
142
143
144
145
146
147
|
# File 'lib/ciri/p2p/peer_store.rb', line 138
def find_attempt_peers(count)
@peers.values.reject do |peer_info|
@bootnodes.include?(peer_info[:node]) || peer_status(peer_info[:node].raw_node_id) == Status::CONNECTED
end.sort_by do |peer_info|
-peer_info[:score]
end.map do |peer_info|
peer_info[:node]
end.take(count)
end
|
#find_bootnodes(count) ⇒ Object
TODO find high scoring peers, use bootnodes as fallback
132
133
134
135
|
# File 'lib/ciri/p2p/peer_store.rb', line 132
def find_bootnodes(count)
nodes = @bootnodes.sample(count)
nodes + find_attempt_peers(count - nodes.size)
end
|
#get_node_addresses(raw_node_id) ⇒ Object
157
158
159
160
|
# File 'lib/ciri/p2p/peer_store.rb', line 157
def get_node_addresses(raw_node_id)
peer_info = @peers[raw_node_id]
peer_info && peer_info[:node].addresses
end
|
#has_ban?(raw_node_id, now: Time.now) ⇒ Boolean
109
110
111
112
113
114
115
116
117
|
# File 'lib/ciri/p2p/peer_store.rb', line 109
def has_ban?(raw_node_id, now: Time.now)
record = @ban_peers[raw_node_id]
if record && (record[:ban_at].to_i + record[:timeout_secs]) > now.to_i
true
else
@ban_peers.delete(raw_node_id)
false
end
end
|
#has_ping?(raw_node_id, ping_hash, expires_in: PING_EXPIRATION_IN) ⇒ Boolean
77
78
79
80
81
82
83
84
85
86
|
# File 'lib/ciri/p2p/peer_store.rb', line 77
def has_ping?(raw_node_id, ping_hash, expires_in: PING_EXPIRATION_IN)
return false if has_ban?(raw_node_id)
record = @peers_ping_records[raw_node_id]
if record && record[:ping_hash] == ping_hash && (record[:ping_at] + expires_in) > Time.now.to_i
return true
elsif record
@peers_ping_records.delete(raw_node_id)
end
false
end
|
#has_seen?(raw_node_id, expires_in: PEER_LAST_SEEN_VALID) ⇒ Boolean
97
98
99
100
101
102
|
# File 'lib/ciri/p2p/peer_store.rb', line 97
def has_seen?(raw_node_id, expires_in: PEER_LAST_SEEN_VALID)
return false if has_ban?(raw_node_id)
seen = (last_seen_at = @peers_seen_records[raw_node_id]) && (last_seen_at + expires_in > Time.now.to_i)
!!seen
end
|
#peer_status(raw_node_id) ⇒ Object
166
167
168
169
170
171
172
|
# File 'lib/ciri/p2p/peer_store.rb', line 166
def peer_status(raw_node_id)
if (peer_info = @peers[raw_node_id])
peer_info[:status]
else
Status::UNKNOWN
end
end
|
#report_peer(raw_node_id, behaviour) ⇒ Object
123
124
125
126
127
128
129
|
# File 'lib/ciri/p2p/peer_store.rb', line 123
def report_peer(raw_node_id, behaviour)
score = @score_schema[behaviour]
raise ValueError.new("unsupport report behaviour: #{behaviour}") if score.nil?
if (node_info = @peers[raw_node_id])
node_info[:score] += score
end
end
|
#update_last_seen(raw_node_id, at: Time.now.to_i) ⇒ Object
93
94
95
|
# File 'lib/ciri/p2p/peer_store.rb', line 93
def update_last_seen(raw_node_id, at: Time.now.to_i)
@peers_seen_records[raw_node_id] = at
end
|
#update_peer_status(raw_node_id, status) ⇒ Object
174
175
176
177
178
|
# File 'lib/ciri/p2p/peer_store.rb', line 174
def update_peer_status(raw_node_id, status)
if (peer_info = @peers[raw_node_id])
peer_info[:status] = status
end
end
|
#update_ping(raw_node_id, ping_hash, ping_at: Time.now.to_i) ⇒ Object
89
90
91
|
# File 'lib/ciri/p2p/peer_store.rb', line 89
def update_ping(raw_node_id, ping_hash, ping_at: Time.now.to_i)
@peers_ping_records[raw_node_id] = {ping_hash: ping_hash, ping_at: ping_at}
end
|