class Ciri::P2P::Discovery::Protocol::Message

implement the DiscV4 protocol github.com/ethereum/devp2p/blob/master/discv4.md

Constants

MAX_LEN

Attributes

message_hash[R]
packet_data[R]
packet_type[R]

Public Class Methods

decode_message(raw_bytes) click to toggle source

return a Message

# File lib/ciri/p2p/discovery/protocol.rb, line 102
def decode_message(raw_bytes)
  hash = raw_bytes[0...32]
  # signature is 65 length r,s,v
  signature = raw_bytes[32...97]
  packet_type = Utils.big_endian_decode raw_bytes[97]
  packet_data = raw_bytes[98..-1]
  Message.new(message_hash: hash, signature: signature, packet_type: packet_type, packet_data: packet_data)
end
new(message_hash:, signature:, packet_type:, packet_data:) click to toggle source
# File lib/ciri/p2p/discovery/protocol.rb, line 45
def initialize(message_hash:, signature:, packet_type:, packet_data:)
  @message_hash = message_hash
  @signature = signature
  @packet_type = packet_type
  @packet_data = packet_data
end
pack(packet, private_key:) click to toggle source

return a new message instance include packet

# File lib/ciri/p2p/discovery/protocol.rb, line 112
def pack(packet, private_key:)
  packet_data = Ciri::RLP.encode(packet)
  packet_type = packet.class.code
  encoded_packet_type = Utils.big_endian_encode(packet_type)
  signature = private_key.ecdsa_signature(Utils.keccak(encoded_packet_type + packet_data)).to_s
  hash = Utils.keccak(signature + encoded_packet_type + packet_data)
  if (msg_size=hash.size + signature.size + encoded_packet_type.size + packet_data.size) > MAX_LEN
    raise InvalidMessageError.new("failed to pack, message size is too long, size: #{msg_size}, max_len: #{MAX_LEN}")
  end
  Message.new(message_hash: hash, signature: signature, packet_type: packet_type, packet_data: packet_data)
end

Public Instance Methods

encode_message() click to toggle source

encode message to string

# File lib/ciri/p2p/discovery/protocol.rb, line 91
def encode_message
  buf = String.new
  buf << message_hash
  buf << @signature
  buf << packet_type
  buf << packet_data
  buf
end
packet() click to toggle source
# File lib/ciri/p2p/discovery/protocol.rb, line 61
def packet
  packet_class = case @packet_type
                 when Ping::CODE
                   Ping
                 when Pong::CODE
                   Pong
                 when FindNode::CODE
                   FindNode
                 when Neighbors::CODE
                   Neighbors
                 else
                   raise UnknownMessageCodeError.new("unkonwn discovery message code: #{@packet_type}")
                 end
  # TODO according discv4 protocol, rlp_decode should support ignore additional elements
  # we should support ignore_extra_data option in Ciri::RLP
  packet_class.rlp_decode @packet_data
end
sender() click to toggle source

compute key and return NodeID

# File lib/ciri/p2p/discovery/protocol.rb, line 53
def sender
  @sender ||= begin
                encoded_packet_type = Utils.big_endian_encode(packet_type)
                public_key = Key.ecdsa_recover(Utils.keccak(encoded_packet_type + packet_data), @signature)
                NodeID.new(public_key)
              end
end
validate() click to toggle source

validate message hash and signature

# File lib/ciri/p2p/discovery/protocol.rb, line 80
def validate
  encoded_packet_type = Utils.big_endian_encode(packet_type)
  raise InvalidMessageError.new("mismatch hash") if message_hash != Utils.keccak(@signature + encoded_packet_type + packet_data)
  begin
    sender
  rescue StandardError => e
    raise InvalidMessageError.new("recover sender error: #{e}")
  end
end