Class: Bitcoin::Protocol::Parser
- Inherits:
-
Object
- Object
- Bitcoin::Protocol::Parser
- Defined in:
- lib/bitcoin/protocol/parser.rb
Instance Attribute Summary collapse
-
#stats ⇒ Object
readonly
Returns the value of attribute stats.
Instance Method Summary collapse
- #handle_mempool_request(payload) ⇒ Object
- #handle_notfound_reply(payload) ⇒ Object
- #handle_stream_error(type, msg) ⇒ Object
-
#initialize(handler = nil) ⇒ Parser
constructor
A new instance of Parser.
- #log ⇒ Object
- #parse(buf) ⇒ Object
- #parse_addr(payload) ⇒ Object
- #parse_alert(payload) ⇒ Object
- #parse_buffer ⇒ Object
- #parse_getblocks(payload) ⇒ Object
- #parse_headers(payload) ⇒ Object
-
#parse_inv(payload, type = :put) ⇒ Object
handles inv/getdata packets.
- #parse_version(payload) ⇒ Object
- #process_pkt(command, payload) ⇒ Object
Constructor Details
Instance Attribute Details
#stats ⇒ Object (readonly)
Returns the value of attribute stats.
7 8 9 |
# File 'lib/bitcoin/protocol/parser.rb', line 7 def stats @stats end |
Instance Method Details
#handle_mempool_request(payload) ⇒ Object
113 114 115 116 117 |
# File 'lib/bitcoin/protocol/parser.rb', line 113 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 |
#handle_notfound_reply(payload) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/bitcoin/protocol/parser.rb', line 119 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 |
#handle_stream_error(type, msg) ⇒ Object
170 171 172 173 174 175 176 177 |
# File 'lib/bitcoin/protocol/parser.rb', line 170 def handle_stream_error(type, msg) case type when :close log.debug {"closing packet stream (#{msg})"} else log.debug { [type, msg] } end end |
#log ⇒ Object
18 19 20 |
# File 'lib/bitcoin/protocol/parser.rb', line 18 def log @log ||= Bitcoin::Logger.create("parser") end |
#parse(buf) ⇒ Object
133 134 135 136 137 |
# File 'lib/bitcoin/protocol/parser.rb', line 133 def parse(buf) @buf += buf while parse_buffer; end @buf end |
#parse_addr(payload) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/bitcoin/protocol/parser.rb', line 47 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*")) rescue puts "Error parsing addr: #{i.inspect}" end @h.on_addr( addr ) } end |
#parse_alert(payload) ⇒ Object
107 108 109 110 |
# File 'lib/bitcoin/protocol/parser.rb', line 107 def parse_alert(payload) return unless @h.respond_to?(:on_alert) @h.on_alert Bitcoin::Protocol::Alert.parse(payload) end |
#parse_buffer ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/bitcoin/protocol/parser.rb', line 139 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("a4A12Va4") payload = @buf[head_size...head_size+length] unless magic == head_magic 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_stream_error(:debug, "chunked packet stream (#{size_info})") else handle_stream_error(:close, "checksum mismatch") end return end @buf = @buf[head_size+length..-1] || "" process_pkt(cmd, payload) end # not empty yet? parse more. @buf[0] != nil end |
#parse_getblocks(payload) ⇒ Object
67 68 69 70 71 72 73 74 |
# File 'lib/bitcoin/protocol/parser.rb', line 67 def parse_getblocks(payload) 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 = i.reverse.pack("C32").hth } stop_hash = payload[0..32].reverse_hth [version, hashes, stop_hash] end |
#parse_headers(payload) ⇒ Object
59 60 61 62 63 64 65 |
# File 'lib/bitcoin/protocol/parser.rb', line 59 def parse_headers(payload) 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 |
#parse_inv(payload, type = :put) ⇒ Object
handles inv/getdata packets
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/bitcoin/protocol/parser.rb', line 24 def parse_inv(payload, type=:put) 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 if type == :put @h.on_inv_transaction(hash) else @h.on_get_transaction(hash) end when 2 if type == :put @h.on_inv_block(hash) else @h.on_get_block(hash) end else p ['parse_inv error', i] end } end |
#parse_version(payload) ⇒ Object
102 103 104 105 |
# File 'lib/bitcoin/protocol/parser.rb', line 102 def parse_version(payload) @version = Bitcoin::Protocol::Version.parse(payload) @h.on_version(@version) end |
#process_pkt(command, payload) ⇒ Object
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 |
# File 'lib/bitcoin/protocol/parser.rb', line 76 def process_pkt(command, payload) @stats['total_packets'] += 1 @stats['total_bytes'] += payload.bytesize @stats[command] ? (@stats[command] += 1) : @stats[command] = 1 case command when 'tx'; @h.on_tx( Tx.new(payload) ) 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 '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)) 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 ['unknown-packet', command, payload] end end |