devp2p 0.2.0 → 0.3.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.
@@ -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