devp2p 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f6ddcb762858bc073f1b5394c9294dff6a391756
4
- data.tar.gz: 8e11801858eff23d391df93214520a47ea31f8c4
3
+ metadata.gz: 2a9119de12a32ac914f74a103d5544358404a241
4
+ data.tar.gz: 1f7107a8432d2a9b7a74c676623eeeceec717f42
5
5
  SHA512:
6
- metadata.gz: 7a2cb995431e8db5e5a34f8b9368844fcea25e53cb8519931c04678b88f3c420d89b8191cba17c0ad5967c911011427023b11286b7e4d0f00d49104dd3c77644
7
- data.tar.gz: 9b0c03fcaa6a819e20f1a1c516f176c240a75351c5e4dde05087b4dc902d2d39e6a9a36b2072ed1fcf4e8f8ef65debbb0d12768afd75d0996f7d9dda2766626f
6
+ metadata.gz: 869067f4897deb649c694a535eedc8d5cee3a725625c3760a6a069f933cdb08c93d5d16141b9a10cdabba60238c472476e5f98c2a32dea4cddafeadd3d65046f
7
+ data.tar.gz: 8340dc9d8f31803e250ecfc859a9397f9f84e70ad80513fc6874e6a4b825c78d8b777689bb286bbceb8ea934b97b0f037e53933483e8fdbc18ae16f032fa95de
@@ -10,7 +10,7 @@ module DEVp2p
10
10
  bootstrap_node_pubkey = Crypto.privtopub bootstrap_node_privkey
11
11
  enode = Utils.host_port_pubkey_to_uri "0.0.0.0", base_port, bootstrap_node_pubkey
12
12
 
13
- services = [Discovery::Transport, PeerManager]#, service_class]
13
+ services = [Discovery::Service, PeerManager]#, service_class]
14
14
 
15
15
  base_config = {}
16
16
  services.each {|s| Utils.update_config_with_defaults base_config, s.default_config }
@@ -31,7 +31,7 @@ module DEVp2p
31
31
 
32
32
  logger.info "registering service", service: klass.name
33
33
  @container.add type: klass, as: get_actor_name(klass.name), args: args
34
- services[klass.name] = nil
34
+ services[klass.name] = actor(klass.name)
35
35
 
36
36
  klass
37
37
  end
@@ -51,18 +51,14 @@ module DEVp2p
51
51
  end
52
52
 
53
53
  def start
54
- services.keys.each do |n|
55
- next if services[n]
56
-
57
- services[n] = actor(n)
58
- services[n].start
54
+ services.each_value do |service|
55
+ service.start if service.stopped?
59
56
  end
60
57
  end
61
58
 
62
59
  def stop
63
- services.keys.each do |n|
64
- services[n].stop if services[n].alive?
65
- services[n] = nil
60
+ services.each_value do |service|
61
+ service.stop if service.alive?
66
62
  end
67
63
 
68
64
  #@container.shutdown
@@ -55,7 +55,10 @@ module DEVp2p
55
55
  def stop
56
56
  logger.debug 'stopping', proto: Actor.current
57
57
  service.on_wire_protocol_stop Actor.current
58
+
58
59
  super
60
+
61
+ terminate
59
62
  end
60
63
 
61
64
  def _run
@@ -119,7 +122,7 @@ module DEVp2p
119
122
  send_packet packet
120
123
  end
121
124
 
122
- name = klass.name.split('::').last.downcase
125
+ name = Utils.class_to_cmd_name(klass)
123
126
  singleton_class.send(:define_method, "receive_#{name}", &receive)
124
127
  singleton_class.send(:define_method, "create_#{name}", &create)
125
128
  singleton_class.send(:define_method, "send_#{name}", &send_packet)
@@ -128,7 +131,7 @@ module DEVp2p
128
131
  end
129
132
  end
130
133
 
131
- @cmd_by_id = klasses.map {|k| [k.cmd_id, Utils.underscore(k.name)] }.to_h
134
+ @cmd_by_id = klasses.map {|k| [k.cmd_id, Utils.class_to_cmd_name(k)] }.to_h
132
135
  end
133
136
 
134
137
  end
@@ -20,7 +20,7 @@ module DEVp2p
20
20
 
21
21
  case structure
22
22
  when RLP::Sedes::CountableList
23
- RLP.encode data, structure
23
+ RLP.encode data, sedes: structure
24
24
  when Hash
25
25
  raise ArgumentError, 'structure and data length mismatch' unless data.size == structure.size
26
26
  RLP.encode data, sedes: RLP::Sedes::List.new(elements: sedes)
@@ -2,7 +2,7 @@ module DEVp2p
2
2
  module Control
3
3
 
4
4
  def initialize_control
5
- @stopped = false
5
+ @stopped = true
6
6
  @killed = false
7
7
  end
8
8
 
@@ -115,4 +115,4 @@ require 'devp2p/discovery/address'
115
115
  require 'devp2p/discovery/node'
116
116
  require 'devp2p/discovery/kademlia_protocol_adapter'
117
117
  require 'devp2p/discovery/protocol'
118
- require 'devp2p/discovery/transport'
118
+ require 'devp2p/discovery/service'
@@ -28,15 +28,15 @@ module DEVp2p
28
28
 
29
29
  attr :pubkey, :kademlia
30
30
 
31
- def initialize(app, transport)
31
+ def initialize(app, service)
32
32
  @app = app
33
- @transport = transport
33
+ @service = service
34
34
 
35
35
  @privkey = Utils.decode_hex app.config[:node][:privkey_hex]
36
36
  @pubkey = Crypto.privtopub @privkey
37
37
 
38
38
  @nodes = {} # nodeid => Node
39
- @node = Node.new(pubkey, @transport.address)
39
+ @node = Node.new(pubkey, @service.address)
40
40
 
41
41
  @kademlia = KademliaProtocolAdapter.new @node, self
42
42
 
@@ -172,12 +172,14 @@ module DEVp2p
172
172
 
173
173
  get_node(nodeid, address) unless @nodes.has_key?(nodeid)
174
174
  send cmd, nodeid, payload, mdc
175
+ rescue
176
+ logger.error 'invalid message', error: $!
175
177
  end
176
178
 
177
179
  def send_message(node, message)
178
180
  raise ArgumentError, 'node must have address' unless node.address
179
181
  logger.debug ">>> message", address: node.address
180
- @transport.send_message node.address, message
182
+ @service.async.send_message node.address, message
181
183
  end
182
184
 
183
185
  def send_ping(node)
@@ -6,7 +6,7 @@ module DEVp2p
6
6
  ##
7
7
  # Persist the list of known nodes with their reputation.
8
8
  #
9
- class Transport < BaseService
9
+ class Service < BaseService
10
10
  include Celluloid::IO
11
11
 
12
12
  name 'discovery'
@@ -69,9 +69,8 @@ module DEVp2p
69
69
 
70
70
  super
71
71
 
72
- @protocol.bootstrap(
73
- @app.config[:discovery][:bootstrap_nodes].map {|x| Node.from_uri(x) }
74
- )
72
+ nodes = @app.config[:discovery][:bootstrap_nodes] || []
73
+ @protocol.bootstrap( nodes.map {|x| Node.from_uri(x) } )
75
74
  end
76
75
 
77
76
  def stop
@@ -122,16 +122,8 @@ module DEVp2p
122
122
 
123
123
  class <<self
124
124
  # special: we need this packet before the protocol can be initialized
125
- def get_hello_packet(peer)
126
- res = {
127
- version: 55,
128
- client_version_string: peer.config[:client_version_string],
129
- capabilities: peer.capabilities,
130
- listen_port: peer.config[:p2p][:listen_port],
131
- remote_pubkey: peer.config[:node][:id]
132
- }
133
-
134
- payload = Hello.encode_payload(res)
125
+ def get_hello_packet(data)
126
+ payload = Hello.encode_payload(data.merge(version: 55))
135
127
  Packet.new protocol_id, Hello.cmd_id, payload
136
128
  end
137
129
  end
@@ -7,7 +7,7 @@ module DEVp2p
7
7
 
8
8
  DUMB_REMOTE_TIMEOUT = 10.0
9
9
 
10
- attr :config, :protocols, :safe_to_read
10
+ attr :config, :protocols, :remote_client_version, :remote_pubkey
11
11
 
12
12
  def initialize(peermanager, socket, remote_pubkey=nil)
13
13
  @peermanager = peermanager
@@ -19,11 +19,12 @@ module DEVp2p
19
19
  @stopped = true
20
20
  @hello_received = false
21
21
 
22
+ _, @port, _, @ip = @socket.peeraddr
22
23
  @remote_client_version = ''
23
24
  logger.debug "peer init", peer: Actor.current
24
25
 
25
26
  privkey = Utils.decode_hex @config[:node][:privkey_hex]
26
- hello_packet = P2PProtocol.get_hello_packet Actor.current
27
+ hello_packet = P2PProtocol.get_hello_packet hello_data
27
28
 
28
29
  @mux = MultiplexedSession.new privkey, hello_packet, remote_pubkey
29
30
  @remote_pubkey = remote_pubkey
@@ -35,12 +36,14 @@ module DEVp2p
35
36
  @safe_to_read = true
36
37
  @safe_to_read_cond = Celluloid::Condition.new
37
38
 
38
- _, @port, _, @ip = @socket.peeraddr
39
-
40
39
  # stop peer if hello not received in DUMB_REMOTE_TIMEOUT
41
40
  after(DUMB_REMOTE_TIMEOUT) { check_if_dumb_remote }
42
41
  end
43
42
 
43
+ def wait_to_read
44
+ @safe_to_read_cond.wait unless @safe_to_read
45
+ end
46
+
44
47
  ##
45
48
  # if peer is responder, then the remote_pubkey will not be available before
46
49
  # the first packet is received
@@ -188,7 +191,7 @@ module DEVp2p
188
191
  async.run_egress_message
189
192
 
190
193
  while !stopped?
191
- @safe_to_read_cond.wait unless safe_to_read
194
+ wait_to_read
192
195
 
193
196
  begin
194
197
  @socket.wait_readable
@@ -236,6 +239,10 @@ module DEVp2p
236
239
  logger.debug "multiplexer error", peer: Actor.current, error: e
237
240
  report_error "multiplexer error"
238
241
  stop
242
+ rescue
243
+ logger.debug "ingress message error", peer: Actor.current, error: $!
244
+ report_error "ingress message error"
245
+ stop
239
246
  end
240
247
  alias run run_ingress_message
241
248
 
@@ -247,10 +254,11 @@ module DEVp2p
247
254
  def stop
248
255
  if !stopped?
249
256
  @stopped = true
250
- logger.debug "stopped", peer: Actor.current
251
257
 
252
258
  @protocols.each_value {|proto| proto.stop }
253
259
  @peermanager.delete Actor.current
260
+
261
+ logger.info "peer stopped", peer: Actor.current
254
262
  terminate
255
263
  end
256
264
  end
@@ -265,6 +273,14 @@ module DEVp2p
265
273
  @logger ||= Logger.new "#{@config[:p2p][:listen_port]}.p2p.peer"
266
274
  end
267
275
 
276
+ def hello_data
277
+ { client_version_string: config[:client_version_string],
278
+ capabilities: capabilities,
279
+ listen_port: config[:p2p][:listen_port],
280
+ remote_pubkey: config[:node][:id]
281
+ }
282
+ end
283
+
268
284
  def handle_packet(packet)
269
285
  raise ArgumentError, 'packet must be Packet' unless packet.is_a?(Packet)
270
286
 
@@ -273,7 +289,7 @@ module DEVp2p
273
289
 
274
290
  packet.cmd_id = cmd_id # rewrite
275
291
  protocol.receive_packet packet
276
- rescue UnknownCommandError
292
+ rescue UnknownCommandError => e
277
293
  logger.error 'received unknown cmd', error: e, packet: packet
278
294
  end
279
295
 
@@ -285,8 +301,8 @@ module DEVp2p
285
301
  @protocols.each_value do |protocol|
286
302
  if packet.cmd_id < max_id + protocol.max_cmd_id + 1
287
303
  return protocol, packet.cmd_id - (max_id == 0 ? 0 : max_id + 1)
288
- max_id += protocol.max_cmd_id
289
304
  end
305
+ max_id += protocol.max_cmd_id
290
306
  end
291
307
  raise UnknownCommandError, "no protocol for id #{packet.cmd_id}"
292
308
  end
@@ -13,7 +13,7 @@ module DEVp2p
13
13
  #
14
14
  class PeerManager < WiredService
15
15
  include Celluloid::IO
16
- finalizer :cleanup
16
+ finalizer :finalize
17
17
 
18
18
  name 'peermanager'
19
19
  required_services []
@@ -36,6 +36,7 @@ module DEVp2p
36
36
  logger.info "PeerManager init"
37
37
 
38
38
  @peers = []
39
+ @excluded = []
39
40
  @errors = @config[:log_disconnects] ? PeerErrors.new : PeerErrorsBase.new
40
41
 
41
42
  @wire_protocol = P2PProtocol
@@ -46,7 +47,7 @@ module DEVp2p
46
47
  end
47
48
 
48
49
  @connect_timeout = 2.0
49
- @connect_loop_delay = 0.1
50
+ @connect_loop_delay = 0.5
50
51
  @discovery_delay = 0.5
51
52
 
52
53
  @host = @config[:p2p][:listen_host]
@@ -85,13 +86,17 @@ module DEVp2p
85
86
 
86
87
  def add(peer)
87
88
  @peers.push peer
88
- #link peer
89
89
  end
90
90
 
91
91
  def delete(peer)
92
92
  @peers.delete peer
93
93
  end
94
94
 
95
+ def exclude(peer)
96
+ @excluded.push peer.remote_pubkey
97
+ peer.stop
98
+ end
99
+
95
100
  def on_hello_received(proto, version, client_version_string, capabilities, listen_port, remote_pubkey)
96
101
  logger.debug 'hello_received', listen_port: listen_port, peer: proto.peer, num_peers: @peers.size
97
102
 
@@ -114,7 +119,7 @@ module DEVp2p
114
119
  end
115
120
 
116
121
  def broadcast(protocol, command_name, args=[], kwargs={}, num_peers=nil, exclude_peers=[])
117
- logger.debug "broadcasting", protocol: protocol, command: command_name, num_peers: num_peers, exclude_peers: exclude_peers
122
+ logger.debug "broadcasting", protocol: protocol, command: command_name, num_peers: num_peers, exclude_peers: exclude_peers.map(&:to_s)
118
123
  raise ArgumentError, 'invalid num_peers' unless num_peers.nil? || num_peers > 0
119
124
 
120
125
  peers_with_proto = @peers.select {|p| p.protocols.include?(protocol) && !exclude_peers.include?(p) }
@@ -129,7 +134,7 @@ module DEVp2p
129
134
  args.push kwargs
130
135
  peer.protocols[protocol].send "send_#{command_name}", *args
131
136
 
132
- peer.safe_to_read.wait
137
+ peer.wait_to_read
133
138
  logger.debug "broadcasting done", ts: Time.now
134
139
  end
135
140
  end
@@ -248,12 +253,22 @@ module DEVp2p
248
253
  end
249
254
 
250
255
  node = neighbours.sample
251
- logger.debug 'connecting random neighbour', node: node
252
256
 
253
257
  local_pubkey = Crypto.privtopub Utils.decode_hex(@config[:node][:privkey_hex])
254
- next if node.pubkey == local_pubkey
255
- next if @peers.any? {|p| p.remote_pubkey == node.pubkey }
258
+ if node.pubkey == local_pubkey
259
+ logger.debug 'connecting random neighbour', node: node, skipped: true, reason: 'myself'
260
+ next
261
+ end
262
+ if @peers.any? {|p| node.pubkey == p.remote_pubkey }
263
+ logger.debug 'connecting random neighbour', node: node, skipped: true, reason: 'already connected'
264
+ next
265
+ end
266
+ if @excluded.any? {|pubkey| node.pubkey == pubkey }
267
+ logger.debug 'connecting random neighbour', node: node, skipped: true, reason: 'excluded peer'
268
+ next
269
+ end
256
270
 
271
+ logger.debug 'connecting random neighbour', node: node, skipped: false
257
272
  connect node.address.ip, node.address.tcp_port, node.pubkey
258
273
  end
259
274
 
@@ -265,7 +280,7 @@ module DEVp2p
265
280
  cond.wait
266
281
  end
267
282
 
268
- def cleanup
283
+ def finalize
269
284
  @server.close if @server && !@server.closed?
270
285
  end
271
286
 
@@ -5,33 +5,56 @@
5
5
  #
6
6
  class SyncQueue
7
7
 
8
- def initialize
8
+ attr :queue, :max_size
9
+
10
+ def initialize(max_size=nil)
9
11
  @queue = []
10
12
  @num_waiting = 0
11
- @cond = Celluloid::Condition.new
13
+
14
+ @max_size = max_size
15
+ @cond_full = Celluloid::Condition.new
16
+ @cond_empty = Celluloid::Condition.new
12
17
  end
13
18
 
14
- def enq(obj)
15
- @queue.push obj
16
- @cond.signal
19
+ def enq(obj, non_block=false)
20
+ loop do
21
+ if full?
22
+ if non_block
23
+ raise ThreadError, 'queue full'
24
+ else
25
+ begin
26
+ @num_waiting += 1
27
+ @cond_full.wait
28
+ ensure
29
+ @num_waiting -= 1
30
+ end
31
+ end
32
+ else
33
+ @queue.push obj
34
+ @cond_empty.signal
35
+ return obj
36
+ end
37
+ end
17
38
  end
18
39
  alias << enq
19
40
 
20
41
  def deq(non_block=false)
21
42
  loop do
22
- if @queue.empty?
43
+ if empty?
23
44
  if non_block
24
45
  raise ThreadError, 'queue empty'
25
46
  else
26
47
  begin
27
48
  @num_waiting += 1
28
- @cond.wait
49
+ @cond_empty.wait
29
50
  ensure
30
51
  @num_waiting -= 1
31
52
  end
32
53
  end
33
54
  else
34
- return @queue.shift
55
+ obj = @queue.shift
56
+ @cond_full.signal
57
+ return obj
35
58
  end
36
59
  end
37
60
  end
@@ -39,13 +62,13 @@ class SyncQueue
39
62
  # Same as pop except it will not remove the element from queue, just peek.
40
63
  def peek(non_block=false)
41
64
  loop do
42
- if @queue.empty?
65
+ if empty?
43
66
  if non_block
44
67
  raise ThreadError, 'queue empty'
45
68
  else
46
69
  begin
47
70
  @num_waiting += 1
48
- @cond.wait
71
+ @cond_empty.wait
49
72
  ensure
50
73
  @num_waiting -= 1
51
74
  end
@@ -56,6 +79,10 @@ class SyncQueue
56
79
  end
57
80
  end
58
81
 
82
+ def full?
83
+ @max_size && @queue.size >= @max_size
84
+ end
85
+
59
86
  def empty?
60
87
  @queue.empty?
61
88
  end
@@ -102,5 +102,9 @@ module DEVp2p
102
102
  word
103
103
  end
104
104
 
105
+ def class_to_cmd_name(klass)
106
+ klass.name.split(DOUBLE_COLON).last.downcase
107
+ end
108
+
105
109
  end
106
110
  end
@@ -1,6 +1,6 @@
1
1
  # -*- encoding : ascii-8bit -*-
2
2
  module DEVp2p
3
- VERSION = '0.1.1'
3
+ VERSION = '0.2.0'
4
4
 
5
5
  VersionString = begin
6
6
  git_describe_re = /^(?<version>v\d+\.\d+\.\d+)-(?<git>\d+-g[a-fA-F0-9]+(?:-dirty)?)$/
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devp2p
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Xie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-08 00:00:00.000000000 Z
11
+ date: 2016-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie
@@ -84,16 +84,16 @@ dependencies:
84
84
  name: bitcoin-secp256k1
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '='
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.3.1
89
+ version: '0.4'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '='
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.3.1
96
+ version: '0.4'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rlp
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -178,7 +178,7 @@ files:
178
178
  - lib/devp2p/discovery/kademlia_protocol_adapter.rb
179
179
  - lib/devp2p/discovery/node.rb
180
180
  - lib/devp2p/discovery/protocol.rb
181
- - lib/devp2p/discovery/transport.rb
181
+ - lib/devp2p/discovery/service.rb
182
182
  - lib/devp2p/exception.rb
183
183
  - lib/devp2p/frame.rb
184
184
  - lib/devp2p/kademlia.rb