bitcoin-ruby 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +7 -7
- data/README.rdoc +1 -1
- data/examples/simple_network_monitor_and_util.rb +8 -0
- data/lib/bitcoin.rb +57 -17
- data/lib/bitcoin/builder.rb +15 -10
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +3 -2
- data/lib/bitcoin/ffi/secp256k1.rb +143 -81
- data/lib/bitcoin/key.rb +4 -6
- data/lib/bitcoin/logger.rb +11 -0
- data/lib/bitcoin/protocol.rb +19 -6
- data/lib/bitcoin/protocol/aux_pow.rb +62 -62
- data/lib/bitcoin/protocol/block.rb +20 -15
- data/lib/bitcoin/protocol/handler.rb +4 -0
- data/lib/bitcoin/protocol/parser.rb +30 -29
- data/lib/bitcoin/protocol/reject.rb +38 -0
- data/lib/bitcoin/protocol/tx.rb +19 -3
- data/lib/bitcoin/protocol/txin.rb +14 -8
- data/lib/bitcoin/script.rb +6 -1
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +4 -0
- data/spec/bitcoin/builder_spec.rb +11 -0
- data/spec/bitcoin/dogecoin_spec.rb +6 -6
- data/spec/bitcoin/protocol/addr_spec.rb +11 -1
- data/spec/bitcoin/protocol/aux_pow_spec.rb +9 -9
- data/spec/bitcoin/protocol/block_spec.rb +1 -0
- data/spec/bitcoin/protocol/inv_spec.rb +10 -2
- data/spec/bitcoin/protocol/parser_spec.rb +50 -0
- data/spec/bitcoin/protocol/reject.rb +17 -0
- data/spec/bitcoin/protocol/tx_spec.rb +5 -1
- data/spec/bitcoin/script/script_spec.rb +1 -1
- data/spec/bitcoin/secp256k1_spec.rb +8 -0
- data/spec/bitcoin/spec_helper.rb +6 -0
- metadata +8 -3
data/lib/bitcoin/key.rb
CHANGED
@@ -5,6 +5,8 @@ module Bitcoin
|
|
5
5
|
# Elliptic Curve key as used in bitcoin.
|
6
6
|
class Key
|
7
7
|
|
8
|
+
attr_reader :key
|
9
|
+
|
8
10
|
# Generate a new keypair.
|
9
11
|
# Bitcoin::Key.generate
|
10
12
|
def self.generate(opts={compressed: true})
|
@@ -54,6 +56,7 @@ module Bitcoin
|
|
54
56
|
# Set the private key to +priv+ (in hex).
|
55
57
|
def priv= priv
|
56
58
|
set_priv(priv)
|
59
|
+
regenerate_pubkey
|
57
60
|
end
|
58
61
|
|
59
62
|
# Get the public key (in hex).
|
@@ -98,12 +101,7 @@ module Bitcoin
|
|
98
101
|
# key1 = Bitcoin::Key.generate
|
99
102
|
# sig = key1.sign("some data")
|
100
103
|
def sign(data)
|
101
|
-
|
102
|
-
if Script::is_low_der_signature?(sig)
|
103
|
-
sig
|
104
|
-
else
|
105
|
-
Bitcoin::OpenSSL_EC.signature_to_low_s(sig)
|
106
|
-
end
|
104
|
+
Bitcoin.sign_data(key, data)
|
107
105
|
end
|
108
106
|
|
109
107
|
# Verify signature +sig+ for +data+.
|
data/lib/bitcoin/logger.rb
CHANGED
@@ -2,6 +2,17 @@
|
|
2
2
|
|
3
3
|
begin
|
4
4
|
require 'log4r'
|
5
|
+
# monkey-patch Log4r to accept level names as symbols
|
6
|
+
class Log4r::Logger
|
7
|
+
def level= l = 0
|
8
|
+
_level = l.is_a?(Fixnum) ? l : Log4r::LNAMES.index(l.to_s.upcase)
|
9
|
+
Log4r::Log4rTools.validate_level(_level)
|
10
|
+
@level = _level
|
11
|
+
LoggerFactory.define_methods(self)
|
12
|
+
Log4r::Logger.log_internal {"Logger '#{@fullname}' set to #{LNAMES[@level]}"}
|
13
|
+
@level
|
14
|
+
end
|
15
|
+
end
|
5
16
|
rescue LoadError
|
6
17
|
end
|
7
18
|
|
data/lib/bitcoin/protocol.rb
CHANGED
@@ -19,6 +19,7 @@ module Bitcoin
|
|
19
19
|
autoload :Block, 'bitcoin/protocol/block'
|
20
20
|
autoload :Addr, 'bitcoin/protocol/address'
|
21
21
|
autoload :Alert, 'bitcoin/protocol/alert'
|
22
|
+
autoload :Reject, 'bitcoin/protocol/reject'
|
22
23
|
autoload :Version, 'bitcoin/protocol/version'
|
23
24
|
autoload :AuxPow, 'bitcoin/protocol/aux_pow'
|
24
25
|
|
@@ -72,15 +73,27 @@ module Bitcoin
|
|
72
73
|
end
|
73
74
|
|
74
75
|
def self.unpack_var_string_array(payload) # unpacks set<string>
|
75
|
-
|
76
|
-
|
77
|
-
[
|
76
|
+
buf = StringIO.new(payload)
|
77
|
+
size = unpack_var_int_from_io(buf)
|
78
|
+
return [nil, buf.read] if size == 0
|
79
|
+
strings = []
|
80
|
+
size.times{
|
81
|
+
break if buf.eof?
|
82
|
+
strings << unpack_var_string_from_io(buf)
|
83
|
+
}
|
84
|
+
[strings, buf.read]
|
78
85
|
end
|
79
86
|
|
80
87
|
def self.unpack_var_int_array(payload) # unpacks set<int>
|
81
|
-
|
82
|
-
|
83
|
-
[
|
88
|
+
buf = StringIO.new(payload)
|
89
|
+
size = unpack_var_int_from_io(buf)
|
90
|
+
return [nil, buf.read] if size == 0
|
91
|
+
ints = []
|
92
|
+
size.times{
|
93
|
+
break if buf.eof?
|
94
|
+
ints << unpack_var_int_from_io(buf)
|
95
|
+
}
|
96
|
+
[ints, buf.read]
|
84
97
|
end
|
85
98
|
|
86
99
|
def self.unpack_boolean(payload)
|
@@ -4,25 +4,40 @@ module Bitcoin
|
|
4
4
|
module Protocol
|
5
5
|
|
6
6
|
# Auxiliary Proof-of-Work for merge-mined blockchains
|
7
|
+
# See https://en.bitcoin.it/wiki/Merged_mining_specification.
|
8
|
+
#
|
9
|
+
# The AuxPow contains all data needed to verify that the child
|
10
|
+
# block was included in the parents coinbase transaction, and
|
11
|
+
# the parent satisfies the difficulty target.
|
12
|
+
#
|
13
|
+
# It encodes the +parent_block+ header, and its +coinbase_tx+.
|
14
|
+
# The +coinbase_branch+ and +coinbase_index+ can be used to recalculate
|
15
|
+
# the parent blocks merkle root and prove the coinbase transaction is
|
16
|
+
# really included in it.
|
17
|
+
# The +chain_branch+ and +chain_index+ are used to link the child block
|
18
|
+
# to the merkle root contained in the +coinbase_tx+. (So there can be
|
19
|
+
# more than one merge-mined chain)
|
20
|
+
#
|
21
|
+
# TODO: decode merged-mining data from +coinbase_tx+
|
7
22
|
class AuxPow
|
8
23
|
|
9
|
-
# Coinbase transaction
|
10
|
-
attr_accessor :
|
24
|
+
# Coinbase transaction of the parent block, linking to the child block
|
25
|
+
attr_accessor :coinbase_tx
|
11
26
|
|
12
|
-
# Hash of the block header
|
27
|
+
# Hash of the parent block header
|
13
28
|
attr_accessor :block_hash
|
14
29
|
|
15
|
-
# Merkle
|
16
|
-
attr_accessor :
|
30
|
+
# Merkle branch linking the +coinbase_tx+ to the +parent_block+
|
31
|
+
attr_accessor :coinbase_branch
|
17
32
|
|
18
|
-
# Index of
|
19
|
-
attr_accessor :
|
33
|
+
# Index of the +coinbase_tx+ in the parent blocks merkle tree
|
34
|
+
attr_accessor :coinbase_index
|
20
35
|
|
21
|
-
# Merkle
|
22
|
-
attr_accessor :
|
36
|
+
# Merkle branch linking the child block to the +coinbase_tx+
|
37
|
+
attr_accessor :chain_branch
|
23
38
|
|
24
|
-
# Index of
|
25
|
-
attr_accessor :
|
39
|
+
# Index of the child block in the chain merkle tree
|
40
|
+
attr_accessor :chain_index
|
26
41
|
|
27
42
|
# Parent block header
|
28
43
|
attr_accessor :parent_block
|
@@ -32,47 +47,32 @@ module Bitcoin
|
|
32
47
|
end
|
33
48
|
|
34
49
|
def parse_data(data)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
branch_count, payload = P.unpack_var_int(payload)
|
40
|
-
@branch = []
|
41
|
-
branch_count.times {
|
42
|
-
b, payload = payload.unpack("a32a*")
|
43
|
-
@branch << b
|
44
|
-
}
|
45
|
-
@mrkl_index, payload = payload.unpack("Ia*")
|
46
|
-
|
47
|
-
@aux_branch = []
|
48
|
-
aux_branch_count, payload = P.unpack_var_int(payload)
|
49
|
-
aux_branch_count.times {
|
50
|
-
b, payload = payload.unpack("a32a*")
|
51
|
-
@aux_branch << b
|
52
|
-
}
|
53
|
-
|
54
|
-
@aux_index, payload = payload.unpack("Ia*")
|
55
|
-
block, payload = payload.unpack("a80a*")
|
56
|
-
@parent_block = P::Block.new(block)
|
57
|
-
|
58
|
-
payload
|
50
|
+
buf = StringIO.new(data)
|
51
|
+
parse_data_from_io(buf)
|
52
|
+
buf.eof? ? '' : buf.read
|
59
53
|
end
|
60
54
|
|
61
55
|
def parse_data_from_io(data)
|
62
|
-
@
|
63
|
-
@
|
56
|
+
@coinbase_tx = P::Tx.new(nil)
|
57
|
+
@coinbase_tx.parse_data_from_io(data)
|
64
58
|
|
65
59
|
@block_hash = data.read(32)
|
66
|
-
|
67
|
-
@
|
68
|
-
|
69
|
-
|
60
|
+
coinbase_branch_count = P.unpack_var_int_from_io(data)
|
61
|
+
@coinbase_branch = []
|
62
|
+
coinbase_branch_count.times{
|
63
|
+
break if data.eof?
|
64
|
+
@coinbase_branch << data.read(32).reverse.hth
|
65
|
+
}
|
66
|
+
@coinbase_index = data.read(4).unpack("I")[0]
|
70
67
|
|
71
|
-
@
|
72
|
-
|
73
|
-
|
68
|
+
@chain_branch = []
|
69
|
+
chain_branch_count = P.unpack_var_int_from_io(data)
|
70
|
+
chain_branch_count.times{
|
71
|
+
break if data.eof?
|
72
|
+
@chain_branch << data.read(32).reverse.hth
|
73
|
+
}
|
74
74
|
|
75
|
-
@
|
75
|
+
@chain_index = data.read(4).unpack("I")[0]
|
76
76
|
block = data.read(80)
|
77
77
|
@parent_block = P::Block.new(block)
|
78
78
|
|
@@ -81,14 +81,14 @@ module Bitcoin
|
|
81
81
|
|
82
82
|
|
83
83
|
def to_payload
|
84
|
-
payload = @
|
84
|
+
payload = @coinbase_tx.to_payload
|
85
85
|
payload << @block_hash
|
86
|
-
payload << P.pack_var_int(@
|
87
|
-
payload << @
|
88
|
-
payload << [@
|
89
|
-
payload << P.pack_var_int(@
|
90
|
-
payload << @
|
91
|
-
payload << [@
|
86
|
+
payload << P.pack_var_int(@coinbase_branch.count)
|
87
|
+
payload << @coinbase_branch.map(&:htb).map(&:reverse).join
|
88
|
+
payload << [@coinbase_index].pack("I")
|
89
|
+
payload << P.pack_var_int(@chain_branch.count)
|
90
|
+
payload << @chain_branch.map(&:htb).map(&:reverse).join
|
91
|
+
payload << [@chain_index].pack("I")
|
92
92
|
payload << @parent_block.to_payload
|
93
93
|
payload
|
94
94
|
end
|
@@ -96,24 +96,24 @@ module Bitcoin
|
|
96
96
|
def self.from_hash h
|
97
97
|
aux_pow = new(nil)
|
98
98
|
aux_pow.instance_eval do
|
99
|
-
@
|
99
|
+
@coinbase_tx = P::Tx.from_hash(h['coinbase_tx'])
|
100
100
|
@block_hash = h['block_hash'].htb
|
101
|
-
@
|
102
|
-
@
|
103
|
-
@
|
104
|
-
@
|
101
|
+
@coinbase_branch = h['coinbase_branch']
|
102
|
+
@coinbase_index = h['coinbase_index']
|
103
|
+
@chain_branch = h['chain_branch']
|
104
|
+
@chain_index = h['chain_index']
|
105
105
|
@parent_block = P::Block.from_hash(h['parent_block'])
|
106
106
|
end
|
107
107
|
aux_pow
|
108
108
|
end
|
109
109
|
|
110
110
|
def to_hash
|
111
|
-
{ '
|
111
|
+
{ 'coinbase_tx' => @coinbase_tx.to_hash,
|
112
112
|
'block_hash' => @block_hash.hth,
|
113
|
-
'
|
114
|
-
'
|
115
|
-
'
|
116
|
-
'
|
113
|
+
'coinbase_branch' => @coinbase_branch,
|
114
|
+
'coinbase_index' => @coinbase_index,
|
115
|
+
'chain_branch' => @chain_branch,
|
116
|
+
'chain_index' => @chain_index,
|
117
117
|
'parent_block' => @parent_block.to_hash }
|
118
118
|
end
|
119
119
|
|
@@ -14,7 +14,9 @@ module Bitcoin
|
|
14
14
|
attr_accessor :hash
|
15
15
|
|
16
16
|
# previous block hash
|
17
|
-
attr_accessor :
|
17
|
+
attr_accessor :prev_block_hash
|
18
|
+
alias :prev_block :prev_block_hash
|
19
|
+
def prev_block=(hash); @prev_block_hash = hash; end
|
18
20
|
|
19
21
|
# transactions (Array of Tx)
|
20
22
|
attr_accessor :tx
|
@@ -52,7 +54,7 @@ module Bitcoin
|
|
52
54
|
end
|
53
55
|
|
54
56
|
def prev_block_hex
|
55
|
-
@prev_block_hex ||= @
|
57
|
+
@prev_block_hex ||= @prev_block_hash.reverse.unpack("H*")[0]
|
56
58
|
end
|
57
59
|
|
58
60
|
# create block from raw binary +data+
|
@@ -70,7 +72,7 @@ module Bitcoin
|
|
70
72
|
# parse raw binary data
|
71
73
|
def parse_data_from_io(buf, header_only=false)
|
72
74
|
buf = buf.is_a?(String) ? StringIO.new(buf) : buf
|
73
|
-
@ver, @
|
75
|
+
@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce = buf.read(80).unpack("Va32a32VVV")
|
74
76
|
recalc_block_hash
|
75
77
|
|
76
78
|
if Bitcoin.network[:auxpow_chain_id] != nil && (@ver & BLOCK_VERSION_AUXPOW) > 0
|
@@ -84,7 +86,10 @@ module Bitcoin
|
|
84
86
|
@tx_count = tx_size
|
85
87
|
return buf if header_only
|
86
88
|
|
87
|
-
tx_size.times{
|
89
|
+
tx_size.times{
|
90
|
+
break if payload == true
|
91
|
+
return buf if buf.eof?
|
92
|
+
|
88
93
|
t = Tx.new(nil)
|
89
94
|
payload = t.parse_data_from_io(buf)
|
90
95
|
@tx << t
|
@@ -96,11 +101,11 @@ module Bitcoin
|
|
96
101
|
|
97
102
|
# recalculate the block hash
|
98
103
|
def recalc_block_hash
|
99
|
-
@hash = Bitcoin.block_hash(@
|
104
|
+
@hash = Bitcoin.block_hash(@prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver)
|
100
105
|
end
|
101
106
|
|
102
107
|
def recalc_block_scrypt_hash
|
103
|
-
@scrypt_hash = Bitcoin.block_scrypt_hash(@
|
108
|
+
@scrypt_hash = Bitcoin.block_scrypt_hash(@prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver)
|
104
109
|
end
|
105
110
|
|
106
111
|
def recalc_mrkl_root
|
@@ -115,12 +120,12 @@ module Bitcoin
|
|
115
120
|
# get the block header info
|
116
121
|
# [<version>, <prev_block>, <merkle_root>, <time>, <bits>, <nonce>, <txcount>, <size>]
|
117
122
|
def header_info
|
118
|
-
[@ver, @
|
123
|
+
[@ver, @prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, Time.at(@time), @bits, @nonce, @tx.size, @payload.size]
|
119
124
|
end
|
120
125
|
|
121
126
|
# convert to raw binary format
|
122
127
|
def to_payload
|
123
|
-
head = [@ver, @
|
128
|
+
head = [@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce].pack("Va32a32VVV")
|
124
129
|
head << @aux_pow.to_payload if @aux_pow
|
125
130
|
return head if @tx.size == 0
|
126
131
|
head << Protocol.pack_var_int(@tx.size)
|
@@ -129,13 +134,13 @@ module Bitcoin
|
|
129
134
|
end
|
130
135
|
|
131
136
|
# convert to ruby hash (see also #from_hash)
|
132
|
-
def to_hash
|
137
|
+
def to_hash(options = {})
|
133
138
|
h = {
|
134
139
|
'hash' => @hash, 'ver' => @ver,
|
135
|
-
'prev_block' => @
|
140
|
+
'prev_block' => @prev_block_hash.reverse_hth, 'mrkl_root' => @mrkl_root.reverse_hth,
|
136
141
|
'time' => @time, 'bits' => @bits, 'nonce' => @nonce,
|
137
142
|
'n_tx' => @tx.size, 'size' => (@payload||to_payload).bytesize,
|
138
|
-
'tx' => @tx.map{|i| i.to_hash },
|
143
|
+
'tx' => @tx.map{|i| i.to_hash(options) },
|
139
144
|
'mrkl_tree' => Bitcoin.hash_mrkl_tree( @tx.map{|i| i.hash } )
|
140
145
|
}
|
141
146
|
h['aux_pow'] = @aux_pow.to_hash if @aux_pow
|
@@ -177,7 +182,7 @@ module Bitcoin
|
|
177
182
|
# convert to json representation as seen in the block explorer.
|
178
183
|
# (see also #from_json)
|
179
184
|
def to_json(options = {:space => ''}, *a)
|
180
|
-
JSON.pretty_generate( to_hash, options )
|
185
|
+
JSON.pretty_generate( to_hash(options), options )
|
181
186
|
end
|
182
187
|
|
183
188
|
# write json representation to a file
|
@@ -191,12 +196,12 @@ module Bitcoin
|
|
191
196
|
blk = new(nil)
|
192
197
|
blk.instance_eval{
|
193
198
|
@ver, @time, @bits, @nonce = h.values_at('ver', 'time', 'bits', 'nonce')
|
194
|
-
@
|
199
|
+
@prev_block_hash, @mrkl_root = h.values_at('prev_block', 'mrkl_root').map{|i| i.htb_reverse }
|
195
200
|
unless h['hash'] == recalc_block_hash
|
196
201
|
raise "Block hash mismatch! Claimed: #{h['hash']}, Actual: #{@hash}" if do_raise
|
197
202
|
end
|
198
203
|
@aux_pow = AuxPow.from_hash(h['aux_pow']) if h['aux_pow']
|
199
|
-
h['tx'].each{|tx| @tx << Tx.from_hash(tx) }
|
204
|
+
h['tx'].each{|tx| @tx << Tx.from_hash(tx, do_raise) }
|
200
205
|
if h['tx'].any?
|
201
206
|
(raise "Block merkle root mismatch! Block: #{h['hash']}" unless verify_mrkl_root) if do_raise
|
202
207
|
end
|
@@ -222,7 +227,7 @@ module Bitcoin
|
|
222
227
|
|
223
228
|
# block header binary output
|
224
229
|
def block_header
|
225
|
-
[@ver, @
|
230
|
+
[@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce, Protocol.pack_var_int(0)].pack("Va32a32VVVa*")
|
226
231
|
end
|
227
232
|
|
228
233
|
# read binary block from a file
|
@@ -9,29 +9,19 @@ module Bitcoin
|
|
9
9
|
def initialize(handler=nil)
|
10
10
|
@h = handler || Handler.new
|
11
11
|
@buf = ""
|
12
|
-
@stats = {
|
13
|
-
'total_packets' => 0,
|
14
|
-
'total_bytes' => 0
|
15
|
-
}
|
12
|
+
@stats = { 'total_packets' => 0, 'total_bytes' => 0, 'total_errors' => 0 }
|
16
13
|
end
|
17
14
|
|
18
|
-
def log
|
19
|
-
@log ||= Bitcoin::Logger.create("parser")
|
20
|
-
end
|
15
|
+
def log; @log ||= Bitcoin::Logger.create("parser"); end
|
21
16
|
|
22
17
|
# handles inv/getdata packets
|
23
|
-
#
|
24
18
|
def parse_inv(payload, type=:put)
|
25
19
|
count, payload = Protocol.unpack_var_int(payload)
|
26
|
-
payload.each_byte.each_slice(36).with_index
|
20
|
+
payload.each_byte.each_slice(36).with_index do |i, idx|
|
27
21
|
hash = i[4..-1].reverse.pack("C32")
|
28
22
|
case i[0]
|
29
23
|
when 1
|
30
|
-
|
31
|
-
@h.on_inv_transaction(hash)
|
32
|
-
else
|
33
|
-
@h.on_get_transaction(hash)
|
34
|
-
end
|
24
|
+
type == :put ? @h.on_inv_transaction(hash) : @h.on_get_transaction(hash)
|
35
25
|
when 2
|
36
26
|
if type == :put
|
37
27
|
if @h.respond_to?(:on_inv_block_v2)
|
@@ -43,28 +33,26 @@ module Bitcoin
|
|
43
33
|
@h.on_get_block(hash)
|
44
34
|
end
|
45
35
|
else
|
46
|
-
|
36
|
+
parse_error :parse_inv, i.pack("C*")
|
47
37
|
end
|
48
|
-
|
38
|
+
end
|
49
39
|
end
|
50
40
|
|
51
41
|
def parse_addr(payload)
|
52
42
|
count, payload = Protocol.unpack_var_int(payload)
|
53
|
-
payload.each_byte.each_slice(30)
|
54
|
-
|
55
|
-
|
56
|
-
rescue
|
57
|
-
puts "Error parsing addr: #{i.inspect}"
|
58
|
-
end
|
59
|
-
@h.on_addr( addr )
|
60
|
-
}
|
43
|
+
payload.each_byte.each_slice(30) do |i|
|
44
|
+
@h.on_addr(Addr.new(i.pack("C*"))) rescue parse_error(:addr, i.pack("C*"))
|
45
|
+
end
|
61
46
|
end
|
62
47
|
|
63
48
|
def parse_headers(payload)
|
64
49
|
return unless @h.respond_to?(:on_headers)
|
65
50
|
buf = StringIO.new(payload)
|
66
51
|
count = Protocol.unpack_var_int_from_io(buf)
|
67
|
-
headers = count.times.map{
|
52
|
+
headers = count.times.map{
|
53
|
+
break if buf.eof?
|
54
|
+
b = Block.new; b.parse_data_from_io(buf, header_only=true); b
|
55
|
+
}
|
68
56
|
@h.on_headers(headers)
|
69
57
|
end
|
70
58
|
|
@@ -98,8 +86,9 @@ module Bitcoin
|
|
98
86
|
when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload)) if @h.respond_to?(:on_getheaders)
|
99
87
|
when 'mempool'; handle_mempool_request(payload)
|
100
88
|
when 'notfound'; handle_notfound_reply(payload)
|
89
|
+
when 'reject'; handle_reject(payload)
|
101
90
|
else
|
102
|
-
|
91
|
+
parse_error :unknown_packet, [command, payload.hth]
|
103
92
|
end
|
104
93
|
end
|
105
94
|
|
@@ -113,6 +102,11 @@ module Bitcoin
|
|
113
102
|
@h.on_alert Bitcoin::Protocol::Alert.parse(payload)
|
114
103
|
end
|
115
104
|
|
105
|
+
def handle_reject(payload)
|
106
|
+
return unless @h.respond_to?(:on_reject)
|
107
|
+
@h.on_reject Bitcoin::Protocol::Reject.parse(payload)
|
108
|
+
end
|
109
|
+
|
116
110
|
# https://en.bitcoin.it/wiki/BIP_0035
|
117
111
|
def handle_mempool_request(payload)
|
118
112
|
return unless @version.fields[:version] >= 60002 # Protocol version >= 60002
|
@@ -123,15 +117,15 @@ module Bitcoin
|
|
123
117
|
def handle_notfound_reply(payload)
|
124
118
|
return unless @h.respond_to?(:on_notfound)
|
125
119
|
count, payload = Protocol.unpack_var_int(payload)
|
126
|
-
payload.each_byte.each_slice(36)
|
120
|
+
payload.each_byte.each_slice(36) do |i|
|
127
121
|
hash = i[4..-1].reverse.pack("C32")
|
128
122
|
case i[0]
|
129
123
|
when 1; @h.on_notfound(:tx, hash)
|
130
124
|
when 2; @h.on_notfound(:block, hash)
|
131
125
|
else
|
132
|
-
|
126
|
+
parse_error(:notfound, [i.pack("C*"), hash])
|
133
127
|
end
|
134
|
-
|
128
|
+
end
|
135
129
|
end
|
136
130
|
|
137
131
|
def parse(buf)
|
@@ -179,6 +173,13 @@ module Bitcoin
|
|
179
173
|
log.debug { [type, msg] }
|
180
174
|
end
|
181
175
|
end
|
176
|
+
|
177
|
+
def parse_error *err
|
178
|
+
@stats['total_errors'] += 1
|
179
|
+
return unless @h.respond_to?(:on_error)
|
180
|
+
@h.on_error *err
|
181
|
+
end
|
182
|
+
|
182
183
|
end # Parser
|
183
184
|
|
184
185
|
end
|