monacoin-ruby 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,194 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+ module Protocol
5
+
6
+ class Parser
7
+ attr_reader :stats
8
+
9
+ def initialize(handler=nil)
10
+ @h = handler || Handler.new
11
+ @buf = ""
12
+ @stats = { 'total_packets' => 0, 'total_bytes' => 0, 'total_errors' => 0 }
13
+ end
14
+
15
+ def log; @log ||= Bitcoin::Logger.create("parser"); end
16
+
17
+ # handles inv/getdata packets
18
+ def parse_inv(payload, type=:put)
19
+ count, payload = Protocol.unpack_var_int(payload)
20
+ payload.each_byte.each_slice(36).with_index do |i, idx|
21
+ hash = i[4..-1].reverse.pack("C32")
22
+ case i[0]
23
+ when 1
24
+ type == :put ? @h.on_inv_transaction(hash) : @h.on_get_transaction(hash)
25
+ when 2
26
+ if type == :put
27
+ if @h.respond_to?(:on_inv_block_v2)
28
+ @h.on_inv_block_v2(hash, idx, count)
29
+ else
30
+ @h.on_inv_block(hash)
31
+ end
32
+ else
33
+ @h.on_get_block(hash)
34
+ end
35
+ else
36
+ parse_error :parse_inv, i.pack("C*")
37
+ end
38
+ end
39
+ end
40
+
41
+ def parse_addr(payload)
42
+ count, payload = Protocol.unpack_var_int(payload)
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
46
+ end
47
+
48
+ def parse_headers(payload)
49
+ return unless @h.respond_to?(:on_headers)
50
+ buf = StringIO.new(payload)
51
+ count = Protocol.unpack_var_int_from_io(buf)
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
+ }
56
+ @h.on_headers(headers)
57
+ end
58
+
59
+ def parse_mrkle_block(payload)
60
+ return unless @h.respond_to?(:on_mrkle_block)
61
+ b = Block.new
62
+ b.parse_data_from_io(payload, header_only= :filtered)
63
+ @h.on_mrkle_block(b)
64
+ end
65
+
66
+ def parse_getblocks(payload)
67
+ version, payload = payload.unpack('Va*')
68
+ count, payload = Protocol.unpack_var_int(payload)
69
+ buf, payload = payload.unpack("a#{count*32}a*")
70
+ hashes = buf.each_byte.each_slice(32).map{|i| hash = i.reverse.pack("C32").hth }
71
+ stop_hash = payload[0..32].reverse_hth
72
+ [version, hashes, stop_hash]
73
+ end
74
+
75
+ def process_pkt(command, payload)
76
+ @stats['total_packets'] += 1
77
+ @stats['total_bytes'] += payload.bytesize
78
+ @stats[command] ? (@stats[command] += 1) : @stats[command] = 1
79
+ case command
80
+ when 'tx'; @h.on_tx( Tx.new(payload) )
81
+ when 'block'; @h.on_block( Block.new(payload) )
82
+ when 'headers'; parse_headers(payload)
83
+ when 'inv'; parse_inv(payload, :put)
84
+ when 'getdata'; parse_inv(payload, :get)
85
+ when 'addr'; parse_addr(payload)
86
+ when 'getaddr'; @h.on_getaddr if @h.respond_to?(:on_getaddr)
87
+ when 'verack'; @h.respond_to?(:on_verack) ? @h.on_verack : (@h.respond_to?(:on_handshake_complete) ? @h.on_handshake_complete : nil)
88
+ when 'version'; parse_version(payload)
89
+ when 'alert'; parse_alert(payload)
90
+ when 'ping'; @h.on_ping(payload.unpack("Q")[0])
91
+ when 'pong'; @h.on_pong(payload.unpack("Q")[0])
92
+ when 'getblocks'; @h.on_getblocks(*parse_getblocks(payload)) if @h.respond_to?(:on_getblocks)
93
+ when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload)) if @h.respond_to?(:on_getheaders)
94
+ when 'mempool'; handle_mempool_request(payload)
95
+ when 'notfound'; handle_notfound_reply(payload)
96
+ when 'merkleblock'; parse_mrkle_block(payload)
97
+ when 'reject'; handle_reject(payload)
98
+ else
99
+ parse_error :unknown_packet, [command, payload.hth]
100
+ end
101
+ end
102
+
103
+ def parse_version(payload)
104
+ @version = Bitcoin::Protocol::Version.parse(payload)
105
+ @h.on_version(@version)
106
+ end
107
+
108
+ def parse_alert(payload)
109
+ return unless @h.respond_to?(:on_alert)
110
+ @h.on_alert Bitcoin::Protocol::Alert.parse(payload)
111
+ end
112
+
113
+ def handle_reject(payload)
114
+ return unless @h.respond_to?(:on_reject)
115
+ @h.on_reject Bitcoin::Protocol::Reject.parse(payload)
116
+ end
117
+
118
+ # https://en.bitcoin.it/wiki/BIP_0035
119
+ def handle_mempool_request(payload)
120
+ return unless @version.fields[:version] >= 60002 # Protocol version >= 60002
121
+ return unless (@version.fields[:services] & Bitcoin::Protocol::Version::NODE_NETWORK) == 1 # NODE_NETWORK bit set in Services
122
+ @h.on_mempool if @h.respond_to?(:on_mempool)
123
+ end
124
+
125
+ def handle_notfound_reply(payload)
126
+ return unless @h.respond_to?(:on_notfound)
127
+ count, payload = Protocol.unpack_var_int(payload)
128
+ payload.each_byte.each_slice(36) do |i|
129
+ hash = i[4..-1].reverse.pack("C32")
130
+ case i[0]
131
+ when 1; @h.on_notfound(:tx, hash)
132
+ when 2; @h.on_notfound(:block, hash)
133
+ else
134
+ parse_error(:notfound, [i.pack("C*"), hash])
135
+ end
136
+ end
137
+ end
138
+
139
+ def parse(buf)
140
+ @buf += buf
141
+ while parse_buffer; end
142
+ @buf
143
+ end
144
+
145
+ def parse_buffer
146
+ head_magic = Bitcoin::network[:magic_head]
147
+ head_size = 24
148
+ return false if @buf.size < head_size
149
+
150
+ magic, cmd, length, checksum = @buf.unpack("a4A12Va4")
151
+ payload = @buf[head_size...head_size+length]
152
+
153
+ unless magic == head_magic
154
+ handle_stream_error(:close, "head_magic not found")
155
+ @buf = ''
156
+ else
157
+
158
+ if Digest::SHA256.digest(Digest::SHA256.digest( payload ))[0...4] != checksum
159
+ if (length < 50000) && (payload.size < length)
160
+ size_info = [payload.size, length].join('/')
161
+ handle_stream_error(:debug, "chunked packet stream (#{size_info})")
162
+ else
163
+ handle_stream_error(:close, "checksum mismatch")
164
+ end
165
+ return
166
+ end
167
+ @buf = @buf[head_size+length..-1] || ""
168
+
169
+ process_pkt(cmd, payload)
170
+ end
171
+
172
+ # not empty yet? parse more.
173
+ @buf[0] != nil
174
+ end
175
+
176
+ def handle_stream_error(type, msg)
177
+ case type
178
+ when :close
179
+ log.debug {"closing packet stream (#{msg})"}
180
+ else
181
+ log.debug { [type, msg] }
182
+ end
183
+ end
184
+
185
+ def parse_error *err
186
+ @stats['total_errors'] += 1
187
+ return unless @h.respond_to?(:on_error)
188
+ @h.on_error *err
189
+ end
190
+
191
+ end # Parser
192
+
193
+ end
194
+ end
@@ -0,0 +1,61 @@
1
+ class Bitcoin::Protocol::PartialMerkleTree
2
+ Node = Struct.new(:value, :left, :right, :width_idx)
3
+
4
+ BIT_MASK = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]
5
+
6
+ def initialize(total_txs, hashes, flags)
7
+ @total_txs, @flags = total_txs, flags
8
+ @hashes = hashes.map{|h| h.reverse_hth }
9
+ @visit_idx = 0
10
+ end
11
+
12
+ def tx_hashes
13
+ @leaves.reject{|n| n.value.nil? }.map{|n| n.value }
14
+ end
15
+
16
+ def build_tree
17
+ lay = @leaves = @total_txs.times.map{ Node.new(nil, nil, nil) }
18
+ while lay.size > 1
19
+ lay = lay.each_slice(2).map do |left, right|
20
+ Node.new(nil, left, right)
21
+ end
22
+ end
23
+ return lay[0]
24
+ end
25
+
26
+ def current_flag
27
+ @flags[@visit_idx / 8].ord & BIT_MASK[@visit_idx % 8] == 0
28
+ end
29
+
30
+ def root
31
+ @root ||= build_tree
32
+ end
33
+
34
+ def set_value(node = root)
35
+ if current_flag || (node.left.nil? && node.right.nil?)
36
+ node.value = @hashes.shift
37
+ return
38
+ end
39
+
40
+ if node.left
41
+ @visit_idx += 1
42
+ set_value(node.left)
43
+ end
44
+ if node.right
45
+ @visit_idx += 1
46
+ set_value(node.right)
47
+ end
48
+
49
+ right = node.right || node.left
50
+ node.value = Bitcoin.bitcoin_mrkl(node.left.value, right.value)
51
+
52
+ return
53
+ end
54
+
55
+ def valid_tree?(mrkl_root_hash)
56
+ return false unless @hashes.empty?
57
+ return false if ((@visit_idx + 1)/8.0).ceil != @flags.length
58
+ return false if mrkl_root_hash != root.value
59
+ return true
60
+ end
61
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+ module Protocol
5
+
6
+ class Reject < Struct.new(:message, :ccode, :reason, :data)
7
+ CCODE_TABLE = {
8
+ 0x01 => :malformed,
9
+ 0x10 => :invalid,
10
+ 0x11 => :obsolete,
11
+ 0x12 => :duplicate,
12
+ 0x40 => :nonstandard,
13
+ 0x41 => :dust,
14
+ 0x42 => :insufficientfee,
15
+ 0x43 => :checkpoint,
16
+ }
17
+
18
+ def self.parse(payload)
19
+ message, payload = Bitcoin::Protocol.unpack_var_string(payload)
20
+ ccode, payload = payload.unpack("Ca*")
21
+ reason, payload = Bitcoin::Protocol.unpack_var_string(payload)
22
+ data = payload
23
+
24
+ code = CCODE_TABLE[ccode] || ccode
25
+ new(message, code, reason, data)
26
+ end
27
+
28
+ def tx_hash
29
+ message == "tx" && self[:data].reverse.bth
30
+ end
31
+
32
+ def block_hash
33
+ message == "block" && self[:data].reverse.bth
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+
5
+ module Protocol
6
+
7
+ class ScriptWitness
8
+
9
+ # witness stack
10
+ attr_reader :stack
11
+
12
+ def initialize
13
+ @stack = []
14
+ end
15
+
16
+ # check empty
17
+ def empty?
18
+ stack.empty?
19
+ end
20
+
21
+ # output script in raw binary format
22
+ def to_payload
23
+ payload = Bitcoin::Protocol.pack_var_int(stack.size)
24
+ payload << stack.map{|e| Bitcoin::Protocol.pack_var_int(e.bytesize) << e }.join
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,587 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'bitcoin/script'
4
+
5
+ module Bitcoin
6
+ module Protocol
7
+
8
+ class Tx
9
+
10
+ MARKER = 0
11
+ FLAG = 1
12
+
13
+ SIGHASH_TYPE = Script::SIGHASH_TYPE
14
+
15
+ # transaction hash
16
+ attr_reader :hash
17
+
18
+ # inputs (Array of TxIn)
19
+ attr_reader :in
20
+
21
+ # outputs (Array of TxOut)
22
+ attr_reader :out
23
+
24
+ # raw protocol payload
25
+ attr_reader :payload
26
+
27
+ # version (usually 1)
28
+ attr_accessor :ver
29
+
30
+ # lock time
31
+ attr_accessor :lock_time
32
+
33
+ # parsed / evaluated input scripts cached for later use
34
+ attr_reader :scripts
35
+
36
+ attr_accessor :marker
37
+ attr_accessor :flag
38
+
39
+ alias :inputs :in
40
+ alias :outputs :out
41
+
42
+ # compare to another tx
43
+ def ==(other)
44
+ @hash == other.hash
45
+ end
46
+
47
+ # return the tx hash in binary format
48
+ def binary_hash
49
+ @binary_hash ||= [@hash].pack("H*").reverse
50
+ end
51
+
52
+ # create tx from raw binary +data+
53
+ def initialize(data=nil)
54
+ @ver, @lock_time, @in, @out, @scripts = 1, 0, [], [], []
55
+ @enable_bitcoinconsensus = !!ENV['USE_BITCOINCONSENSUS']
56
+ parse_data_from_io(data) if data
57
+ end
58
+
59
+ # generate the tx hash for given +payload+ in hex format
60
+ def hash_from_payload(payload)
61
+ Digest::SHA256.digest(Digest::SHA256.digest( payload )).reverse_hth
62
+ end
63
+ alias generate_hash hash_from_payload
64
+
65
+ # refresh_hash recalculates the tx hash and sets it on the instance
66
+ def refresh_hash
67
+ @hash = generate_hash(to_old_payload)
68
+ end
69
+
70
+ # add an input
71
+ def add_in(input); (@in ||= []) << input; end
72
+
73
+ # add an output
74
+ def add_out(output); (@out ||= []) << output; end
75
+
76
+ # parse raw binary data
77
+ def parse_data_from_io(data)
78
+ buf = data.is_a?(String) ? StringIO.new(data) : data
79
+
80
+ @ver = buf.read(4).unpack("V")[0]
81
+
82
+ return false if buf.eof?
83
+
84
+ # segwit serialization format is defined by https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki
85
+ # Also note that it is impossible to parse 0 input transactions. Regular transactions with 0 inputs look
86
+ # like malformed segwit transactions.
87
+ @marker = buf.read(1).unpack('c').first
88
+ @flag = buf.read(1).unpack('c').first
89
+
90
+ witness = @marker == 0 && @flag != 0
91
+
92
+ # Non-segwit format does not contain marker or flag fields.
93
+ buf.seek(buf.pos - 2) unless witness
94
+
95
+ in_size = Protocol.unpack_var_int_from_io(buf)
96
+
97
+ @in = []
98
+ in_size.times{
99
+ break if buf.eof?
100
+ @in << TxIn.from_io(buf)
101
+ }
102
+
103
+ return false if buf.eof?
104
+
105
+ out_size = Protocol.unpack_var_int_from_io(buf)
106
+ @out = []
107
+ out_size.times{
108
+ break if buf.eof?
109
+ @out << TxOut.from_io(buf)
110
+ }
111
+
112
+ return false if buf.eof?
113
+
114
+ if witness
115
+ in_size.times do |i|
116
+ witness_count = Protocol.unpack_var_int_from_io(buf)
117
+ witness_count.times do
118
+ size = Protocol.unpack_var_int_from_io(buf)
119
+ @in[i].script_witness.stack << buf.read(size)
120
+ end
121
+ end
122
+ end
123
+
124
+ @lock_time = buf.read(4).unpack("V")[0]
125
+
126
+ @hash = hash_from_payload(to_old_payload)
127
+ @payload = to_payload
128
+
129
+ if buf.eof?
130
+ true
131
+ else
132
+ data.is_a?(StringIO) ? buf : buf.read
133
+ end
134
+ end
135
+
136
+ alias :parse_data :parse_data_from_io
137
+
138
+ # output transaction in raw binary format
139
+ def to_payload
140
+ witness? ? to_witness_payload : to_old_payload
141
+ end
142
+
143
+ def to_old_payload
144
+ pin = ""
145
+ @in.each{|input| pin << input.to_payload }
146
+ pout = ""
147
+ @out.each{|output| pout << output.to_payload }
148
+
149
+ [@ver].pack("V") << Protocol.pack_var_int(@in.size) << pin << Protocol.pack_var_int(@out.size) << pout << [@lock_time].pack("V")
150
+ end
151
+
152
+ # output transaction in raw binary format with witness
153
+ def to_witness_payload
154
+ buf = [@ver, MARKER, FLAG].pack('Vcc')
155
+ buf << Protocol.pack_var_int(@in.length) << @in.map(&:to_payload).join
156
+ buf << Protocol.pack_var_int(@out.length) << @out.map(&:to_payload).join
157
+ buf << witness_payload << [@lock_time].pack('V')
158
+ buf
159
+ end
160
+
161
+ def witness_payload
162
+ @in.map { |i| i.script_witness.to_payload }.join
163
+ end
164
+
165
+ def witness?
166
+ !@in.find { |i| !i.script_witness.empty? }.nil?
167
+ end
168
+
169
+ # generate a signature hash for input +input_idx+.
170
+ # either pass the +outpoint_tx+ or the +script_pubkey+ directly.
171
+ def signature_hash_for_input(input_idx, subscript, hash_type=nil, prev_out_value=nil, fork_id=nil)
172
+ # https://github.com/bitcoin/bitcoin/blob/e071a3f6c06f41068ad17134189a4ac3073ef76b/script.cpp#L834
173
+ # http://code.google.com/p/bitcoinj/source/browse/trunk/src/com/google/bitcoin/core/Script.java#318
174
+ # https://en.bitcoin.it/wiki/OP_CHECKSIG#How_it_works
175
+ # https://github.com/bitcoin/bitcoin/blob/c2e8c8acd8ae0c94c70b59f55169841ad195bb99/src/script.cpp#L1058
176
+ # https://en.bitcoin.it/wiki/OP_CHECKSIG
177
+
178
+ hash_type ||= SIGHASH_TYPE[:all]
179
+
180
+ # fork_id is optional and if set, SIGHASH_FORKID flag as defined by the
181
+ # Bitcoin Cash protocol will be respected.
182
+ #
183
+ # https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/doc/abc/replay-protected-sighash.md
184
+ if fork_id && (hash_type&SIGHASH_TYPE[:forkid]) != 0
185
+ raise "SIGHASH_FORKID is enabled, so prev_out_value is required" if prev_out_value.nil?
186
+
187
+ # According to the spec, we should modify the sighash by replacing the 24 most significant
188
+ # bits with the fork ID. However, Bitcoin ABC does not currently implement this since the
189
+ # fork_id is an implicit 0 and it would make the sighash JSON tests fail. Will leave as a
190
+ # TODO for now.
191
+ raise NotImplementedError, "fork_id must be 0" unless fork_id == 0
192
+
193
+ script_code = Bitcoin::Protocol.pack_var_string(subscript)
194
+ return signature_hash_for_input_bip143(input_idx, script_code, prev_out_value, hash_type)
195
+ end
196
+
197
+ # Note: BitcoinQT checks if input_idx >= @in.size and returns 1 with an error message.
198
+ # But this check is never actually useful because BitcoinQT would crash
199
+ # right before VerifyScript if input index is out of bounds (inside CScriptCheck::operator()()).
200
+ # That's why we don't need to do such a check here.
201
+ #
202
+ # However, if you look at the case SIGHASH_TYPE[:single] below, we must
203
+ # return 1 because it's possible to have more inputs than outputs and BitcoinQT returns 1 as well.
204
+ return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range
205
+
206
+ pin = @in.map.with_index{|input,idx|
207
+ if idx == input_idx
208
+ subscript = subscript.out[ input.prev_out_index ].script if subscript.respond_to?(:out) # legacy api (outpoint_tx)
209
+
210
+ # Remove all instances of OP_CODESEPARATOR from the script.
211
+ parsed_subscript = Script.new(subscript)
212
+ parsed_subscript.chunks.delete(Script::OP_CODESEPARATOR)
213
+ subscript = parsed_subscript.to_binary
214
+
215
+ input.to_payload(subscript)
216
+ else
217
+ case (hash_type & 0x1f)
218
+ when SIGHASH_TYPE[:none]; input.to_payload("", "\x00\x00\x00\x00")
219
+ when SIGHASH_TYPE[:single]; input.to_payload("", "\x00\x00\x00\x00")
220
+ else; input.to_payload("")
221
+ end
222
+ end
223
+ }
224
+
225
+ pout = @out.map(&:to_payload)
226
+ in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(@out.size)
227
+
228
+ case (hash_type & 0x1f)
229
+ when SIGHASH_TYPE[:none]
230
+ pout = ""
231
+ out_size = Protocol.pack_var_int(0)
232
+ when SIGHASH_TYPE[:single]
233
+ return "\x01".ljust(32, "\x00") if input_idx >= @out.size # ERROR: SignatureHash() : input_idx=%d out of range (SIGHASH_SINGLE)
234
+ pout = @out[0...(input_idx+1)].map.with_index{|out,idx| (idx==input_idx) ? out.to_payload : out.to_null_payload }.join
235
+ out_size = Protocol.pack_var_int(input_idx+1)
236
+ end
237
+
238
+ if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
239
+ in_size, pin = Protocol.pack_var_int(1), [ pin[input_idx] ]
240
+ end
241
+
242
+ buf = [ [@ver].pack("V"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("VV") ].join
243
+ Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
244
+ end
245
+
246
+ # generate a witness signature hash for input +input_idx+.
247
+ # https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
248
+ def signature_hash_for_witness_input(input_idx, witness_program, prev_out_value, witness_script = nil, hash_type=nil, skip_separator_index = 0)
249
+ return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range
250
+
251
+ hash_type ||= SIGHASH_TYPE[:all]
252
+
253
+ script = Bitcoin::Script.new(witness_program)
254
+ raise "ScriptPubkey does not contain witness program." unless script.is_witness?
255
+
256
+ if script.is_witness_v0_keyhash?
257
+ script_code = [["1976a914", script.get_hash160, "88ac"].join].pack("H*")
258
+ elsif script.is_witness_v0_scripthash?
259
+ raise "witness script does not match script pubkey" unless Bitcoin::Script.to_witness_p2sh_script(Digest::SHA256.digest(witness_script).bth) == witness_program
260
+ script = skip_separator_index > 0 ? Bitcoin::Script.new(witness_script).subscript_codeseparator(skip_separator_index) : witness_script
261
+ script_code = Bitcoin::Protocol.pack_var_string(script)
262
+ end
263
+
264
+ signature_hash_for_input_bip143(input_idx, script_code, prev_out_value, hash_type)
265
+ end
266
+
267
+ # verify input signature +in_idx+ against the corresponding
268
+ # output in +outpoint_tx+
269
+ # outpoint. This arg can also be a Script or TxOut.
270
+ #
271
+ # options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc, fork_id
272
+ def verify_input_signature(in_idx, outpoint_data, block_timestamp=Time.now.to_i, opts={})
273
+ if @enable_bitcoinconsensus
274
+ return bitcoinconsensus_verify_script(in_idx, outpoint_data, block_timestamp, opts)
275
+ end
276
+
277
+ # If FORKID is enabled, we also ensure strict encoding.
278
+ opts[:verify_strictenc] ||= !!opts[:fork_id]
279
+
280
+ outpoint_idx = @in[in_idx].prev_out_index
281
+ script_sig = @in[in_idx].script_sig
282
+
283
+ amount = amount_from_outpoint_data(outpoint_data, outpoint_idx)
284
+ script_pubkey = script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
285
+
286
+ if opts[:fork_id] && amount.nil?
287
+ raise "verify_input_signature must be called with a previous transaction or " \
288
+ "transaction output if SIGHASH_FORKID is enabled"
289
+ end
290
+
291
+ @scripts[in_idx] = Bitcoin::Script.new(script_sig, script_pubkey)
292
+ return false if opts[:verify_sigpushonly] && !@scripts[in_idx].is_push_only?(script_sig)
293
+ return false if opts[:verify_minimaldata] && !@scripts[in_idx].pushes_are_canonical?
294
+ sig_valid = @scripts[in_idx].run(block_timestamp, opts) do |pubkey,sig,hash_type,subscript|
295
+ hash = signature_hash_for_input(in_idx, subscript, hash_type, amount, opts[:fork_id])
296
+ Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] )
297
+ end
298
+ # BIP62 rule #6
299
+ return false if opts[:verify_cleanstack] && !@scripts[in_idx].stack.empty?
300
+
301
+ return sig_valid
302
+ end
303
+
304
+ # verify witness input signature +in_idx+ against the corresponding
305
+ # output in +outpoint_tx+
306
+ # outpoint. This arg can also be a Script or TxOut
307
+ #
308
+ # options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc
309
+ def verify_witness_input_signature(in_idx, outpoint_data, prev_out_amount, block_timestamp=Time.now.to_i, opts={})
310
+ if @enable_bitcoinconsensus
311
+ return bitcoinconsensus_verify_script(in_idx, outpoint_data, block_timestamp, opts)
312
+ end
313
+
314
+ outpoint_idx = @in[in_idx].prev_out_index
315
+ script_sig = ''
316
+
317
+ script_pubkey = script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
318
+ script_pubkey = Bitcoin::Script.new(script_pubkey)
319
+
320
+ if script_pubkey.is_p2sh?
321
+ redeem_script = Bitcoin::Script.new(@in[in_idx].script_sig).get_pubkey
322
+ script_pubkey = Bitcoin::Script.new(redeem_script.htb) if Bitcoin.hash160(redeem_script) == script_pubkey.get_hash160 # P2SH-P2WPKH or P2SH-P2WSH
323
+ end
324
+
325
+ @in[in_idx].script_witness.stack.each{|s|script_sig << Bitcoin::Script.pack_pushdata(s)}
326
+ code_separator_index = 0
327
+
328
+ if script_pubkey.is_witness_v0_keyhash? # P2WPKH
329
+ @scripts[in_idx] = Bitcoin::Script.new(script_sig, Bitcoin::Script.to_hash160_script(script_pubkey.get_hash160))
330
+ elsif script_pubkey.is_witness_v0_scripthash? # P2WSH
331
+ witness_hex = @in[in_idx].script_witness.stack.last.bth
332
+ witness_script = Bitcoin::Script.new(witness_hex.htb)
333
+ return false unless Bitcoin.sha256(witness_hex) == script_pubkey.get_hash160
334
+ @scripts[in_idx] = Bitcoin::Script.new(script_sig, Bitcoin::Script.to_p2sh_script(Bitcoin.hash160(witness_hex)))
335
+ else
336
+ return false
337
+ end
338
+
339
+ return false if opts[:verify_sigpushonly] && !@scripts[in_idx].is_push_only?(script_sig)
340
+ return false if opts[:verify_minimaldata] && !@scripts[in_idx].pushes_are_canonical?
341
+ sig_valid = @scripts[in_idx].run(block_timestamp, opts) do |pubkey,sig,hash_type,subscript|
342
+ if script_pubkey.is_witness_v0_keyhash?
343
+ hash = signature_hash_for_witness_input(in_idx, script_pubkey.to_payload, prev_out_amount, nil, hash_type)
344
+ elsif script_pubkey.is_witness_v0_scripthash?
345
+ hash = signature_hash_for_witness_input(in_idx, script_pubkey.to_payload, prev_out_amount, witness_hex.htb, hash_type, code_separator_index)
346
+ code_separator_index += 1 if witness_script.codeseparator_count > code_separator_index
347
+ end
348
+ Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] )
349
+ end
350
+ # BIP62 rule #6
351
+ return false if opts[:verify_cleanstack] && !@scripts[in_idx].stack.empty?
352
+
353
+ return sig_valid
354
+ end
355
+
356
+ def bitcoinconsensus_verify_script(in_idx, outpoint_data, block_timestamp=Time.now.to_i, opts={})
357
+ raise "Bitcoin::BitcoinConsensus shared library not found" unless Bitcoin::BitcoinConsensus.lib_available?
358
+
359
+ outpoint_idx = @in[in_idx].prev_out_index
360
+ script_pubkey = script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
361
+
362
+ flags = Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_NONE
363
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_P2SH if block_timestamp >= 1333238400
364
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_SIGPUSHONLY if opts[:verify_sigpushonly]
365
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_MINIMALDATA if opts[:verify_minimaldata]
366
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_CLEANSTACK if opts[:verify_cleanstack]
367
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_LOW_S if opts[:verify_low_s]
368
+
369
+ payload ||= to_payload
370
+ Bitcoin::BitcoinConsensus.verify_script(in_idx, script_pubkey, payload, flags)
371
+ end
372
+
373
+ # convert to ruby hash (see also #from_hash)
374
+ def to_hash(options = {})
375
+ @hash ||= hash_from_payload(to_old_payload)
376
+ h = {
377
+ 'hash' => @hash, 'ver' => @ver, # 'nid' => normalized_hash,
378
+ 'vin_sz' => @in.size, 'vout_sz' => @out.size,
379
+ 'lock_time' => @lock_time, 'size' => (@payload ||= to_payload).bytesize,
380
+ 'in' => @in.map{|i|i.to_hash(options)},
381
+ 'out' => @out.map{|o| o.to_hash(options) }
382
+ }
383
+ h['nid'] = normalized_hash if options[:with_nid]
384
+ h
385
+ end
386
+
387
+ # generates rawblock json as seen in the block explorer.
388
+ def to_json(options = {:space => ''}, *a)
389
+ JSON.pretty_generate( to_hash(options), options )
390
+ end
391
+
392
+ # write json representation to a file
393
+ # (see also #to_json)
394
+ def to_json_file(path)
395
+ File.open(path, 'wb'){|f| f.print to_json; }
396
+ end
397
+
398
+ # parse ruby hash (see also #to_hash)
399
+ def self.from_hash(h, do_raise=true)
400
+ tx = new(nil)
401
+ tx.ver, tx.lock_time = (h['ver'] || h['version']), h['lock_time']
402
+ ins = h['in'] || h['inputs']
403
+ outs = h['out'] || h['outputs']
404
+ ins .each{|input|
405
+ tx.add_in(TxIn.from_hash(input))
406
+ }
407
+ outs.each{|output| tx.add_out TxOut.from_hash(output) }
408
+ tx.instance_eval{
409
+ @hash = hash_from_payload(to_old_payload)
410
+ @payload = to_payload
411
+ }
412
+ if h['hash'] && (h['hash'] != tx.hash)
413
+ raise "Tx hash mismatch! Claimed: #{h['hash']}, Actual: #{tx.hash}" if do_raise
414
+ end
415
+ tx
416
+ end
417
+
418
+ # convert ruby hash to raw binary
419
+ def self.binary_from_hash(h)
420
+ tx = from_hash(h)
421
+ tx.to_payload
422
+ end
423
+
424
+ # parse json representation
425
+ def self.from_json(json_string); from_hash( JSON.load(json_string) ); end
426
+
427
+ # convert json representation to raw binary
428
+ def self.binary_from_json(json_string); from_json(json_string).to_payload; end
429
+
430
+ # read binary block from a file
431
+ def self.from_file(path); new( Bitcoin::Protocol.read_binary_file(path) ); end
432
+
433
+ # read json block from a file
434
+ def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
435
+
436
+ def size
437
+ payload.bytesize
438
+ end
439
+
440
+ # Checks if transaction is final taking into account height and time
441
+ # of a block in which it is located (or about to be included if it's unconfirmed tx).
442
+ def is_final?(block_height, block_time)
443
+ # No time lock - tx is final.
444
+ return true if lock_time == 0
445
+
446
+ # Time based nLockTime implemented in 0.1.6
447
+ # If lock_time is below the magic threshold treat it as a block height.
448
+ # If lock_time is above the threshold, it's a unix timestamp.
449
+ return true if lock_time < (lock_time < Bitcoin::LOCKTIME_THRESHOLD ? block_height : block_time)
450
+
451
+ inputs.each{|input| return false if !input.is_final? }
452
+
453
+ return true
454
+ end
455
+
456
+ def legacy_sigops_count
457
+ # Note: input scripts normally never have any opcodes since every input script
458
+ # can be statically reduced to a pushdata-only script.
459
+ # However, anyone is allowed to create a non-standard transaction with any opcodes in the inputs.
460
+ count = 0
461
+ self.in.each do |txin|
462
+ count += Bitcoin::Script.new(txin.script_sig).sigops_count_accurate(false)
463
+ end
464
+ self.out.each do |txout|
465
+ count += Bitcoin::Script.new(txout.pk_script).sigops_count_accurate(false)
466
+ end
467
+ count
468
+ end
469
+
470
+ DEFAULT_BLOCK_PRIORITY_SIZE = 27000
471
+
472
+ def minimum_relay_fee; calculate_minimum_fee(allow_free=true, :relay); end
473
+ def minimum_block_fee; calculate_minimum_fee(allow_free=true, :block); end
474
+
475
+ def calculate_minimum_fee(allow_free=true, mode=:block)
476
+ # Base fee is either nMinTxFee or nMinRelayTxFee
477
+ base_fee = (mode == :relay) ? Bitcoin.network[:min_relay_tx_fee] : Bitcoin.network[:min_tx_fee]
478
+ tx_size = to_payload.bytesize
479
+ min_fee = (1 + tx_size / 1_000) * base_fee
480
+
481
+ if allow_free
482
+ # There is a free transaction area in blocks created by most miners,
483
+ # * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000
484
+ # to be considered to fall into this category. We don't want to encourage sending
485
+ # multiple transactions instead of one big transaction to avoid fees.
486
+ # * If we are creating a transaction we allow transactions up to 1,000 bytes
487
+ # to be considered safe and assume they can likely make it into this section.
488
+ min_fee = 0 if tx_size < (mode == :block ? Bitcoin.network[:free_tx_bytes] : DEFAULT_BLOCK_PRIORITY_SIZE - 1_000)
489
+ end
490
+
491
+ # This code can be removed after enough miners have upgraded to version 0.9.
492
+ # Until then, be safe when sending and require a fee if any output is less than CENT
493
+ if min_fee < base_fee && mode == :block
494
+ outputs.each do |output|
495
+ if output.value < Bitcoin.network[:dust]
496
+ # If per dust fee, then we add min fee for each output less than dust.
497
+ # Otherwise, we set to min fee if there is any output less than dust.
498
+ if Bitcoin.network[:per_dust_fee]
499
+ min_fee += base_fee
500
+ else
501
+ min_fee = base_fee
502
+ break
503
+ end
504
+ end
505
+ end
506
+ end
507
+
508
+ min_fee = Bitcoin::network[:max_money] unless min_fee.between?(0, Bitcoin::network[:max_money])
509
+ min_fee
510
+ end
511
+
512
+ def is_coinbase?
513
+ inputs.size == 1 and inputs.first.coinbase?
514
+ end
515
+
516
+ def normalized_hash
517
+ signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).reverse.hth
518
+ end
519
+ alias :nhash :normalized_hash
520
+
521
+ # get witness hash
522
+ def witness_hash
523
+ hash_from_payload(to_witness_payload)
524
+ end
525
+
526
+ # sort transaction inputs and outputs under BIP 69
527
+ # https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki
528
+ def lexicographical_sort!
529
+ inputs.sort_by!{|i| [i.previous_output, i.prev_out_index]}
530
+ outputs.sort_by!{|o| [o.amount, o.pk_script.bth]}
531
+ end
532
+
533
+ private
534
+
535
+ def signature_hash_for_input_bip143(input_idx, script_code, prev_out_value, hash_type)
536
+ hash_prevouts = Digest::SHA256.digest(Digest::SHA256.digest(@in.map{|i| [i.prev_out_hash, i.prev_out_index].pack("a32V")}.join))
537
+ hash_sequence = Digest::SHA256.digest(Digest::SHA256.digest(@in.map{|i|i.sequence}.join))
538
+ outpoint = [@in[input_idx].prev_out_hash, @in[input_idx].prev_out_index].pack("a32V")
539
+ amount = [prev_out_value].pack("Q")
540
+ nsequence = @in[input_idx].sequence
541
+
542
+ hash_outputs = Digest::SHA256.digest(Digest::SHA256.digest(@out.map{|o|o.to_payload}.join))
543
+
544
+ case (hash_type & 0x1f)
545
+ when SIGHASH_TYPE[:single]
546
+ hash_outputs = input_idx >= @out.size ? "\x00".ljust(32, "\x00") : Digest::SHA256.digest(Digest::SHA256.digest(@out[input_idx].to_payload))
547
+ hash_sequence = "\x00".ljust(32, "\x00")
548
+ when SIGHASH_TYPE[:none]
549
+ hash_sequence = hash_outputs = "\x00".ljust(32, "\x00")
550
+ end
551
+
552
+ if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
553
+ hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
554
+ end
555
+
556
+ buf = [ [@ver].pack("V"), hash_prevouts, hash_sequence, outpoint,
557
+ script_code, amount, nsequence, hash_outputs, [@lock_time, hash_type].pack("VV")].join
558
+
559
+ Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
560
+ end
561
+
562
+ def script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
563
+ if outpoint_data.respond_to?(:out)
564
+ # If given an entire previous transaction, take the script from it
565
+ outpoint_data.out[outpoint_idx].pk_script
566
+ elsif outpoint_data.respond_to?(:pk_script)
567
+ # If given an transaction output, take the script
568
+ outpoint_data.pk_script
569
+ else
570
+ # Otherwise, we assume it's already a script.
571
+ outpoint_data
572
+ end
573
+ end
574
+
575
+ def amount_from_outpoint_data(outpoint_data, outpoint_idx)
576
+ if outpoint_data.respond_to?(:out)
577
+ # If given an entire previous transaction, take the amount from the
578
+ # output at the outpoint_idx
579
+ outpoint_data.out[outpoint_idx].amount
580
+ elsif outpoint_data.respond_to?(:pk_script)
581
+ # If given an transaction output, take the amount
582
+ outpoint_data.amount
583
+ end
584
+ end
585
+ end
586
+ end
587
+ end