class Ciri::P2P::RLPX::Connection
RLPX::Connection
implement RLPX
protocol operations all operations end with bang(!)
Public Class Methods
new(io)
click to toggle source
# File lib/ciri/p2p/rlpx/connection.rb, line 55 def initialize(io) set_timeout(io) @io = io @frame_io = nil end
Public Instance Methods
encryption_handshake!(private_key:, remote_node_id: nil)
click to toggle source
Encryption handshake, exchange keys with node, must been invoked before other operations
# File lib/ciri/p2p/rlpx/connection.rb, line 62 def encryption_handshake!(private_key:, remote_node_id: nil) enc_handshake = EncryptionHandshake.new(private_key: private_key, remote_id: remote_node_id) secrets = remote_node_id.nil? ? receiver_enc_handshake(enc_handshake) : initiator_enc_handshake(enc_handshake) @frame_io = FrameIO.new(@io, secrets) end
protocol_handshake!(our_hs)
click to toggle source
protocol handshake
# File lib/ciri/p2p/rlpx/connection.rb, line 69 def protocol_handshake!(our_hs) @frame_io.send_data(Code::HANDSHAKE, our_hs.rlp_encode) remote_hs = read_protocol_handshake # enable snappy compress if remote peer support @frame_io.snappy = remote_hs.version >= SNAPPY_PROTOCOL_VERSION remote_hs end
Private Instance Methods
encoded_prefix(n)
click to toggle source
encode 16 uint prefix
# File lib/ciri/p2p/rlpx/connection.rb, line 173 def encoded_prefix(n) prefix = Utils.big_endian_encode(n) # pad to 2 bytes prefix.ljust(2, "\x00".b) end
initiator_enc_handshake(initiator)
click to toggle source
# File lib/ciri/p2p/rlpx/connection.rb, line 97 def initiator_enc_handshake(initiator) initiator_auth_msg = initiator.auth_msg auth_msg_plain_text = initiator_auth_msg.rlp_encode # seal eip8 auth_packet = seal_eip8(auth_msg_plain_text, initiator) @io.write(auth_packet) @io.flush auth_ack_mgs_binary, auth_ack_packet = read_enc_handshake_msg(ENC_AUTH_RESP_MSG_LENGTH, initiator.private_key) auth_ack_msg = AuthRespV4.rlp_decode auth_ack_mgs_binary initiator.handle_auth_ack_msg(auth_ack_msg) initiator.extract_secrets(auth_packet, auth_ack_packet, initiator: true) end
read_enc_handshake_msg(plain_size, private_key)
click to toggle source
# File lib/ciri/p2p/rlpx/connection.rb, line 112 def read_enc_handshake_msg(plain_size, private_key) packet = @io.read(plain_size) decrypt_binary_msg = begin private_key.ecies_decrypt(packet) rescue Crypto::ECIESDecryptionError => e nil end # pre eip old plain format return decrypt_binary_msg if decrypt_binary_msg # try decode eip8 format prefix = packet[0...2] size = Ciri::Utils.big_endian_decode(prefix) raise FormatError.new("EIP8 format message size #{size} less than plain_size #{plain_size}") if size < plain_size # continue read remain bytes packet << @io.read(size - plain_size + 2) # decrypt message [private_key.ecies_decrypt(packet[2..-1], prefix), packet] end
read_protocol_handshake()
click to toggle source
# File lib/ciri/p2p/rlpx/connection.rb, line 135 def read_protocol_handshake msg = @frame_io.read_msg if msg.size > BASE_PROTOCOL_MAX_MSG_SIZE raise MessageOverflowError.new("message size #{msg.size} is too big") end if msg.code == Code::DISCONNECT payload = RLP.decode(msg.payload) raise UnexpectedMessageError.new("expected handshake, get disconnect, reason: #{payload}") end if msg.code != Code::HANDSHAKE raise UnexpectedMessageError.new("expected handshake, get #{msg.code}") end ProtocolHandshake.rlp_decode(msg.payload) end
receiver_enc_handshake(receiver)
click to toggle source
# File lib/ciri/p2p/rlpx/connection.rb, line 79 def receiver_enc_handshake(receiver) auth_msg_binary, auth_packet = read_enc_handshake_msg(ENC_AUTH_MSG_LENGTH, receiver.private_key) auth_msg = AuthMsgV4.rlp_decode(auth_msg_binary) receiver.handle_auth_msg(auth_msg) auth_ack_msg = receiver.auth_ack_msg auth_ack_msg_plain_text = auth_ack_msg.rlp_encode auth_ack_packet = if auth_msg.got_plain raise NotImplementedError.new('not support pre eip8 plain text seal') else seal_eip8(auth_ack_msg_plain_text, receiver) end @io.write(auth_ack_packet) @io.flush receiver.extract_secrets(auth_packet, auth_ack_packet, initiator: false) end
seal_eip8(encoded_msg, handshake)
click to toggle source
# File lib/ciri/p2p/rlpx/connection.rb, line 163 def seal_eip8(encoded_msg, handshake) # padding encoded message, make message distinguished from pre eip8 encoded_msg += "\x00".b * rand(100..300) prefix = encoded_prefix(encoded_msg.size + ECIES_OVERHEAD) enc = handshake.remote_key.ecies_encrypt(encoded_msg, prefix) prefix + enc end
set_timeout(io)
click to toggle source
# File lib/ciri/p2p/rlpx/connection.rb, line 151 def set_timeout(io) timeout = HANDSHAKE_TIMEOUT if io.is_a?(BasicSocket) secs = Integer(timeout) usecs = Integer((timeout - secs) * 1_000_000) optval = [secs, usecs].pack("l_2") io.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval io.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval end end