lib/bitcoin/protocol/parser.rb in bitcoin-ruby-0.0.1 vs lib/bitcoin/protocol/parser.rb in bitcoin-ruby-0.0.2
- old
+ new
@@ -1,5 +1,7 @@
+# encoding: ascii-8bit
+
module Bitcoin
module Protocol
class Parser
@@ -35,12 +37,10 @@
p ['parse_inv error', i]
end
}
end
- def hth(h); h.unpack("H*")[0]; end
-
def parse_addr(payload)
count, payload = Protocol.unpack_var_int(payload)
payload.each_byte.each_slice(30){|i|
begin
addr = Addr.new(i.pack("C*"))
@@ -50,22 +50,23 @@
@h.on_addr( addr )
}
end
def parse_headers(payload)
- count, payload = Protocol.unpack_var_int(payload)
- idx = 0
- headers = count.times.map{ Block.new(payload[idx..idx+=81]) }
+ return unless @h.respond_to?(:on_headers)
+ buf = StringIO.new(payload)
+ count = Protocol.unpack_var_int_from_io(buf)
+ headers = count.times.map{ b = Block.new; b.parse_data_from_io(buf, header_only=true); b }
@h.on_headers(headers)
end
def parse_getblocks(payload)
- version, payload = payload.unpack('a4a*')
+ version, payload = payload.unpack('Va*')
count, payload = Protocol.unpack_var_int(payload)
buf, payload = payload.unpack("a#{count*32}a*")
- hashes = buf.each_byte.each_slice(32).map{|i| hash = Protocol.hth(i.reverse.pack("C32")) }
- stop_hash = Protocol.hth(payload[0..32].reverse)
+ hashes = buf.each_byte.each_slice(32).map{|i| hash = i.reverse.pack("C32").hth }
+ stop_hash = payload[0..32].reverse_hth
[version, hashes, stop_hash]
end
def process_pkt(command, payload)
case command
@@ -73,32 +74,56 @@
when 'block'; @h.on_block( Block.new(payload) )
when 'headers'; parse_headers(payload)
when 'inv'; parse_inv(payload, :put)
when 'getdata'; parse_inv(payload, :get)
when 'addr'; parse_addr(payload)
- when 'verack'; @h.on_handshake_complete # nop
+ when 'getaddr'; @h.on_getaddr if @h.respond_to?(:on_getaddr)
+ when 'verack'; @h.respond_to?(:on_verack) ? @h.on_verack : (@h.respond_to?(:on_handshake_complete) ? @h.on_handshake_complete : nil)
when 'version'; parse_version(payload)
when 'alert'; parse_alert(payload)
when 'ping'; @h.on_ping(payload.unpack("Q")[0])
when 'pong'; @h.on_pong(payload.unpack("Q")[0])
- when 'getblocks'; @h.on_getblocks(*parse_getblocks(payload))
- when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload))
+ when 'getblocks'; @h.on_getblocks(*parse_getblocks(payload)) if @h.respond_to?(:on_getblocks)
+ when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload)) if @h.respond_to?(:on_getheaders)
+ when 'mempool'; handle_mempool_request(payload)
+ when 'notfound'; handle_notfound_reply(payload)
else
- p ['unkown-packet', command, payload]
+ p ['unknown-packet', command, payload]
end
end
def parse_version(payload)
- version = Bitcoin::Protocol::Version.parse(payload)
- @h.on_version(version)
+ @version = Bitcoin::Protocol::Version.parse(payload)
+ @h.on_version(@version)
end
def parse_alert(payload)
return unless @h.respond_to?(:on_alert)
@h.on_alert Bitcoin::Protocol::Alert.parse(payload)
end
+ # https://en.bitcoin.it/wiki/BIP_0035
+ def handle_mempool_request(payload)
+ return unless @version[:version] >= 60002 # Protocol version >= 60002
+ return unless (@version[:services] & Bitcoin::Protocol::Version::NODE_NETWORK) == 1 # NODE_NETWORK bit set in Services
+ @h.on_mempool if @h.respond_to?(:on_mempool)
+ end
+
+ def handle_notfound_reply(payload)
+ return unless @h.respond_to?(:on_notfound)
+ count, payload = Protocol.unpack_var_int(payload)
+ payload.each_byte.each_slice(36){|i|
+ hash = i[4..-1].reverse.pack("C32")
+ case i[0]
+ when 1; @h.on_notfound(:tx, hash)
+ when 2; @h.on_notfound(:block, hash)
+ else
+ p ['handle_notfound_reply error', i, hash]
+ end
+ }
+ end
+
def parse(buf)
@buf += buf
while parse_buffer; end
@buf
end
@@ -106,24 +131,24 @@
def parse_buffer
head_magic = Bitcoin::network[:magic_head]
head_size = 24
return false if @buf.size <= head_size
- magic, cmd, length, checksum = @buf.unpack("a4A12Ia4")
+ magic, cmd, length, checksum = @buf.unpack("a4A12Va4")
payload = @buf[head_size...head_size+length]
unless magic == head_magic
- handle_error(:close, "head_magic not found")
+ handle_stream_error(:close, "head_magic not found")
@buf = ''
else
if Digest::SHA256.digest(Digest::SHA256.digest( payload ))[0...4] != checksum
if (length < 50000) && (payload.size < length)
size_info = [payload.size, length].join('/')
- handle_error(:debug, "chunked packet stream (#{size_info})")
+ handle_stream_error(:debug, "chunked packet stream (#{size_info})")
else
- handle_error(:close, "checksum mismatch")
+ handle_stream_error(:close, "checksum mismatch")
end
return
end
@buf = @buf[head_size+length..-1] || ""
@@ -132,10 +157,10 @@
# not empty yet? parse more.
@buf[0] != nil
end
- def handle_error(type, msg)
+ def handle_stream_error(type, msg)
case type
when :close
log.debug {"closing packet stream (#{msg})"}
else
log.debug { [type, msg] }