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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2a9119de12a32ac914f74a103d5544358404a241
4
- data.tar.gz: 1f7107a8432d2a9b7a74c676623eeeceec717f42
3
+ metadata.gz: 7da6641d9e8596c7a5ef84501b60dba802a38c7d
4
+ data.tar.gz: eb5ae074cca21812736d03f67949648242482b87
5
5
  SHA512:
6
- metadata.gz: 869067f4897deb649c694a535eedc8d5cee3a725625c3760a6a069f933cdb08c93d5d16141b9a10cdabba60238c472476e5f98c2a32dea4cddafeadd3d65046f
7
- data.tar.gz: 8340dc9d8f31803e250ecfc859a9397f9f84e70ad80513fc6874e6a4b825c78d8b777689bb286bbceb8ea934b97b0f037e53933483e8fdbc18ae16f032fa95de
6
+ metadata.gz: 2580541428dc68edea68859e888e235c940637fe2219759d35a58ba3256d96d7de364a343c74935bf714b7c4f54c8be3fc1291e0a6d099bf32df38973c0678c2
7
+ data.tar.gz: 95f168e5a41dc87646a55dbc794a0cb64984fc8e10aef647f7b94fa09820f1cc6645bdb6e1b6fa625925d618df1b436a99aabc8a8d07611ca1f28f32a739a634
@@ -1,16 +1,9 @@
1
1
  # -*- encoding : ascii-8bit -*-
2
2
 
3
+ require 'concurrent'
3
4
  require 'block_logger'
4
- require 'celluloid/io'
5
-
6
5
  require 'rlp'
7
6
 
8
- #Celluloid.exception_handler do |ex|
9
- # puts "!!!!! Exception raised in celluloid actor:"
10
- # puts ex
11
- # puts ex.backtrace[0,20].join("\n")
12
- #end
13
-
14
7
  module DEVp2p
15
8
  Logger = BlockLogger
16
9
 
@@ -22,36 +15,28 @@ end
22
15
 
23
16
  require 'devp2p/version'
24
17
 
25
- require 'devp2p/configurable'
26
- require 'devp2p/utils'
27
18
  require 'devp2p/exception'
19
+ require 'devp2p/crypto'
20
+ require 'devp2p/utils'
21
+ require 'devp2p/configurable'
28
22
 
29
- require 'devp2p/control'
30
- require 'devp2p/base_service'
23
+ require 'devp2p/app'
24
+ require 'devp2p/service'
31
25
  require 'devp2p/wired_service'
32
26
 
33
- require 'devp2p/kademlia'
34
- require 'devp2p/discovery'
35
-
36
27
  require 'devp2p/sync_queue'
37
-
38
- require 'devp2p/packet'
39
28
  require 'devp2p/frame'
29
+ require 'devp2p/packet'
40
30
  require 'devp2p/multiplexer'
41
-
42
- require 'devp2p/crypto'
43
- require 'devp2p/rlpx_session'
44
31
  require 'devp2p/multiplexed_session'
32
+ require 'devp2p/rlpx_session'
45
33
 
46
- require 'devp2p/command'
47
- require 'devp2p/base_protocol'
34
+ require 'devp2p/kademlia'
35
+ require 'devp2p/discovery'
48
36
  require 'devp2p/connection_monitor'
37
+ require 'devp2p/command'
38
+ require 'devp2p/protocol'
49
39
  require 'devp2p/p2p_protocol'
50
-
51
- require 'devp2p/peer'
52
- require 'devp2p/peer_errors'
53
40
  require 'devp2p/peer_manager'
54
-
55
- require 'devp2p/base_app'
56
- require 'devp2p/app_helper'
57
-
41
+ require 'devp2p/peer_errors'
42
+ require 'devp2p/peer'
@@ -0,0 +1,70 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+ require 'hashie'
3
+
4
+ module DEVp2p
5
+ class App
6
+ include Concurrent::Async
7
+
8
+ extend Configurable
9
+ add_config(
10
+ default_config: {
11
+ client_version_string: "ruby-devp2p #{VersionString}",
12
+ deactivated_services: []
13
+ }
14
+ )
15
+
16
+ attr :config, :services
17
+
18
+ def initialize(config=default_config)
19
+ super()
20
+
21
+ @config = Hashie::Mash.new(default_config).merge(config)
22
+ @registry = {}
23
+ @services = Hashie::Mash.new
24
+ end
25
+
26
+ def register_service(klass, *args)
27
+ raise ArgumentError, "service #{klass.name} already registered" if services.has_key?(klass.name)
28
+
29
+ logger.info "registering service", service: klass.name
30
+ @registry[klass.name] = [klass, args]
31
+ services[klass.name] = klass.new(*args)
32
+ end
33
+
34
+ def deregister_service(klass)
35
+ raise ArgumentError, "service #{klass.name} not registered" unless services.has_key?(klass.name)
36
+
37
+ logger.info "deregistering service", service: klass.name
38
+ services[klass.name].async.stop
39
+ services.delete klass.name
40
+ @registry.delete klass.name
41
+ end
42
+
43
+ def start
44
+ @registry.each do |name, (klass, args)|
45
+ services[name] ||= klass.new(*args)
46
+ services[name].async.start
47
+ end
48
+ rescue
49
+ puts $!
50
+ puts $!.backtrace[0,10].join("\n")
51
+ end
52
+
53
+ def stop
54
+ services.keys.each do |name|
55
+ services[name].async.stop
56
+ services.delete name
57
+ end
58
+ rescue
59
+ puts $!
60
+ puts $!.backtrace[0,10].join("\n")
61
+ end
62
+
63
+ private
64
+
65
+ def logger
66
+ @logger ||= Logger.new 'app'
67
+ end
68
+
69
+ end
70
+ end
@@ -64,7 +64,7 @@ module DEVp2p
64
64
  # optionally implement create
65
65
  def create(proto, *args)
66
66
  options = args.last.is_a?(Hash) ? args.pop : {}
67
- raise ArgumentError, "proto #{proto} must be protocol" unless proto.is_a?(BaseProtocol)
67
+ raise ArgumentError, "proto #{proto} must be protocol" unless proto.is_a?(Protocol)
68
68
  raise ArgumentError, "command structure mismatch" if !options.empty? && structure.instance_of?(RLP::Sedes::CountableList)
69
69
  options.empty? ? args : options
70
70
  end
@@ -6,7 +6,7 @@ module DEVp2p
6
6
  # monitors the connection by sending pings and checking pongs
7
7
  #
8
8
  class ConnectionMonitor
9
- include Celluloid
9
+ include Concurrent::Async
10
10
 
11
11
  def initialize(proto)
12
12
  @proto = proto
@@ -28,8 +28,10 @@ module DEVp2p
28
28
  }
29
29
  @proto.receive_pong_callbacks.push(track_response)
30
30
 
31
- monitor = Actor.current
32
- @proto.receive_hello_callbacks.push(->(p, **kwargs) { monitor.start })
31
+ monitor = self
32
+ # FIXME: sleep 1 to make sure ConnectionMonitor start after connection of
33
+ # other protocols like ETHProtocol
34
+ @proto.receive_hello_callbacks.push(->(p, **kwargs) { sleep 1; monitor.start })
33
35
  end
34
36
 
35
37
  def latency(num_samples=@max_samples)
@@ -38,38 +40,42 @@ module DEVp2p
38
40
  (0...num_samples).map {|i| @samples[i] }.reduce(0, &:+)
39
41
  end
40
42
 
41
- def run
42
- logger.debug 'started', monitor: Actor.current
43
- loop do
44
- logger.debug 'pinging', monitor: Actor.current
45
- @proto.send_ping
43
+ def start
44
+ logger.debug 'started', monitor: self
46
45
 
47
- now = @last_request = Time.now
48
- sleep @ping_interval
46
+ logger.debug 'pinging', monitor: self
47
+ @proto.async.send_ping
48
+ now = @last_request = Time.now
49
+
50
+ @task = Concurrent::TimerTask.new(execution_interval: @ping_interval) do
49
51
  logger.debug('latency', peer: @proto, latency: ("%.3f" % latency))
50
52
 
51
53
  if now - @last_response > @response_delay_threshold
52
- logger.debug "unresponsive_peer", monitor: Actor.current
53
- @proto.peer.report_error 'not responding to ping'
54
- @proto.stop
55
- terminate
54
+ logger.debug "unresponsive_peer", monitor: self
55
+ @proto.peer.async.report_error 'not responding to ping'
56
+ @proto.async.stop
56
57
  end
57
- end
58
- end
59
58
 
60
- def start
61
- async.run
59
+ logger.debug 'pinging', monitor: self
60
+ @proto.async.send_ping
61
+ now = @last_request = Time.now
62
+ end
63
+ @task.execute
64
+ rescue
65
+ puts $!
66
+ puts $!.backtrace[0,10].join("\n")
62
67
  end
63
68
 
64
69
  def stop
65
- logger.debug 'stopped', monitor: Actor.current
66
- terminate
70
+ logger.debug 'stopped', monitor: self
71
+ @task.shutdown
72
+ @task = nil
67
73
  end
68
74
 
69
75
  private
70
76
 
71
77
  def logger
72
- @logger ||= Logger.new("#{@proto.peer.config[:p2p][:listen_port]}.p2p.ctxmonitor.#{object_id}")
78
+ @logger ||= Logger.new("p2p.ctxmonitor")
73
79
  end
74
80
 
75
81
  end
@@ -1,3 +1,4 @@
1
+ # -*- encoding : ascii-8bit -*-
1
2
  ##
2
3
  # # Node Discovery Protocol
3
4
  #
@@ -110,7 +111,6 @@
110
111
  # }
111
112
  #
112
113
 
113
-
114
114
  require 'devp2p/discovery/address'
115
115
  require 'devp2p/discovery/node'
116
116
  require 'devp2p/discovery/kademlia_protocol_adapter'
@@ -173,7 +173,7 @@ module DEVp2p
173
173
  get_node(nodeid, address) unless @nodes.has_key?(nodeid)
174
174
  send cmd, nodeid, payload, mdc
175
175
  rescue
176
- logger.error 'invalid message', error: $!
176
+ logger.error 'invalid message', error: $!, from: address
177
177
  end
178
178
 
179
179
  def send_message(node, message)
@@ -319,7 +319,7 @@ module DEVp2p
319
319
  private
320
320
 
321
321
  def logger
322
- @logger ||= Logger.new("#{udp_port}.p2p.discovery").tap {|l| l.level = :info }
322
+ @logger ||= Logger.new("p2p.discovery").tap {|l| l.level = :info }
323
323
  end
324
324
 
325
325
  def encode_cmd_id(cmd_id)
@@ -3,12 +3,82 @@
3
3
  module DEVp2p
4
4
  module Discovery
5
5
 
6
- ##
7
- # Persist the list of known nodes with their reputation.
8
- #
9
- class Service < BaseService
10
- include Celluloid::IO
6
+ class Receiver
7
+ include Concurrent::Async
11
8
 
9
+ def initialize(service, socket)
10
+ super()
11
+
12
+ @service = service
13
+ @socket = socket
14
+
15
+ @stopped = false
16
+ end
17
+
18
+ def start
19
+ maxlen = Multiplexer.max_window_size * 2
20
+
21
+ loop do
22
+ break if @stopped || @socket.closed?
23
+
24
+ message, info = @socket.recvfrom maxlen
25
+ handle_packet message, info[3], info[1]
26
+ end
27
+ rescue
28
+ puts $!
29
+ puts $!.backtrace[0,10].join("\n")
30
+ end
31
+
32
+ def stop
33
+ @stopped = true
34
+ end
35
+
36
+ def handle_packet(message, ip, port)
37
+ logger.debug "handling packet", ip: ip, port: port, size: message.size
38
+ @service.async.receive_message Address.new(ip, port), message
39
+ end
40
+
41
+ private
42
+
43
+ def logger
44
+ @logger ||= Logger.new "p2p.discovery"
45
+ end
46
+ end
47
+
48
+ class Sender
49
+ include Concurrent::Async
50
+
51
+ def initialize(service, socket)
52
+ super()
53
+
54
+ @service = service
55
+ @socket = socket
56
+
57
+ @stopped = false
58
+ end
59
+
60
+ def start
61
+ # do nothing
62
+ end
63
+
64
+ def send_message(address, message)
65
+ raise ArgumentError, 'address must be Address' unless address.instance_of?(Address)
66
+ logger.debug "sending", size: message.size, to: address
67
+
68
+ @socket.send message, 0, address.ip, address.udp_port
69
+ rescue
70
+ puts $!
71
+ puts $!.backtrace[0,10].join("\n")
72
+ end
73
+
74
+ private
75
+
76
+ def logger
77
+ @logger ||= Logger.new "p2p.discovery"
78
+ end
79
+ end
80
+
81
+ class Service < ::DEVp2p::Service
12
82
  name 'discovery'
13
83
 
14
84
  default_config(
@@ -27,33 +97,8 @@ module DEVp2p
27
97
  super(app)
28
98
  logger.info "Discovery service init"
29
99
 
30
- @server = nil # will be UDPSocket
31
- @protocol = Protocol.new app, Actor.current
32
- end
33
-
34
- def address
35
- ip = @app.config[:discovery][:listen_host]
36
- port = @app.config[:discovery][:listen_port]
37
- Address.new ip, port
38
- end
39
-
40
- def send_message(address, message)
41
- raise ArgumentError, 'address must be Address' unless address.instance_of?(Address)
42
- logger.debug "sending", size: message.size, to: address
43
-
44
- begin
45
- @server.send message, 0, address.ip, address.udp_port
46
- rescue
47
- # should never reach here? udp has no connection!
48
- logger.error "udp write error", error: $!
49
- logger.error "waiting for recovery"
50
- sleep 5
51
- end
52
- end
53
-
54
- def receive_message(address, message)
55
- raise ArgumentError, 'address must be Address' unless address.instance_of?(Address)
56
- @protocol.receive_message address, message
100
+ @socket = nil
101
+ @protocol = Protocol.new app, self
57
102
  end
58
103
 
59
104
  def start
@@ -64,38 +109,52 @@ module DEVp2p
64
109
 
65
110
  logger.info "starting udp listener", port: port, host: ip
66
111
 
67
- @server = UDPSocket.new
68
- @server.bind ip, port
112
+ @socket = UDPSocket.new
113
+ @socket.bind ip, port
69
114
 
70
- super
115
+ @receiver = Receiver.new self, @socket
116
+ @sender = Sender.new self, @socket
117
+ @receiver.async.start
118
+ @sender.async.start
71
119
 
72
120
  nodes = @app.config[:discovery][:bootstrap_nodes] || []
73
121
  @protocol.bootstrap( nodes.map {|x| Node.from_uri(x) } )
122
+ rescue
123
+ puts $!
124
+ puts $!.backtrace[0,10].join("\n")
74
125
  end
75
126
 
76
127
  def stop
77
128
  logger.info "stopping discovery"
78
- super
129
+
130
+ @socket.close if @socket
131
+ @sender.async.stop if @sender
132
+ @receiver.async.stop if @receiver
133
+
134
+ @socket = nil
135
+ @sender = nil
136
+ @receiver = nil
79
137
  end
80
138
 
81
- private
139
+ def address
140
+ ip = @app.config[:discovery][:listen_host]
141
+ port = @app.config[:discovery][:listen_port]
142
+ Address.new ip, port
143
+ end
82
144
 
83
- def logger
84
- @logger ||= Logger.new "#{@app.config[:discovery][:listen_port]}.p2p.discovery"
145
+ def receive_message(address, message)
146
+ raise ArgumentError, 'address must be Address' unless address.instance_of?(Address)
147
+ @protocol.receive_message address, message
85
148
  end
86
149
 
87
- def _run
88
- maxlen = Multiplexer.max_window_size * 2
89
- loop do
90
- break if stopped?
91
- message, info = @server.recvfrom maxlen
92
- async.handle_packet message, info[3], info[1]
93
- end
150
+ def send_message(address, message)
151
+ @sender.async.send_message address, message
94
152
  end
95
153
 
96
- def handle_packet(message, ip, port)
97
- logger.debug "handling packet", ip: ip, port: port, size: message.size
98
- receive_message Address.new(ip, port), message
154
+ private
155
+
156
+ def logger
157
+ @logger ||= Logger.new "p2p.discovery"
99
158
  end
100
159
 
101
160
  end