class DEVp2p::Protocol

A protocol mediates between the network and the service. It implements a collection of commands.

For each command X the following methods are created at initialization:

On ‘protocol.receive_packet`, the packet is deserialized according to the `command.structure` and the `command.receive` method called with a hash containing the received data.

The default implementation of ‘command.receive` calls callbacks which can be registered in a list which is available as: `protocol.receive_X_callbacks`.

Attributes

peer[RW]

Public Class Methods

new(peer, service) click to toggle source
# File lib/devp2p/protocol.rb, line 35
def initialize(peer, service)
  raise ArgumentError, 'service must be WiredService' unless service.is_a?(WiredService)
  raise ArgumentError, 'peer.send_packet must be callable' unless peer.respond_to?(:send_packet)

  @peer = peer
  @service = service

  @stopped = false

  setup
end

Public Instance Methods

inspect()
Alias for: to_s
receive_packet(packet) click to toggle source
# File lib/devp2p/protocol.rb, line 69
def receive_packet(packet)
  cmd_name = @cmd_by_id[packet.cmd_id]
  cmd = "receive_#{cmd_name}"
  send cmd, packet
rescue ProtocolError => e
  logger.warn "protocol exception, stopping", error: e
  stop
end
send_packet(packet) click to toggle source
# File lib/devp2p/protocol.rb, line 78
def send_packet(packet)
  peer.async.send_packet packet
end
start() click to toggle source
# File lib/devp2p/protocol.rb, line 47
def start
  logger.debug 'starting', proto: self
  service.async.on_wire_protocol_start self
rescue
  puts $!
  puts $!.backtrace[0,10].join("\n")
end
stop() click to toggle source
# File lib/devp2p/protocol.rb, line 55
def stop
  logger.debug 'stopping', proto: self
  service.async.on_wire_protocol_stop self

  @stopped = true
rescue
  puts $!
  puts $!.backtrace[0,10].join("\n")
end
stopped?() click to toggle source
# File lib/devp2p/protocol.rb, line 65
def stopped?
  @stopped
end
to_s() click to toggle source
# File lib/devp2p/protocol.rb, line 82
def to_s
  "<#{name} #{peer}>"
end
Also aliased as: inspect

Private Instance Methods

logger() click to toggle source
# File lib/devp2p/protocol.rb, line 89
def logger
  @logger ||= Logger.new('protocol')
end
setup() click to toggle source
# File lib/devp2p/protocol.rb, line 93
def setup
  klasses = []
  self.class.constants.each do |name|
    c = self.class.const_get name
    klasses.push(c) if c.instance_of?(Class) && c < Command
  end

  raise DuplicatedCommand unless klasses.map(&:cmd_id).uniq.size == klasses.size

  proto = self

  klasses.each do |klass|
    instance = klass.new

    # decode rlp, create hash, call receive
    receive = lambda do |packet|
      raise ArgumentError, "packet is not a Packet: #{packet.inspect}" unless packet.is_a?(Packet)
      instance.receive proto, klass.decode_payload(packet.payload)
    end

    # get data, rlp encode, return packet
    create = lambda do |*args|
      res = instance.create(proto, *args)
      payload = klass.encode_payload res
      Packet.new protocol_id, klass.cmd_id, payload
    end

    # create and send packet
    send_packet = lambda do |*args|
      packet = create.call *args
      send_packet packet
    end

    name = Utils.class_to_cmd_name(klass)
    singleton_class.send(:define_method, "receive_#{name}", &receive)
    singleton_class.send(:define_method, "create_#{name}", &create)
    singleton_class.send(:define_method, "send_#{name}", &send_packet)
    singleton_class.send(:define_method, "receive_#{name}_callbacks") do
      instance.receive_callbacks
    end
  end

  @cmd_by_id = klasses.map {|k| [k.cmd_id, Utils.class_to_cmd_name(k)] }.to_h
end