monacoin-ruby 0.1.2 → 0.1.3

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.
@@ -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