devp2p 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,7 @@ module DEVp2p
7
7
  #
8
8
  # @see https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol
9
9
  #
10
- class P2PProtocol < BaseProtocol
10
+ class P2PProtocol < Protocol
11
11
 
12
12
  class Ping < Command
13
13
  cmd_id 2
@@ -52,14 +52,14 @@ module DEVp2p
52
52
  return proto.send_disconnect(reason: reasons[:connected_to_self])
53
53
  end
54
54
 
55
- proto.peer.receive_hello proto, data
55
+ proto.peer.async.receive_hello proto, data
56
56
  super(proto, data)
57
57
  end
58
58
 
59
59
  private
60
60
 
61
61
  def logger
62
- @logger = Logger.new "#{::Celluloid::Actor.current.peer.config[:p2p][:listen_port]}.p2p.protocol"
62
+ @logger = Logger.new "p2p.protocol"
63
63
  end
64
64
 
65
65
  end
@@ -99,23 +99,23 @@ module DEVp2p
99
99
  raise ArgumentError, "unknown reason" unless reason_key(reason)
100
100
  logger.debug "send_disconnect", peer: proto.peer, reason: reason_name(reason)
101
101
 
102
- proto.peer.report_error "sending disconnect #{reason_name(reason)}"
102
+ proto.peer.async.report_error "sending disconnect #{reason_name(reason)}"
103
103
 
104
- Celluloid::Actor.current.after(0.5) { proto.peer.stop }
104
+ Concurrent::ScheduledTask.execute(0.5) { proto.peer.async.stop }
105
105
 
106
106
  {reason: reason}
107
107
  end
108
108
 
109
109
  def receive(proto, data)
110
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
111
+ proto.peer.async.report_error "disconnected #{reason_name(data[:reason])}"
112
+ proto.peer.async.stop
113
113
  end
114
114
 
115
115
  private
116
116
 
117
117
  def logger
118
- @logger = Logger.new "#{::Celluloid::Actor.current.peer.config[:p2p][:listen_port]}.p2p.protocol"
118
+ @logger = Logger.new "p2p.protocol"
119
119
  end
120
120
 
121
121
  end
@@ -154,7 +154,7 @@ module DEVp2p
154
154
  private
155
155
 
156
156
  def logger
157
- @logger = Logger.new "#{@config[:p2p][:listen_port]}.p2p.protocol"
157
+ @logger = Logger.new "p2p.protocol"
158
158
  end
159
159
 
160
160
  end
@@ -3,7 +3,7 @@
3
3
  module DEVp2p
4
4
 
5
5
  class Peer
6
- include Celluloid::IO
6
+ include Concurrent::Async
7
7
 
8
8
  DUMB_REMOTE_TIMEOUT = 10.0
9
9
 
@@ -16,12 +16,12 @@ module DEVp2p
16
16
 
17
17
  @protocols = {}
18
18
 
19
- @stopped = true
19
+ @stopped = false
20
20
  @hello_received = false
21
21
 
22
22
  _, @port, _, @ip = @socket.peeraddr
23
23
  @remote_client_version = ''
24
- logger.debug "peer init", peer: Actor.current
24
+ logger.debug "peer init", peer: self
25
25
 
26
26
  privkey = Utils.decode_hex @config[:node][:privkey_hex]
27
27
  hello_packet = P2PProtocol.get_hello_packet hello_data
@@ -32,16 +32,37 @@ module DEVp2p
32
32
  connect_service @peermanager
33
33
 
34
34
  # assure, we don't get messages while replies are not read
35
- # TODO: is it safe to use flag + cond to mimic gevent.event.Event?
36
- @safe_to_read = true
37
- @safe_to_read_cond = Celluloid::Condition.new
35
+ @safe_to_read = Concurrent::Event.new
36
+ @safe_to_read.set
38
37
 
39
38
  # stop peer if hello not received in DUMB_REMOTE_TIMEOUT
40
- after(DUMB_REMOTE_TIMEOUT) { check_if_dumb_remote }
39
+ Concurrent::ScheduledTask.execute(DUMB_REMOTE_TIMEOUT) { check_if_dumb_remote }
40
+ end
41
+
42
+ def start
43
+ @stopped = false
44
+ @run = Thread.new { run }
45
+ end
46
+
47
+ def stop
48
+ if !stopped?
49
+ @stopped = true
50
+
51
+ @protocols.each_value {|proto| proto.async.stop }
52
+ @peermanager.async.delete self
53
+
54
+ logger.info "peer stopped", peer: self
55
+ @run.kill
56
+ @run_decoded_packets.kill
57
+ @run_egress_message.kill
58
+ end
59
+ rescue
60
+ puts $!
61
+ puts $!.backtrace[0,10].join("\n")
41
62
  end
42
63
 
43
- def wait_to_read
44
- @safe_to_read_cond.wait unless @safe_to_read
64
+ def stopped?
65
+ @stopped
45
66
  end
46
67
 
47
68
  ##
@@ -60,8 +81,10 @@ module DEVp2p
60
81
  def to_s
61
82
  pn = "#@ip:#@port"
62
83
  cv = @remote_client_version.split('/')[0,2].join('/')
63
- "<Peer #{pn} #{cv}>"
84
+ pn = "#{pn} #{cv}" unless cv.empty?
85
+ "<Peer #{pn}>"
64
86
  end
87
+ alias inspect to_s
65
88
 
66
89
  def report_error(reason)
67
90
  pn = "#@ip:#@port"
@@ -73,11 +96,11 @@ module DEVp2p
73
96
 
74
97
  # create protocol instance which connects peer with service
75
98
  protocol_class = service.wire_protocol
76
- protocol = protocol_class.new Actor.current, service
99
+ protocol = protocol_class.new self, service
77
100
 
78
101
  # register protocol
79
102
  raise PeerError, 'protocol already connected' if @protocols.has_key?(protocol_class)
80
- logger.debug "registering protocol", protocol: protocol.name, peer: Actor.current
103
+ logger.debug "registering protocol", protocol: protocol.name, peer: self
81
104
 
82
105
  @protocols[protocol_class] = protocol
83
106
  @mux.add_protocol protocol.protocol_id
@@ -144,7 +167,7 @@ module DEVp2p
144
167
  def send_packet(packet)
145
168
  protocol = @protocols.values.find {|pro| pro.protocol_id == packet.protocol_id }
146
169
  raise PeerError, "no protocol found" unless protocol
147
- logger.debug "send packet", cmd: protocol.cmd_by_id[packet.cmd_id], protocol: protocol.name, peer: Actor.current
170
+ logger.debug "send packet", cmd: protocol.cmd_by_id[packet.cmd_id], protocol: protocol.name, peer: self
148
171
 
149
172
  # rewrite cmd_id (backwards compatibility)
150
173
  if @offset_based_dispatch
@@ -166,13 +189,12 @@ module DEVp2p
166
189
  def send_data(data)
167
190
  return if data.nil? || data.empty?
168
191
 
169
- @safe_to_read = false
192
+ @safe_to_read.reset
170
193
 
171
194
  @socket.write data
172
195
  logger.debug "wrote data", size: data.size
173
196
 
174
- @safe_to_read = true
175
- @safe_to_read_cond.broadcast
197
+ @safe_to_read.set
176
198
  rescue Errno::ETIMEDOUT
177
199
  logger.debug "write timeout"
178
200
  report_error "write timeout"
@@ -183,31 +205,22 @@ module DEVp2p
183
205
  stop
184
206
  end
185
207
 
186
- def run_ingress_message
208
+ def run
187
209
  logger.debug "peer starting main loop"
188
210
  raise PeerError, 'connection is closed' if @socket.closed?
189
211
 
190
- async.run_decoded_packets
191
- async.run_egress_message
212
+ @run_decoded_packets = Thread.new { run_decoded_packets }
213
+ @run_egress_message = Thread.new { run_egress_message }
192
214
 
193
215
  while !stopped?
194
- wait_to_read
216
+ @safe_to_read.wait
195
217
 
196
218
  begin
197
- @socket.wait_readable
198
- rescue SystemCallError => e
199
- logger.debug "read error", error: e, peer: Actor.current
200
- report_error "network error #{e}"
201
- if Errno::EBADF === e # bad file descriptor
219
+ imsg = @socket.recv(4096)
220
+ if imsg.empty?
221
+ logger.info "socket closed"
202
222
  stop
203
- else
204
- raise e
205
- break
206
223
  end
207
- end
208
-
209
- begin
210
- imsg = @socket.recv(4096)
211
224
  rescue EOFError # imsg is empty
212
225
  if @socket.closed?
213
226
  logger.info "socket closed"
@@ -216,7 +229,7 @@ module DEVp2p
216
229
  imsg = ''
217
230
  end
218
231
  rescue SystemCallError => e
219
- logger.debug "read error", error: e, peer: Actor.current
232
+ logger.debug "read error", error: e, peer: self
220
233
  report_error "network error #{e}"
221
234
  if [Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::ENETDOWN, Errno::EHOSTUNREACH].any? {|syserr| e.instance_of?(syserr) }
222
235
  stop
@@ -232,45 +245,23 @@ module DEVp2p
232
245
  end
233
246
  end
234
247
  rescue RLPxSessionError, DecryptionError => e
235
- logger.debug "rlpx session error", peer: Actor.current, error: e
248
+ logger.debug "rlpx session error", peer: self, error: e
236
249
  report_error "rlpx session error"
237
250
  stop
238
251
  rescue MultiplexerError => e
239
- logger.debug "multiplexer error", peer: Actor.current, error: e
252
+ logger.debug "multiplexer error", peer: self, error: e
240
253
  report_error "multiplexer error"
241
254
  stop
242
255
  rescue
243
- logger.debug "ingress message error", peer: Actor.current, error: $!
256
+ logger.debug "ingress message error", peer: self, error: $!
244
257
  report_error "ingress message error"
245
258
  stop
246
259
  end
247
- alias run run_ingress_message
248
-
249
- def start
250
- @stopped = false
251
- async.run
252
- end
253
-
254
- def stop
255
- if !stopped?
256
- @stopped = true
257
-
258
- @protocols.each_value {|proto| proto.stop }
259
- @peermanager.delete Actor.current
260
-
261
- logger.info "peer stopped", peer: Actor.current
262
- terminate
263
- end
264
- end
265
-
266
- def stopped?
267
- @stopped
268
- end
269
260
 
270
261
  private
271
262
 
272
263
  def logger
273
- @logger ||= Logger.new "#{@config[:p2p][:listen_port]}.p2p.peer"
264
+ @logger ||= Logger.new "p2p.peer"
274
265
  end
275
266
 
276
267
  def hello_data
@@ -291,6 +282,9 @@ module DEVp2p
291
282
  protocol.receive_packet packet
292
283
  rescue UnknownCommandError => e
293
284
  logger.error 'received unknown cmd', error: e, packet: packet
285
+ rescue
286
+ logger.error $!
287
+ logger.error $!.backtrace[0,10].join("\n")
294
288
  end
295
289
 
296
290
  def protocol_cmd_id_from_packet(packet)
@@ -12,8 +12,43 @@ module DEVp2p
12
12
  # connect closest node
13
13
  #
14
14
  class PeerManager < WiredService
15
- include Celluloid::IO
16
- finalizer :finalize
15
+
16
+ class ServiceListener
17
+ include Concurrent::Async
18
+
19
+ def initialize(service, server)
20
+ super()
21
+
22
+ @service = service
23
+ @server = server
24
+
25
+ @stopped = false
26
+ end
27
+
28
+ def start
29
+ loop do
30
+ break if @stopped
31
+ @service.async.handle_connection @server.accept
32
+ end
33
+ rescue IOError
34
+ logger.error "listening error: #{$!}"
35
+ puts $!
36
+ @stopped = true
37
+ rescue
38
+ logger.error $!
39
+ logger.error $!.backtrace[0,10].join("\n")
40
+ end
41
+
42
+ def stop
43
+ @stopped = true
44
+ end
45
+
46
+ private
47
+
48
+ def logger
49
+ @logger ||= Logger.new "p2p.peermgr"
50
+ end
51
+ end
17
52
 
18
53
  name 'peermanager'
19
54
  required_services []
@@ -52,6 +87,8 @@ module DEVp2p
52
87
 
53
88
  @host = @config[:p2p][:listen_host]
54
89
  @port = @config[:p2p][:listen_port]
90
+
91
+ @stopped = false
55
92
  end
56
93
 
57
94
  def start
@@ -60,8 +97,13 @@ module DEVp2p
60
97
  logger.info "starting tcp listener", host: @host, port: @port
61
98
  @server = TCPServer.new @host, @port
62
99
 
63
- super
64
- after(0.01) { discovery_loop }
100
+ @service_listener = ServiceListener.new self, @server
101
+ @service_listener.async.start
102
+
103
+ @discovery_loop = Thread.new do
104
+ sleep 0.1
105
+ discovery_loop
106
+ end
65
107
  end
66
108
 
67
109
  def stop
@@ -69,19 +111,13 @@ module DEVp2p
69
111
 
70
112
  @server.close if @server
71
113
  @peers.each(&:stop)
114
+ @discovery_loop.kill
72
115
 
73
- super
116
+ @stopped = true
74
117
  end
75
118
 
76
- def _run
77
- loop do
78
- break if stopped?
79
- async.handle_connection @server.accept
80
- end
81
- rescue IOError
82
- logger.error "listening error: #{$!}"
83
- puts $!
84
- @stopped = true
119
+ def stopped?
120
+ @stopped
85
121
  end
86
122
 
87
123
  def add(peer)
@@ -94,7 +130,7 @@ module DEVp2p
94
130
 
95
131
  def exclude(peer)
96
132
  @excluded.push peer.remote_pubkey
97
- peer.stop
133
+ peer.async.stop
98
134
  end
99
135
 
100
136
  def on_hello_received(proto, version, client_version_string, capabilities, listen_port, remote_pubkey)
@@ -134,7 +170,7 @@ module DEVp2p
134
170
  args.push kwargs
135
171
  peer.protocols[protocol].send "send_#{command_name}", *args
136
172
 
137
- peer.wait_to_read
173
+ peer.safe_to_read.wait
138
174
  logger.debug "broadcasting done", ts: Time.now
139
175
  end
140
176
  end
@@ -160,6 +196,11 @@ module DEVp2p
160
196
  logger.debug "connection error #{$!}"
161
197
  @errors.add address, "connection error #{$!}"
162
198
  false
199
+ rescue
200
+ address = "#{host}:#{port}"
201
+ logger.debug $!
202
+ @errors.add address, "connection error #{$!}"
203
+ false
163
204
  end
164
205
 
165
206
  def num_peers
@@ -176,10 +217,20 @@ module DEVp2p
176
217
  @errors.add *args
177
218
  end
178
219
 
220
+ def handle_connection(socket)
221
+ _, port, host = socket.peeraddr
222
+ logger.debug "incoming connection", host: host, port: port
223
+
224
+ start_peer socket
225
+ rescue EOFError
226
+ logger.debug "connection disconnected", host: host, port: port
227
+ socket.close
228
+ end
229
+
179
230
  private
180
231
 
181
232
  def logger
182
- @logger ||= Logger.new "#{@config[:p2p][:listen_port]}.p2p.peermgr"
233
+ @logger ||= Logger.new "p2p.peermgr"
183
234
  end
184
235
 
185
236
  def bootstrap(bootstrap_nodes=[])
@@ -195,33 +246,25 @@ module DEVp2p
195
246
  end
196
247
  end
197
248
 
198
- def handle_connection(socket)
199
- _, port, host = socket.peeraddr
200
- logger.debug "incoming connection", host: host, port: port
201
-
202
- peer = start_peer socket
203
- #Celluloid::Actor.join peer
204
- rescue EOFError
205
- logger.debug "connection disconnected", host: host, port: port
206
- socket.close
207
- end
208
-
209
249
  # FIXME: TODO: timeout is ignored!
210
250
  def create_connection(host, port, timeout)
211
- Celluloid::IO::TCPSocket.new ::TCPSocket.new(host, port)
251
+ ::TCPSocket.new(host, port)
212
252
  end
213
253
 
214
254
  def start_peer(socket, remote_pubkey=nil)
215
- peer = Peer.new Actor.current, socket, remote_pubkey
255
+ peer = Peer.new self, socket, remote_pubkey
216
256
  logger.debug "created new peer", peer: peer, fileno: socket.to_io.fileno
217
257
 
218
258
  add peer
219
- peer.start
259
+ peer.async.start
220
260
 
221
261
  logger.debug "peer started", peer: peer, fileno: socket.to_io.fileno
222
262
  raise PeerError, 'connection closed' if socket.closed?
223
263
 
224
264
  peer
265
+ rescue
266
+ puts $!
267
+ puts $!.backtrace[0,10].join("\n")
225
268
  end
226
269
 
227
270
  def discovery_loop
@@ -274,14 +317,9 @@ module DEVp2p
274
317
 
275
318
  sleep @connect_loop_delay
276
319
  end
277
-
278
- # TODO: ???
279
- cond = Celluloid::Condition.new
280
- cond.wait
281
- end
282
-
283
- def finalize
284
- @server.close if @server && !@server.closed?
320
+ rescue
321
+ puts $!
322
+ puts $!.backtrace[0,10].join("\n")
285
323
  end
286
324
 
287
325
  end