Module: Bitcoin::Network::MessageHandler

Included in:
Connection
Defined in:
lib/bitcoin/network/message_handler.rb

Overview

P2P message handler used by peer connection class.

Instance Method Summary collapse

Instance Method Details

#defer_handle_command(command, payload) ⇒ Object

handle command with EM#defer



45
46
47
48
49
50
51
52
53
54
# 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) ⇒ Object

handle p2p message.



8
9
10
11
12
13
14
15
16
17
# 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) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 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_doneObject



113
114
115
116
117
118
# 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) ⇒ Object



139
140
141
142
# 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) ⇒ Object



179
180
181
182
# 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) ⇒ Object



231
232
233
# File 'lib/bitcoin/network/message_handler.rb', line 231

def on_cmpct_block(cmpct_block)
  logger.info("receive cmpct_block message. #{cmpct_block.build_json}")
end

#on_fee_filter(fee_filter) ⇒ Object



149
150
151
152
# 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_addrObject



134
135
136
137
# 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) ⇒ Object



235
236
237
# File 'lib/bitcoin/network/message_handler.rb', line 235

def on_get_data(get_data)
  logger.info("receive get data message. #{get_data.build_json}")
end

#on_get_headers(headers) ⇒ Object



169
170
171
172
# File 'lib/bitcoin/network/message_handler.rb', line 169

def on_get_headers(headers)
  logger.info('receive getheaders message.')
  # TODO
end

#on_headers(headers) ⇒ Object



174
175
176
177
# 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) ⇒ Object



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# 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_poolObject



194
195
196
197
# 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) ⇒ Object



227
228
229
# 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}")
end

#on_not_found(not_found) ⇒ Object



189
190
191
192
# 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) ⇒ Object



154
155
156
157
# 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) ⇒ Object



159
160
161
162
163
164
165
166
167
# 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) ⇒ Object



199
200
201
202
# 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) ⇒ Object



204
205
206
207
# 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_headersObject



144
145
146
147
# File 'lib/bitcoin/network/message_handler.rb', line 144

def on_send_headers
  logger.info('receive sendheaders message.')
  @sendheaders = true
end

#on_tx(tx) ⇒ Object



184
185
186
187
# 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_ackObject



128
129
130
131
132
# 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) ⇒ Object



120
121
122
123
124
125
126
# 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) ⇒ Object



19
20
21
22
23
24
25
26
27
# 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_headerObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 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) ⇒ Object



105
106
107
108
109
110
111
# 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