devp2p 0.1.1 → 0.2.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: 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