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.
- checksums.yaml +4 -4
- data/lib/devp2p.rb +14 -29
- data/lib/devp2p/app.rb +70 -0
- data/lib/devp2p/command.rb +1 -1
- data/lib/devp2p/connection_monitor.rb +27 -21
- data/lib/devp2p/discovery.rb +1 -1
- data/lib/devp2p/discovery/protocol.rb +2 -2
- data/lib/devp2p/discovery/service.rb +108 -49
- data/lib/devp2p/p2p_protocol.rb +9 -9
- data/lib/devp2p/peer.rb +53 -59
- data/lib/devp2p/peer_manager.rb +77 -39
- data/lib/devp2p/{base_protocol.rb → protocol.rb} +19 -17
- data/lib/devp2p/service.rb +50 -0
- data/lib/devp2p/sync_queue.rb +58 -45
- data/lib/devp2p/version.rb +2 -1
- data/lib/devp2p/wired_service.rb +3 -3
- metadata +19 -35
- data/lib/devp2p/app_helper.rb +0 -85
- data/lib/devp2p/base_app.rb +0 -88
- data/lib/devp2p/base_service.rb +0 -55
- data/lib/devp2p/control.rb +0 -32
data/lib/devp2p/p2p_protocol.rb
CHANGED
@@ -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 <
|
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 "
|
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
|
-
|
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 "
|
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 "
|
157
|
+
@logger = Logger.new "p2p.protocol"
|
158
158
|
end
|
159
159
|
|
160
160
|
end
|
data/lib/devp2p/peer.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module DEVp2p
|
4
4
|
|
5
5
|
class Peer
|
6
|
-
include
|
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 =
|
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:
|
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
|
-
|
36
|
-
@safe_to_read
|
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
|
-
|
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
|
44
|
-
@
|
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
|
-
"
|
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
|
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:
|
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:
|
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
|
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
|
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
|
208
|
+
def run
|
187
209
|
logger.debug "peer starting main loop"
|
188
210
|
raise PeerError, 'connection is closed' if @socket.closed?
|
189
211
|
|
190
|
-
|
191
|
-
|
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
|
-
|
216
|
+
@safe_to_read.wait
|
195
217
|
|
196
218
|
begin
|
197
|
-
@socket.
|
198
|
-
|
199
|
-
|
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:
|
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:
|
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:
|
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:
|
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 "
|
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)
|
data/lib/devp2p/peer_manager.rb
CHANGED
@@ -12,8 +12,43 @@ module DEVp2p
|
|
12
12
|
# connect closest node
|
13
13
|
#
|
14
14
|
class PeerManager < WiredService
|
15
|
-
|
16
|
-
|
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
|
-
|
64
|
-
|
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
|
-
|
116
|
+
@stopped = true
|
74
117
|
end
|
75
118
|
|
76
|
-
def
|
77
|
-
|
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.
|
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 "
|
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
|
-
|
251
|
+
::TCPSocket.new(host, port)
|
212
252
|
end
|
213
253
|
|
214
254
|
def start_peer(socket, remote_pubkey=nil)
|
215
|
-
peer = Peer.new
|
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
|
-
|
279
|
-
|
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
|