devp2p 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +22 -0
- data/lib/devp2p.rb +57 -0
- data/lib/devp2p/app_helper.rb +85 -0
- data/lib/devp2p/base_app.rb +80 -0
- data/lib/devp2p/base_protocol.rb +136 -0
- data/lib/devp2p/base_service.rb +55 -0
- data/lib/devp2p/command.rb +82 -0
- data/lib/devp2p/configurable.rb +32 -0
- data/lib/devp2p/connection_monitor.rb +77 -0
- data/lib/devp2p/control.rb +32 -0
- data/lib/devp2p/crypto.rb +73 -0
- data/lib/devp2p/crypto/ecc_x.rb +133 -0
- data/lib/devp2p/crypto/ecies.rb +134 -0
- data/lib/devp2p/discovery.rb +118 -0
- data/lib/devp2p/discovery/address.rb +83 -0
- data/lib/devp2p/discovery/kademlia_protocol_adapter.rb +11 -0
- data/lib/devp2p/discovery/node.rb +32 -0
- data/lib/devp2p/discovery/protocol.rb +342 -0
- data/lib/devp2p/discovery/transport.rb +105 -0
- data/lib/devp2p/exception.rb +30 -0
- data/lib/devp2p/frame.rb +197 -0
- data/lib/devp2p/kademlia.rb +48 -0
- data/lib/devp2p/kademlia/k_bucket.rb +178 -0
- data/lib/devp2p/kademlia/node.rb +40 -0
- data/lib/devp2p/kademlia/protocol.rb +284 -0
- data/lib/devp2p/kademlia/routing_table.rb +131 -0
- data/lib/devp2p/kademlia/wire_interface.rb +30 -0
- data/lib/devp2p/multiplexed_session.rb +110 -0
- data/lib/devp2p/multiplexer.rb +358 -0
- data/lib/devp2p/p2p_protocol.rb +170 -0
- data/lib/devp2p/packet.rb +35 -0
- data/lib/devp2p/peer.rb +329 -0
- data/lib/devp2p/peer_errors.rb +35 -0
- data/lib/devp2p/peer_manager.rb +274 -0
- data/lib/devp2p/rlpx_session.rb +434 -0
- data/lib/devp2p/sync_queue.rb +76 -0
- data/lib/devp2p/utils.rb +106 -0
- data/lib/devp2p/version.rb +13 -0
- data/lib/devp2p/wired_service.rb +30 -0
- metadata +227 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module DEVp2p
|
4
|
+
|
5
|
+
##
|
6
|
+
# DEV P2P Wire Protocol
|
7
|
+
#
|
8
|
+
# @see https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol
|
9
|
+
#
|
10
|
+
class P2PProtocol < BaseProtocol
|
11
|
+
|
12
|
+
class Ping < Command
|
13
|
+
cmd_id 2
|
14
|
+
|
15
|
+
def receive(proto, data)
|
16
|
+
proto.send_pong
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Pong < Command
|
21
|
+
cmd_id 3
|
22
|
+
end
|
23
|
+
|
24
|
+
class Hello < Command
|
25
|
+
cmd_id 0
|
26
|
+
decode_strict false # don't throw for additional list elements as mandated by EIP-8
|
27
|
+
|
28
|
+
structure(
|
29
|
+
version: RLP::Sedes.big_endian_int,
|
30
|
+
client_version_string: RLP::Sedes.binary,
|
31
|
+
capabilities: RLP::Sedes::CountableList.new(
|
32
|
+
RLP::Sedes::List.new(elements: [RLP::Sedes.binary, RLP::Sedes.big_endian_int])
|
33
|
+
),
|
34
|
+
listen_port: RLP::Sedes.big_endian_int,
|
35
|
+
remote_pubkey: RLP::Sedes.binary
|
36
|
+
)
|
37
|
+
|
38
|
+
def create(proto)
|
39
|
+
{ version: proto.class.version,
|
40
|
+
client_version_string: proto.config[:client_version_string],
|
41
|
+
capabilities: proto.peer.capabilities,
|
42
|
+
listen_port: proto.config[:p2p][:listen_port],
|
43
|
+
remote_pubkey: proto.config[:node][:id] }
|
44
|
+
end
|
45
|
+
|
46
|
+
def receive(proto, data)
|
47
|
+
logger.debug 'receive_hello', peer: proto.peer, version: data[:version]
|
48
|
+
|
49
|
+
reasons = proto.class::Disconnect::Reason
|
50
|
+
if data[:remote_pubkey] == proto.config[:node][:id]
|
51
|
+
logger.debug 'connected myself'
|
52
|
+
return proto.send_disconnect(reason: reasons[:connected_to_self])
|
53
|
+
end
|
54
|
+
|
55
|
+
proto.peer.receive_hello proto, data
|
56
|
+
super(proto, data)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def logger
|
62
|
+
@logger = Logger.new "#{::Celluloid::Actor.current.peer.config[:p2p][:listen_port]}.p2p.protocol"
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
class Disconnect < Command
|
68
|
+
cmd_id 1
|
69
|
+
|
70
|
+
structure reason: RLP::Sedes.big_endian_int
|
71
|
+
|
72
|
+
Reason = {
|
73
|
+
disconnect_requested: 0,
|
74
|
+
tcp_sub_system_error: 1,
|
75
|
+
bad_protocol: 2, # e.g. a malformed message, bad RLP, incorrect magic number
|
76
|
+
useless_peer: 3,
|
77
|
+
too_many_peers: 4,
|
78
|
+
already_connected: 5,
|
79
|
+
incompatible_p2p_version: 6,
|
80
|
+
null_node_identity_received: 7,
|
81
|
+
client_quitting: 8,
|
82
|
+
unexpected_identity: 9,
|
83
|
+
connected_to_self: 10,
|
84
|
+
timeout: 11,
|
85
|
+
subprotocol_error: 12,
|
86
|
+
other: 16
|
87
|
+
}.freeze
|
88
|
+
|
89
|
+
def reason_key(id)
|
90
|
+
Reason.invert[id]
|
91
|
+
end
|
92
|
+
|
93
|
+
def reason_name(id)
|
94
|
+
key = reason_key id
|
95
|
+
key ? key.to_s : "unknown (id:#{id})"
|
96
|
+
end
|
97
|
+
|
98
|
+
def create(proto, reason=Reason[:client_quitting])
|
99
|
+
raise ArgumentError, "unknown reason" unless reason_key(reason)
|
100
|
+
logger.debug "send_disconnect", peer: proto.peer, reason: reason_name(reason)
|
101
|
+
|
102
|
+
proto.peer.report_error "sending disconnect #{reason_name(reason)}"
|
103
|
+
|
104
|
+
Celluloid::Actor.current.after(0.5) { proto.peer.stop }
|
105
|
+
|
106
|
+
{reason: reason}
|
107
|
+
end
|
108
|
+
|
109
|
+
def receive(proto, data)
|
110
|
+
logger.debug "receive_disconnect", peer: proto.peer, reason: reason_name(data[:reason])
|
111
|
+
proto.peer.report_error "disconnected #{reason_name(data[:reason])}"
|
112
|
+
proto.peer.stop
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def logger
|
118
|
+
@logger = Logger.new "#{::Celluloid::Actor.current.peer.config[:p2p][:listen_port]}.p2p.protocol"
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
class <<self
|
124
|
+
# special: we need this packet before the protocol can be initialized
|
125
|
+
def get_hello_packet(peer)
|
126
|
+
res = {
|
127
|
+
version: 55,
|
128
|
+
client_version_string: peer.config[:client_version_string],
|
129
|
+
capabilities: peer.capabilities,
|
130
|
+
listen_port: peer.config[:p2p][:listen_port],
|
131
|
+
remote_pubkey: peer.config[:node][:id]
|
132
|
+
}
|
133
|
+
|
134
|
+
payload = Hello.encode_payload(res)
|
135
|
+
Packet.new protocol_id, Hello.cmd_id, payload
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
name 'p2p'
|
140
|
+
protocol_id 0
|
141
|
+
version 4
|
142
|
+
max_cmd_id 15
|
143
|
+
|
144
|
+
attr :config
|
145
|
+
|
146
|
+
def initialize(peer, service)
|
147
|
+
raise ArgumentError, "invalid peer" unless peer.respond_to?(:capabilities)
|
148
|
+
raise ArgumentError, "invalid peer" unless peer.respond_to?(:stop)
|
149
|
+
raise ArgumentError, "invalid peer" unless peer.respond_to?(:receive_hello)
|
150
|
+
|
151
|
+
@config = peer.config
|
152
|
+
super(peer, service)
|
153
|
+
|
154
|
+
@monitor = ConnectionMonitor.new self
|
155
|
+
end
|
156
|
+
|
157
|
+
def stop
|
158
|
+
@monitor.stop
|
159
|
+
super
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def logger
|
165
|
+
@logger = Logger.new "#{@config[:p2p][:listen_port]}.p2p.protocol"
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module DEVp2p
|
4
|
+
|
5
|
+
##
|
6
|
+
# Packets are emitted and received by subprotocols.
|
7
|
+
#
|
8
|
+
class Packet
|
9
|
+
|
10
|
+
attr_accessor :protocol_id, :cmd_id, :prioritize, :payload, :total_payload_size
|
11
|
+
|
12
|
+
def initialize(protocol_id, cmd_id, payload, prioritize=false)
|
13
|
+
@protocol_id = protocol_id
|
14
|
+
@cmd_id = cmd_id
|
15
|
+
@payload = payload
|
16
|
+
@prioritize = prioritize
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"Packet(protocol_id=#{protocol_id} cmd_id=#{cmd_id} payload_size=#{payload.size} prioritize=#{prioritize})"
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
protocol_id == other.protocol_id &&
|
25
|
+
cmd_id == other.cmd_id &&
|
26
|
+
payload == other.payload
|
27
|
+
end
|
28
|
+
|
29
|
+
def size
|
30
|
+
payload.size
|
31
|
+
end
|
32
|
+
alias :length :size
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/devp2p/peer.rb
ADDED
@@ -0,0 +1,329 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module DEVp2p
|
4
|
+
|
5
|
+
class Peer
|
6
|
+
include Celluloid::IO
|
7
|
+
|
8
|
+
DUMB_REMOTE_TIMEOUT = 10.0
|
9
|
+
|
10
|
+
attr :config, :protocols, :safe_to_read
|
11
|
+
|
12
|
+
def initialize(peermanager, socket, remote_pubkey=nil)
|
13
|
+
@peermanager = peermanager
|
14
|
+
@socket = socket
|
15
|
+
@config = peermanager.config
|
16
|
+
|
17
|
+
@protocols = {}
|
18
|
+
|
19
|
+
@stopped = true
|
20
|
+
@hello_received = false
|
21
|
+
|
22
|
+
@remote_client_version = ''
|
23
|
+
logger.debug "peer init", peer: Actor.current
|
24
|
+
|
25
|
+
privkey = Utils.decode_hex @config[:node][:privkey_hex]
|
26
|
+
hello_packet = P2PProtocol.get_hello_packet Actor.current
|
27
|
+
|
28
|
+
@mux = MultiplexedSession.new privkey, hello_packet, remote_pubkey
|
29
|
+
@remote_pubkey = remote_pubkey
|
30
|
+
|
31
|
+
connect_service @peermanager
|
32
|
+
|
33
|
+
# assure, we don't get messages while replies are not read
|
34
|
+
# TODO: is it safe to use flag + cond to mimic gevent.event.Event?
|
35
|
+
@safe_to_read = true
|
36
|
+
@safe_to_read_cond = Celluloid::Condition.new
|
37
|
+
|
38
|
+
_, @port, _, @ip = @socket.peeraddr
|
39
|
+
|
40
|
+
# stop peer if hello not received in DUMB_REMOTE_TIMEOUT
|
41
|
+
after(DUMB_REMOTE_TIMEOUT) { check_if_dumb_remote }
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# if peer is responder, then the remote_pubkey will not be available before
|
46
|
+
# the first packet is received
|
47
|
+
#
|
48
|
+
def remote_pubkey
|
49
|
+
@mux.remote_pubkey
|
50
|
+
end
|
51
|
+
|
52
|
+
def remote_pubkey=(key)
|
53
|
+
@remote_pubkey_available = !!key
|
54
|
+
@mux.remote_pubkey = key
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
pn = "#@ip:#@port"
|
59
|
+
cv = @remote_client_version.split('/')[0,2].join('/')
|
60
|
+
"<Peer #{pn} #{cv}>"
|
61
|
+
end
|
62
|
+
|
63
|
+
def report_error(reason)
|
64
|
+
pn = "#@ip:#@port"
|
65
|
+
@peermanager.add_error pn, reason, @remote_client_version
|
66
|
+
end
|
67
|
+
|
68
|
+
def connect_service(service)
|
69
|
+
raise ArgumentError, "service must be WiredService" unless service.is_a?(WiredService)
|
70
|
+
|
71
|
+
# create protocol instance which connects peer with service
|
72
|
+
protocol_class = service.wire_protocol
|
73
|
+
protocol = protocol_class.new Actor.current, service
|
74
|
+
|
75
|
+
# register protocol
|
76
|
+
raise PeerError, 'protocol already connected' if @protocols.has_key?(protocol_class)
|
77
|
+
logger.debug "registering protocol", protocol: protocol.name, peer: Actor.current
|
78
|
+
|
79
|
+
@protocols[protocol_class] = protocol
|
80
|
+
@mux.add_protocol protocol.protocol_id
|
81
|
+
|
82
|
+
protocol.start
|
83
|
+
end
|
84
|
+
|
85
|
+
def has_protocol?(protocol)
|
86
|
+
@protocols.has_key?(protocol)
|
87
|
+
end
|
88
|
+
|
89
|
+
def receive_hello(proto, data)
|
90
|
+
version = data[:version]
|
91
|
+
listen_port = data[:listen_port]
|
92
|
+
capabilities = data[:capabilities]
|
93
|
+
remote_pubkey = data[:remote_pubkey]
|
94
|
+
client_version_string = data[:client_version_string]
|
95
|
+
|
96
|
+
logger.info 'received hello', version: version, client_version: client_version_string, capabilities: capabilities
|
97
|
+
|
98
|
+
raise ArgumentError, "invalid remote pubkey" unless remote_pubkey.size == 64
|
99
|
+
raise ArgumentError, "remote pubkey mismatch" if @remote_pubkey_available && @remote_pubkey != remote_pubkey
|
100
|
+
|
101
|
+
@hello_received = true
|
102
|
+
|
103
|
+
# enable backwards compatibility for legacy peers
|
104
|
+
if version < 5
|
105
|
+
@offset_based_dispatch = true
|
106
|
+
max_window_size = 2**32 # disable chunked transfers
|
107
|
+
end
|
108
|
+
|
109
|
+
# call peermanager
|
110
|
+
agree = @peermanager.on_hello_received(proto, version, client_version_string, capabilities, listen_port, remote_pubkey)
|
111
|
+
return unless agree
|
112
|
+
|
113
|
+
@remote_client_version = client_version_string
|
114
|
+
@remote_pubkey = remote_pubkey
|
115
|
+
|
116
|
+
# register in common protocols
|
117
|
+
logger.debug 'connecting services', services: @peermanager.wired_services
|
118
|
+
remote_services = capabilities.map {|name, version| [name, version] }.to_h
|
119
|
+
|
120
|
+
@peermanager.wired_services.sort_by(&:name).each do |service|
|
121
|
+
raise PeerError, 'invalid service' unless service.is_a?(WiredService)
|
122
|
+
|
123
|
+
proto = service.wire_protocol
|
124
|
+
if remote_services.has_key?(proto.name)
|
125
|
+
if remote_services[proto.name] == proto.version
|
126
|
+
if service != @peermanager # p2p protocol already registered
|
127
|
+
connect_service service
|
128
|
+
end
|
129
|
+
else
|
130
|
+
logger.debug 'wrong version', service: proto.name, local_version: proto.version, remote_version: remote_services[proto.name]
|
131
|
+
report_error 'wrong version'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def capabilities
|
138
|
+
@peermanager.wired_services.map {|s| [s.wire_protocol.name, s.wire_protocol.version] }
|
139
|
+
end
|
140
|
+
|
141
|
+
def send_packet(packet)
|
142
|
+
protocol = @protocols.values.find {|pro| pro.protocol_id == packet.protocol_id }
|
143
|
+
raise PeerError, "no protocol found" unless protocol
|
144
|
+
logger.debug "send packet", cmd: protocol.cmd_by_id[packet.cmd_id], protocol: protocol.name, peer: Actor.current
|
145
|
+
|
146
|
+
# rewrite cmd_id (backwards compatibility)
|
147
|
+
if @offset_based_dispatch
|
148
|
+
@protocols.values.each_with_index do |proto, i|
|
149
|
+
if packet.protocol_id > i
|
150
|
+
packet.cmd_id += (protocol.max_cmd_id == 0 ? 0 : protocol.max_cmd_id + 1)
|
151
|
+
end
|
152
|
+
if packet.protocol_id == protocol.protocol_id
|
153
|
+
protocol = proto
|
154
|
+
break
|
155
|
+
end
|
156
|
+
packet.protocol_id = 0
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
@mux.add_packet packet
|
161
|
+
end
|
162
|
+
|
163
|
+
def send_data(data)
|
164
|
+
return if data.nil? || data.empty?
|
165
|
+
|
166
|
+
@safe_to_read = false
|
167
|
+
|
168
|
+
@socket.write data
|
169
|
+
logger.debug "wrote data", size: data.size
|
170
|
+
|
171
|
+
@safe_to_read = true
|
172
|
+
@safe_to_read_cond.broadcast
|
173
|
+
rescue Errno::ETIMEDOUT
|
174
|
+
logger.debug "write timeout"
|
175
|
+
report_error "write timeout"
|
176
|
+
stop
|
177
|
+
rescue SystemCallError => e
|
178
|
+
logger.debug "write error #{e}"
|
179
|
+
report_error "write error #{e}"
|
180
|
+
stop
|
181
|
+
end
|
182
|
+
|
183
|
+
def run_ingress_message
|
184
|
+
logger.debug "peer starting main loop"
|
185
|
+
raise PeerError, 'connection is closed' if @socket.closed?
|
186
|
+
|
187
|
+
async.run_decoded_packets
|
188
|
+
async.run_egress_message
|
189
|
+
|
190
|
+
while !stopped?
|
191
|
+
@safe_to_read_cond.wait unless safe_to_read
|
192
|
+
|
193
|
+
begin
|
194
|
+
@socket.wait_readable
|
195
|
+
rescue SystemCallError => e
|
196
|
+
logger.debug "read error", error: e, peer: Actor.current
|
197
|
+
report_error "network error #{e}"
|
198
|
+
if Errno::EBADF === e # bad file descriptor
|
199
|
+
stop
|
200
|
+
else
|
201
|
+
raise e
|
202
|
+
break
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
begin
|
207
|
+
imsg = @socket.recv(4096)
|
208
|
+
rescue EOFError # imsg is empty
|
209
|
+
if @socket.closed?
|
210
|
+
logger.info "socket closed"
|
211
|
+
stop
|
212
|
+
else
|
213
|
+
imsg = ''
|
214
|
+
end
|
215
|
+
rescue SystemCallError => e
|
216
|
+
logger.debug "read error", error: e, peer: Actor.current
|
217
|
+
report_error "network error #{e}"
|
218
|
+
if [Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::ENETDOWN, Errno::EHOSTUNREACH].any? {|syserr| e.instance_of?(syserr) }
|
219
|
+
stop
|
220
|
+
else
|
221
|
+
raise e
|
222
|
+
break
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
if !imsg.empty?
|
227
|
+
logger.debug "read data", size: imsg.size
|
228
|
+
@mux.add_message imsg
|
229
|
+
end
|
230
|
+
end
|
231
|
+
rescue RLPxSessionError, DecryptionError => e
|
232
|
+
logger.debug "rlpx session error", peer: Actor.current, error: e
|
233
|
+
report_error "rlpx session error"
|
234
|
+
stop
|
235
|
+
rescue MultiplexerError => e
|
236
|
+
logger.debug "multiplexer error", peer: Actor.current, error: e
|
237
|
+
report_error "multiplexer error"
|
238
|
+
stop
|
239
|
+
end
|
240
|
+
alias run run_ingress_message
|
241
|
+
|
242
|
+
def start
|
243
|
+
@stopped = false
|
244
|
+
async.run
|
245
|
+
end
|
246
|
+
|
247
|
+
def stop
|
248
|
+
if !stopped?
|
249
|
+
@stopped = true
|
250
|
+
logger.debug "stopped", peer: Actor.current
|
251
|
+
|
252
|
+
@protocols.each_value {|proto| proto.stop }
|
253
|
+
@peermanager.delete Actor.current
|
254
|
+
terminate
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def stopped?
|
259
|
+
@stopped
|
260
|
+
end
|
261
|
+
|
262
|
+
private
|
263
|
+
|
264
|
+
def logger
|
265
|
+
@logger ||= Logger.new "#{@config[:p2p][:listen_port]}.p2p.peer"
|
266
|
+
end
|
267
|
+
|
268
|
+
def handle_packet(packet)
|
269
|
+
raise ArgumentError, 'packet must be Packet' unless packet.is_a?(Packet)
|
270
|
+
|
271
|
+
protocol, cmd_id = protocol_cmd_id_from_packet packet
|
272
|
+
logger.debug "recv packet", cmd: protocol.cmd_by_id[cmd_id], protocol: protocol.name, orig_cmd_id: packet.cmd_id
|
273
|
+
|
274
|
+
packet.cmd_id = cmd_id # rewrite
|
275
|
+
protocol.receive_packet packet
|
276
|
+
rescue UnknownCommandError
|
277
|
+
logger.error 'received unknown cmd', error: e, packet: packet
|
278
|
+
end
|
279
|
+
|
280
|
+
def protocol_cmd_id_from_packet(packet)
|
281
|
+
# offset-based dispatch (backwards compatibility)
|
282
|
+
if @offset_based_dispatch
|
283
|
+
max_id = 0
|
284
|
+
|
285
|
+
@protocols.each_value do |protocol|
|
286
|
+
if packet.cmd_id < max_id + protocol.max_cmd_id + 1
|
287
|
+
return protocol, packet.cmd_id - (max_id == 0 ? 0 : max_id + 1)
|
288
|
+
max_id += protocol.max_cmd_id
|
289
|
+
end
|
290
|
+
end
|
291
|
+
raise UnknownCommandError, "no protocol for id #{packet.cmd_id}"
|
292
|
+
end
|
293
|
+
|
294
|
+
# new-style dispatch based on protocol_id
|
295
|
+
@protocols.values.each_with_index do |protocol, i|
|
296
|
+
if packet.protocol_id == protocol.protocol_id
|
297
|
+
return protocol, packet.cmd_id
|
298
|
+
end
|
299
|
+
end
|
300
|
+
raise UnknownCommandError, "no protocol for protocol id #{packet.protocol_id}"
|
301
|
+
end
|
302
|
+
|
303
|
+
##
|
304
|
+
# Stop peer if hello not received
|
305
|
+
#
|
306
|
+
def check_if_dumb_remote
|
307
|
+
if !@hello_received
|
308
|
+
report_error "No hello in #{DUMB_REMOTE_TIMEOUT} seconds"
|
309
|
+
stop
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
def run_egress_message
|
314
|
+
while !stopped?
|
315
|
+
# TODO: async.send_data?
|
316
|
+
send_data @mux.get_message
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def run_decoded_packets
|
321
|
+
while !stopped?
|
322
|
+
# TODO: async.handle_packet?
|
323
|
+
handle_packet @mux.get_packet # get_packet blocks
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|