class Ciri::P2P::PeerStore
PeerStore store information of all peers we have seen
TODO rewrite with a database(sqlite)
Support score peers
Constants
- DEFAULT_SCORE_SCHEMA
- PEER_INITIAL_SCORE
- PEER_LAST_SEEN_VALID
- PING_EXPIRATION_IN
Public Class Methods
new(score_schema:{})
click to toggle source
# 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
Public Instance Methods
add_bootnode(node)
click to toggle source
# File lib/ciri/p2p/peer_store.rb, line 104 def add_bootnode(node) @bootnodes << node add_node(node) end
add_node(node)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
TODO find high scoring peers
# File lib/ciri/p2p/peer_store.rb, line 138 def find_attempt_peers(count) @peers.values.reject do |peer_info| # reject already connected peers and bootnodes @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)
click to toggle source
TODO find high scoring peers, use bootnodes as fallback
# 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)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
# 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) # convert to bool !!seen end
peer_status(raw_node_id)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
record ping message
# 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