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



47
48
49
50
51
52
53
54
55
56
# File 'lib/bitcoin/network/message_handler.rb', line 47

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.



10
11
12
13
14
15
16
17
18
19
# File 'lib/bitcoin/network/message_handler.rb', line 10

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



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
104
105
# File 'lib/bitcoin/network/message_handler.rb', line 58

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



115
116
117
118
119
120
# File 'lib/bitcoin/network/message_handler.rb', line 115

def handshake_done
  return unless @incomming_handshake && @outgoing_handshake
  logger.info 'handshake finished.'
  @connected = true
  post_handshake
end

#on_addr(addr) ⇒ Object



141
142
143
144
# File 'lib/bitcoin/network/message_handler.rb', line 141

def on_addr(addr)
  logger.info("receive addr message. #{addr.build_json}")
  # TODO
end

#on_block(block) ⇒ Object



181
182
183
184
# File 'lib/bitcoin/network/message_handler.rb', line 181

def on_block(block)
  logger.info('receive block message.')
  # TODO
end

#on_cmpct_block(cmpct_block) ⇒ Object



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

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

#on_fee_filter(fee_filter) ⇒ Object



151
152
153
154
# File 'lib/bitcoin/network/message_handler.rb', line 151

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



136
137
138
139
# File 'lib/bitcoin/network/message_handler.rb', line 136

def on_get_addr
  logger.info('receive getaddr message.')
  peer.send_addrs
end

#on_get_data(get_data) ⇒ Object



238
239
240
# File 'lib/bitcoin/network/message_handler.rb', line 238

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

#on_get_headers(headers) ⇒ Object



171
172
173
174
# File 'lib/bitcoin/network/message_handler.rb', line 171

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

#on_headers(headers) ⇒ Object



176
177
178
179
# File 'lib/bitcoin/network/message_handler.rb', line 176

def on_headers(headers)
  logger.info('receive headers message.')
  peer.handle_headers(headers)
end

#on_inv(inv) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/bitcoin/network/message_handler.rb', line 211

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



196
197
198
199
# File 'lib/bitcoin/network/message_handler.rb', line 196

def on_mem_pool
  logger.info('receive mempool message.')
  # TODO return mempool tx
end

#on_merkle_block(merkle_block) ⇒ Object



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

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) ⇒ Object



191
192
193
194
# File 'lib/bitcoin/network/message_handler.rb', line 191

def on_not_found(not_found)
  logger.info("receive notfound message. #{not_found.build_json}")
  # TODO
end

#on_ping(ping) ⇒ Object



156
157
158
159
# File 'lib/bitcoin/network/message_handler.rb', line 156

def on_ping(ping)
  logger.info("receive ping message. #{ping.build_json}")
  send_message(ping.to_response)
end

#on_pong(pong) ⇒ Object



161
162
163
164
165
166
167
168
169
# File 'lib/bitcoin/network/message_handler.rb', line 161

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



201
202
203
204
# File 'lib/bitcoin/network/message_handler.rb', line 201

def on_reject(reject)
  logger.warn("receive reject message. #{reject.build_json}")
  # TODO
end

#on_send_cmpct(cmpct) ⇒ Object



206
207
208
209
# File 'lib/bitcoin/network/message_handler.rb', line 206

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



146
147
148
149
# File 'lib/bitcoin/network/message_handler.rb', line 146

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

#on_tx(tx) ⇒ Object



186
187
188
189
# File 'lib/bitcoin/network/message_handler.rb', line 186

def on_tx(tx)
  logger.info("receive tx message. #{tx.build_json}")
  peer.handle_tx(tx)
end

#on_ver_ackObject



130
131
132
133
134
# File 'lib/bitcoin/network/message_handler.rb', line 130

def on_ver_ack
  logger.info('receive verack message.')
  @outgoing_handshake = true
  handshake_done
end

#on_version(version) ⇒ Object



122
123
124
125
126
127
128
# File 'lib/bitcoin/network/message_handler.rb', line 122

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



21
22
23
24
25
26
27
28
29
# File 'lib/bitcoin/network/message_handler.rb', line 21

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



31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/bitcoin/network/message_handler.rb', line 31

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



107
108
109
110
111
112
113
# File 'lib/bitcoin/network/message_handler.rb', line 107

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