devp2p 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|