module Bitcoin::Network::MessageHandler

P2P message handler used by peer connection class.

Public Instance Methods

defer_handle_command(command, payload) click to toggle source

handle command with EM#defer

# File lib/bitcoin/network/message_handler.rb, line 45
def defer_handle_command(command, payload)
  operation = proc {handle_command(command, payload)}
  callback = proc{|result|}
  errback = proc{|e|
    logger.error("error occurred. #{e.message}")
    logger.error(e.backtrace)
    peer.handle_error(e)
  }
  EM.defer(operation, callback, errback)
end
handle(message) click to toggle source

handle p2p message.

# File lib/bitcoin/network/message_handler.rb, line 8
def handle(message)
  peer.last_recv = Time.now.to_i
  peer.bytes_recv += message.bytesize
  begin
    parse(message)
  rescue Bitcoin::Message::Error => e
    logger.error("invalid header magic. #{e.message}")
    close
  end
end
handle_command(command, payload) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 56
def handle_command(command, payload)
  logger.info("[#{addr}] process command #{command}.")
  case command
    when Bitcoin::Message::Version::COMMAND
      on_version(Bitcoin::Message::Version.parse_from_payload(payload))
    when Bitcoin::Message::VerAck::COMMAND
      on_ver_ack
    when Bitcoin::Message::GetAddr::COMMAND
      on_get_addr
    when Bitcoin::Message::Addr::COMMAND
      on_addr(Bitcoin::Message::Addr.parse_from_payload(payload))
    when Bitcoin::Message::SendHeaders::COMMAND
      on_send_headers
    when Bitcoin::Message::FeeFilter::COMMAND
      on_fee_filter(Bitcoin::Message::FeeFilter.parse_from_payload(payload))
    when Bitcoin::Message::Ping::COMMAND
      on_ping(Bitcoin::Message::Ping.parse_from_payload(payload))
    when Bitcoin::Message::Pong::COMMAND
      on_pong(Bitcoin::Message::Pong.parse_from_payload(payload))
    when Bitcoin::Message::GetHeaders::COMMAND
      on_get_headers(Bitcoin::Message::GetHeaders.parse_from_payload(payload))
    when Bitcoin::Message::Headers::COMMAND
      on_headers(Bitcoin::Message::Headers.parse_from_payload(payload))
    when Bitcoin::Message::Block::COMMAND
      on_block(Bitcoin::Message::Block.parse_from_payload(payload))
    when Bitcoin::Message::Tx::COMMAND
      on_tx(Bitcoin::Message::Tx.parse_from_payload(payload))
    when Bitcoin::Message::NotFound::COMMAND
      on_not_found(Bitcoin::Message::NotFound.parse_from_payload(payload))
    when Bitcoin::Message::MemPool::COMMAND
      on_mem_pool
    when Bitcoin::Message::Reject::COMMAND
      on_reject(Bitcoin::Message::Reject.parse_from_payload(payload))
    when Bitcoin::Message::SendCmpct::COMMAND
      on_send_cmpct(Bitcoin::Message::SendCmpct.parse_from_payload(payload))
    when Bitcoin::Message::Inv::COMMAND
      on_inv(Bitcoin::Message::Inv.parse_from_payload(payload))
    when Bitcoin::Message::MerkleBlock::COMMAND
      on_merkle_block(Bitcoin::Message::MerkleBlock.parse_from_payload(payload))
    when Bitcoin::Message::CmpctBlock::COMMAND
      on_cmpct_block(Bitcoin::Message::CmpctBlock.parse_from_payload(payload))
    when Bitcoin::Message::GetData::COMMAND
      on_get_data(Bitcoin::Message::GetData.parse_from_payload(payload))
    else
      logger.warn("unsupported command received. command: #{command}, payload: #{payload.bth}")
      close("with command #{command}")
  end
end
handshake_done() click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 113
def handshake_done
  return unless @incomming_handshake && @outgoing_handshake
  logger.info 'handshake finished.'
  @connected = true
  post_handshake
end
on_addr(addr) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 139
def on_addr(addr)
  logger.info("receive addr message. #{addr.build_json}")
  # TODO
end
on_block(block) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 179
def on_block(block)
  logger.info('receive block message.')
  # TODO
end
on_cmpct_block(cmpct_block) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 232
def on_cmpct_block(cmpct_block)
  logger.info("receive cmpct_block message. #{cmpct_block.build_json}")
end
on_fee_filter(fee_filter) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 149
def on_fee_filter(fee_filter)
  logger.info("receive feefilter message. #{fee_filter.build_json}")
  @fee_rate = fee_filter.fee_rate
end
on_get_addr() click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 134
def on_get_addr
  logger.info('receive getaddr message.')
  peer.send_addrs
end
on_get_data(get_data) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 236
def on_get_data(get_data)
  logger.info("receive get data message. #{get_data.build_json}")
end
on_get_headers(headers) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 169
def on_get_headers(headers)
  logger.info('receive getheaders message.')
  # TODO
end
on_headers(headers) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 174
def on_headers(headers)
  logger.info('receive headers message.')
  peer.handle_headers(headers)
end
on_inv(inv) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 209
def on_inv(inv)
  logger.info('receive inv message.')
  blocks = []
  txs = []
  inv.inventories.each do |i|
    case i.identifier
      when Bitcoin::Message::Inventory::MSG_TX
        txs << i.hash
      when Bitcoin::Message::Inventory::MSG_BLOCK
        blocks << i.hash
      else
        logger.warn("[#{addr}] peer sent unknown inv type: #{i.identifier}")
    end
  end
  logger.info("receive block= #{blocks.size}, txs: #{txs.size}")
  peer.handle_block_inv(blocks) unless blocks.empty?
end
on_mem_pool() click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 194
def on_mem_pool
  logger.info('receive mempool message.')
  # TODO return mempool tx
end
on_merkle_block(merkle_block) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 227
def on_merkle_block(merkle_block)
  logger.info("receive merkle block message. #{merkle_block.build_json}")
  peer.handle_merkle_block(merkle_block)
end
on_not_found(not_found) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 189
def on_not_found(not_found)
  logger.info("receive notfound message. #{not_found.build_json}")
  # TODO
end
on_ping(ping) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 154
def on_ping(ping)
  logger.info("receive ping message. #{ping.build_json}")
  send_message(ping.to_response)
end
on_pong(pong) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 159
def on_pong(pong)
  logger.info("receive pong message. #{pong.build_json}")
  if pong.nonce == peer.last_ping_nonce
    peer.last_ping_nonce = nil
    peer.last_pong = Time.now.to_i
  else
    logger.debug "The remote peer sent the wrong nonce (#{pong.nonce})."
  end
end
on_reject(reject) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 199
def on_reject(reject)
  logger.warn("receive reject message. #{reject.build_json}")
  # TODO
end
on_send_cmpct(cmpct) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 204
def on_send_cmpct(cmpct)
  logger.info("receive sendcmpct message. #{cmpct.build_json}")
  # TODO if mode is high and version is 1, relay block with cmpctblock message
end
on_send_headers() click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 144
def on_send_headers
  logger.info('receive sendheaders message.')
  @sendheaders = true
end
on_tx(tx) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 184
def on_tx(tx)
  logger.info("receive tx message. #{tx.build_json}")
  peer.handle_tx(tx)
end
on_ver_ack() click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 128
def on_ver_ack
  logger.info('receive verack message.')
  @outgoing_handshake = true
  handshake_done
end
on_version(version) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 120
def on_version(version)
  logger.info("receive version message. #{version.build_json}")
  @version = version
  send_message(Bitcoin::Message::VerAck.new)
  @incomming_handshake = true
  handshake_done
end
parse(message) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 19
def parse(message)
  @message += message
  command, payload, rest = parse_header
  return unless command

  defer_handle_command(command, payload)
  @message = ""
  parse(rest) if rest && rest.bytesize > 0
end
parse_header() click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 29
def parse_header
  head_magic = Bitcoin.chain_params.magic_head
  return if @message.nil? || @message.size < MESSAGE_HEADER_SIZE

  magic, command, length, checksum = @message.unpack('a4A12Va4')
  raise Bitcoin::Message::Error, "invalid header magic. #{magic.bth}" unless magic.bth == head_magic

  payload = @message[MESSAGE_HEADER_SIZE...(MESSAGE_HEADER_SIZE + length)]
  return if payload.size < length
  raise Bitcoin::Message::Error, "header checksum mismatch. #{checksum.bth}" unless Bitcoin.double_sha256(payload)[0...4] == checksum

  rest = @message[(MESSAGE_HEADER_SIZE + length)..-1]
  [command, payload, rest]
end
send_message(msg) click to toggle source
# File lib/bitcoin/network/message_handler.rb, line 105
def send_message(msg)
  logger.info "send message #{msg.class::COMMAND}"
  pkt = msg.to_pkt
  peer.last_send = Time.now.to_i
  peer.bytes_sent = pkt.bytesize
  send_data(pkt)
end