bitcoin-ruby 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +2 -7
- data/COPYING +1 -1
- data/Gemfile +2 -6
- data/Gemfile.lock +34 -0
- data/README.rdoc +16 -68
- data/Rakefile +3 -6
- data/bin/bitcoin_shell +0 -1
- data/{concept-examples/blockchain-pow.rb → examples/concept-blockchain-pow.rb} +0 -0
- data/lib/bitcoin.rb +350 -296
- data/lib/bitcoin/builder.rb +3 -1
- data/lib/bitcoin/connection.rb +2 -1
- data/lib/bitcoin/contracthash.rb +76 -0
- data/lib/bitcoin/dogecoin.rb +97 -0
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +74 -0
- data/lib/bitcoin/ffi/openssl.rb +98 -2
- data/lib/bitcoin/ffi/secp256k1.rb +144 -0
- data/lib/bitcoin/key.rb +12 -2
- data/lib/bitcoin/logger.rb +3 -12
- data/lib/bitcoin/protocol/block.rb +3 -9
- data/lib/bitcoin/protocol/parser.rb +6 -2
- data/lib/bitcoin/protocol/tx.rb +44 -13
- data/lib/bitcoin/protocol/txin.rb +4 -2
- data/lib/bitcoin/protocol/txout.rb +2 -2
- data/lib/bitcoin/script.rb +212 -37
- data/lib/bitcoin/trezor/mnemonic.rb +130 -0
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +32 -3
- data/spec/bitcoin/builder_spec.rb +18 -0
- data/spec/bitcoin/contracthash_spec.rb +45 -0
- data/spec/bitcoin/dogecoin_spec.rb +176 -0
- data/spec/bitcoin/ffi_openssl.rb +45 -0
- data/spec/bitcoin/fixtures/156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json +24 -0
- data/spec/bitcoin/fixtures/8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json +24 -0
- data/spec/bitcoin/fixtures/coinbase-toshi.json +33 -0
- data/spec/bitcoin/fixtures/coinbase.json +24 -0
- data/spec/bitcoin/fixtures/dogecoin-block-60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-01-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-02-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-03-toshi.json +73 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-6f0bbdd4e71a8af4305018d738184df32dbb6f27284fdebd5b56d16947f7c181.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-a7c9b06e275e8674cc19a5f7d3e557c72c6d93576e635b33212dbe08ab7cdb60.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin +0 -0
- data/spec/bitcoin/protocol/block_spec.rb +0 -22
- data/spec/bitcoin/protocol/tx_spec.rb +145 -2
- data/spec/bitcoin/script/script_spec.rb +282 -0
- data/spec/bitcoin/secp256k1_spec.rb +48 -0
- data/spec/bitcoin/spec_helper.rb +0 -51
- data/spec/bitcoin/trezor/mnemonic_spec.rb +161 -0
- metadata +48 -98
- data/bin/bitcoin_dns_seed +0 -130
- data/bin/bitcoin_gui +0 -80
- data/bin/bitcoin_node +0 -153
- data/bin/bitcoin_node_cli +0 -81
- data/bin/bitcoin_wallet +0 -402
- data/doc/CONFIG.rdoc +0 -66
- data/doc/EXAMPLES.rdoc +0 -13
- data/doc/NAMECOIN.rdoc +0 -34
- data/doc/NODE.rdoc +0 -225
- data/doc/STORAGE.rdoc +0 -33
- data/doc/WALLET.rdoc +0 -102
- data/examples/balance.rb +0 -66
- data/examples/forwarder.rb +0 -73
- data/examples/index_nhash.rb +0 -24
- data/examples/reindex_p2sh_addrs.rb +0 -44
- data/examples/relay_tx.rb +0 -22
- data/examples/verify_tx.rb +0 -57
- data/lib/bitcoin/config.rb +0 -58
- data/lib/bitcoin/gui/addr_view.rb +0 -44
- data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
- data/lib/bitcoin/gui/bitcoin-ruby.svg +0 -80
- data/lib/bitcoin/gui/conn_view.rb +0 -38
- data/lib/bitcoin/gui/connection.rb +0 -70
- data/lib/bitcoin/gui/em_gtk.rb +0 -30
- data/lib/bitcoin/gui/gui.builder +0 -1643
- data/lib/bitcoin/gui/gui.rb +0 -292
- data/lib/bitcoin/gui/helpers.rb +0 -115
- data/lib/bitcoin/gui/tree_view.rb +0 -84
- data/lib/bitcoin/gui/tx_view.rb +0 -69
- data/lib/bitcoin/namecoin.rb +0 -280
- data/lib/bitcoin/network/command_client.rb +0 -104
- data/lib/bitcoin/network/command_handler.rb +0 -570
- data/lib/bitcoin/network/connection_handler.rb +0 -387
- data/lib/bitcoin/network/node.rb +0 -565
- data/lib/bitcoin/storage/dummy/dummy_store.rb +0 -179
- data/lib/bitcoin/storage/models.rb +0 -171
- data/lib/bitcoin/storage/sequel/migrations.rb +0 -99
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +0 -45
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +0 -14
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +0 -16
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +0 -56
- data/lib/bitcoin/storage/sequel/sequel_store.rb +0 -551
- data/lib/bitcoin/storage/storage.rb +0 -517
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +0 -18
- data/lib/bitcoin/storage/utxo/migrations/003_update_indices.rb +0 -14
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +0 -14
- data/lib/bitcoin/storage/utxo/utxo_store.rb +0 -374
- data/lib/bitcoin/validation.rb +0 -400
- data/lib/bitcoin/wallet/coinselector.rb +0 -33
- data/lib/bitcoin/wallet/keygenerator.rb +0 -77
- data/lib/bitcoin/wallet/keystore.rb +0 -207
- data/lib/bitcoin/wallet/txdp.rb +0 -118
- data/lib/bitcoin/wallet/wallet.rb +0 -281
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +0 -43
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +0 -67
- data/spec/bitcoin/namecoin_spec.rb +0 -182
- data/spec/bitcoin/node/command_api_spec.rb +0 -663
- data/spec/bitcoin/storage/models_spec.rb +0 -104
- data/spec/bitcoin/storage/reorg_spec.rb +0 -236
- data/spec/bitcoin/storage/storage_spec.rb +0 -387
- data/spec/bitcoin/storage/validation_spec.rb +0 -300
- data/spec/bitcoin/wallet/coinselector_spec.rb +0 -38
- data/spec/bitcoin/wallet/keygenerator_spec.rb +0 -69
- data/spec/bitcoin/wallet/keystore_spec.rb +0 -190
- data/spec/bitcoin/wallet/txdp_spec.rb +0 -76
- data/spec/bitcoin/wallet/wallet_spec.rb +0 -238
data/lib/bitcoin/key.rb
CHANGED
@@ -98,7 +98,12 @@ module Bitcoin
|
|
98
98
|
# key1 = Bitcoin::Key.generate
|
99
99
|
# sig = key1.sign("some data")
|
100
100
|
def sign(data)
|
101
|
-
@key.dsa_sign_asn1(data)
|
101
|
+
sig = @key.dsa_sign_asn1(data)
|
102
|
+
if Script::is_low_der_signature?(sig)
|
103
|
+
sig
|
104
|
+
else
|
105
|
+
Bitcoin::OpenSSL_EC.signature_to_low_s(sig)
|
106
|
+
end
|
102
107
|
end
|
103
108
|
|
104
109
|
# Verify signature +sig+ for +data+.
|
@@ -106,7 +111,12 @@ module Bitcoin
|
|
106
111
|
# key2.verify("some data", sig)
|
107
112
|
def verify(data, sig)
|
108
113
|
regenerate_pubkey unless @key.public_key
|
109
|
-
|
114
|
+
sig = Bitcoin::OpenSSL_EC.repack_der_signature(sig)
|
115
|
+
if sig
|
116
|
+
@key.dsa_verify_asn1(data, sig)
|
117
|
+
else
|
118
|
+
false
|
119
|
+
end
|
110
120
|
end
|
111
121
|
|
112
122
|
|
data/lib/bitcoin/logger.rb
CHANGED
@@ -1,17 +1,8 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
def level= l = 0
|
7
|
-
_level = l.is_a?(Fixnum) ? l : Log4r::LNAMES.index(l.to_s.upcase)
|
8
|
-
Log4r::Log4rTools.validate_level(_level)
|
9
|
-
@level = _level
|
10
|
-
LoggerFactory.define_methods(self)
|
11
|
-
Log4r::Logger.log_internal {"Logger '#{@fullname}' set to #{LNAMES[@level]}"}
|
12
|
-
@level
|
13
|
-
end
|
14
|
-
end
|
3
|
+
begin
|
4
|
+
require 'log4r'
|
5
|
+
rescue LoadError
|
15
6
|
end
|
16
7
|
|
17
8
|
module Bitcoin
|
@@ -56,7 +56,7 @@ module Bitcoin
|
|
56
56
|
end
|
57
57
|
|
58
58
|
# create block from raw binary +data+
|
59
|
-
def initialize(data)
|
59
|
+
def initialize(data=nil)
|
60
60
|
@tx = []
|
61
61
|
parse_data_from_io(data) if data
|
62
62
|
end
|
@@ -73,7 +73,7 @@ module Bitcoin
|
|
73
73
|
@ver, @prev_block, @mrkl_root, @time, @bits, @nonce = buf.read(80).unpack("Va32a32VVV")
|
74
74
|
recalc_block_hash
|
75
75
|
|
76
|
-
if Bitcoin.network[:
|
76
|
+
if Bitcoin.network[:auxpow_chain_id] != nil && (@ver & BLOCK_VERSION_AUXPOW) > 0
|
77
77
|
@aux_pow = AuxPow.new(nil)
|
78
78
|
@aux_pow.parse_data_from_io(buf)
|
79
79
|
end
|
@@ -197,7 +197,7 @@ module Bitcoin
|
|
197
197
|
end
|
198
198
|
@aux_pow = AuxPow.from_hash(h['aux_pow']) if h['aux_pow']
|
199
199
|
h['tx'].each{|tx| @tx << Tx.from_hash(tx) }
|
200
|
-
if h['tx'].any?
|
200
|
+
if h['tx'].any?
|
201
201
|
(raise "Block merkle root mismatch! Block: #{h['hash']}" unless verify_mrkl_root) if do_raise
|
202
202
|
end
|
203
203
|
}
|
@@ -231,12 +231,6 @@ module Bitcoin
|
|
231
231
|
# read json block from a file
|
232
232
|
def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
|
233
233
|
|
234
|
-
# Get a Bitcoin::Validation object to validate this block. It needs a +store+
|
235
|
-
# to validate against, and optionally takes the +prev_block+ for optimization.
|
236
|
-
def validator(store, prev_block = nil)
|
237
|
-
@validator ||= Bitcoin::Validation::Block.new(self, store, prev_block)
|
238
|
-
end
|
239
|
-
|
240
234
|
# get the (statistical) amount of work that was needed to generate this block.
|
241
235
|
def block_work
|
242
236
|
target = Bitcoin.decode_compact_bits(@bits).to_i(16)
|
@@ -23,7 +23,7 @@ module Bitcoin
|
|
23
23
|
#
|
24
24
|
def parse_inv(payload, type=:put)
|
25
25
|
count, payload = Protocol.unpack_var_int(payload)
|
26
|
-
payload.each_byte.each_slice(36){|i|
|
26
|
+
payload.each_byte.each_slice(36).with_index{|i, idx|
|
27
27
|
hash = i[4..-1].reverse.pack("C32")
|
28
28
|
case i[0]
|
29
29
|
when 1
|
@@ -34,7 +34,11 @@ module Bitcoin
|
|
34
34
|
end
|
35
35
|
when 2
|
36
36
|
if type == :put
|
37
|
-
@h.
|
37
|
+
if @h.respond_to?(:on_inv_block_v2)
|
38
|
+
@h.on_inv_block_v2(hash, idx, count)
|
39
|
+
else
|
40
|
+
@h.on_inv_block(hash)
|
41
|
+
end
|
38
42
|
else
|
39
43
|
@h.on_get_block(hash)
|
40
44
|
end
|
data/lib/bitcoin/protocol/tx.rb
CHANGED
@@ -44,6 +44,7 @@ module Bitcoin
|
|
44
44
|
# create tx from raw binary +data+
|
45
45
|
def initialize(data=nil)
|
46
46
|
@ver, @lock_time, @in, @out, @scripts = 1, 0, [], [], []
|
47
|
+
@enable_bitcoinconsensus = !!ENV['USE_BITCOINCONSENSUS']
|
47
48
|
parse_data_from_io(data) if data
|
48
49
|
end
|
49
50
|
|
@@ -160,7 +161,13 @@ module Bitcoin
|
|
160
161
|
# verify input signature +in_idx+ against the corresponding
|
161
162
|
# output in +outpoint_tx+
|
162
163
|
# outpoint
|
163
|
-
|
164
|
+
#
|
165
|
+
# options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc
|
166
|
+
def verify_input_signature(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i, opts={})
|
167
|
+
if @enable_bitcoinconsensus
|
168
|
+
return bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp, opts)
|
169
|
+
end
|
170
|
+
|
164
171
|
outpoint_idx = @in[in_idx].prev_out_index
|
165
172
|
script_sig = @in[in_idx].script_sig
|
166
173
|
|
@@ -173,10 +180,39 @@ module Bitcoin
|
|
173
180
|
end
|
174
181
|
|
175
182
|
@scripts[in_idx] = Bitcoin::Script.new(script_sig, script_pubkey)
|
176
|
-
|
183
|
+
return false if opts[:verify_sigpushonly] && !@scripts[in_idx].is_push_only?(script_sig)
|
184
|
+
return false if opts[:verify_minimaldata] && !@scripts[in_idx].pushes_are_canonical?
|
185
|
+
sig_valid = @scripts[in_idx].run(block_timestamp, opts) do |pubkey,sig,hash_type,subscript|
|
177
186
|
hash = signature_hash_for_input(in_idx, subscript, hash_type)
|
178
187
|
Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] )
|
179
188
|
end
|
189
|
+
# BIP62 rule #6
|
190
|
+
return false if opts[:verify_cleanstack] && !@scripts[in_idx].stack.empty?
|
191
|
+
|
192
|
+
return sig_valid
|
193
|
+
end
|
194
|
+
|
195
|
+
def bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i, opts={})
|
196
|
+
raise "Bitcoin::BitcoinConsensus shared library not found" unless Bitcoin::BitcoinConsensus.lib_available?
|
197
|
+
|
198
|
+
# If given an entire previous transaction, take the script from it
|
199
|
+
script_pubkey = if outpoint_tx_or_script.respond_to?(:out)
|
200
|
+
outpoint_idx = @in[in_idx].prev_out_index
|
201
|
+
outpoint_tx_or_script.out[outpoint_idx].pk_script
|
202
|
+
else
|
203
|
+
# Otherwise, it's already a script.
|
204
|
+
outpoint_tx_or_script
|
205
|
+
end
|
206
|
+
|
207
|
+
flags = Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_NONE
|
208
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_P2SH if block_timestamp >= 1333238400
|
209
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_SIGPUSHONLY if opts[:verify_sigpushonly]
|
210
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_MINIMALDATA if opts[:verify_minimaldata]
|
211
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_CLEANSTACK if opts[:verify_cleanstack]
|
212
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_LOW_S if opts[:verify_low_s]
|
213
|
+
|
214
|
+
payload ||= to_payload
|
215
|
+
Bitcoin::BitcoinConsensus.verify_script(in_idx, script_pubkey, payload, flags)
|
180
216
|
end
|
181
217
|
|
182
218
|
# convert to ruby hash (see also #from_hash)
|
@@ -206,9 +242,11 @@ module Bitcoin
|
|
206
242
|
# parse ruby hash (see also #to_hash)
|
207
243
|
def self.from_hash(h)
|
208
244
|
tx = new(nil)
|
209
|
-
tx.ver, tx.lock_time =
|
210
|
-
h['in']
|
211
|
-
h['out']
|
245
|
+
tx.ver, tx.lock_time = (h['ver'] || h['version']), h['lock_time']
|
246
|
+
ins = h['in'] || h['inputs']
|
247
|
+
outs = h['out'] || h['outputs']
|
248
|
+
ins .each{|input| tx.add_in TxIn.from_hash(input) }
|
249
|
+
outs.each{|output| tx.add_out TxOut.from_hash(output) }
|
212
250
|
tx.instance_eval{ @hash = hash_from_payload(@payload = to_payload) }
|
213
251
|
tx
|
214
252
|
end
|
@@ -228,13 +266,6 @@ module Bitcoin
|
|
228
266
|
# read json block from a file
|
229
267
|
def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
|
230
268
|
|
231
|
-
# Get a Bitcoin::Validation object to validate this block. It needs a +store+
|
232
|
-
# to validate against, a block to validate tx chains inside one block, and
|
233
|
-
# optionally takes the +block_validator+ as an optimization.
|
234
|
-
def validator(store, block = nil, block_validator = nil)
|
235
|
-
@validator ||= Bitcoin::Validation::Tx.new(self, store, block, block_validator)
|
236
|
-
end
|
237
|
-
|
238
269
|
def size
|
239
270
|
payload.bytesize
|
240
271
|
end
|
@@ -316,7 +347,7 @@ module Bitcoin
|
|
316
347
|
end
|
317
348
|
|
318
349
|
def normalized_hash
|
319
|
-
signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).
|
350
|
+
signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).reverse.hth
|
320
351
|
end
|
321
352
|
alias :nhash :normalized_hash
|
322
353
|
|
@@ -85,11 +85,13 @@ module Bitcoin
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def self.from_hash(input)
|
88
|
-
|
88
|
+
previous_hash = input['previous_transaction_hash'] || input['prev_out']['hash']
|
89
|
+
previous_output_index = input['output_index'] || input['prev_out']['n']
|
90
|
+
txin = TxIn.new([ previous_hash ].pack('H*').reverse, previous_output_index)
|
89
91
|
if input['coinbase']
|
90
92
|
txin.script_sig = [ input['coinbase'] ].pack("H*")
|
91
93
|
else
|
92
|
-
txin.script_sig = Script.binary_from_string(input['scriptSig'])
|
94
|
+
txin.script_sig = Script.binary_from_string(input['scriptSig'] || input['script'])
|
93
95
|
end
|
94
96
|
txin.sequence = [ input['sequence'] || 0xffffffff ].pack("V")
|
95
97
|
txin
|
@@ -66,8 +66,8 @@ module Bitcoin
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def self.from_hash(output)
|
69
|
-
amount = output['value'].gsub('.','').to_i
|
70
|
-
script = Script.binary_from_string(output['scriptPubKey'])
|
69
|
+
amount = output['value'] ? output['value'].gsub('.','').to_i : output['amount']
|
70
|
+
script = Script.binary_from_string(output['scriptPubKey'] || output['script'])
|
71
71
|
new(amount, script)
|
72
72
|
end
|
73
73
|
|
data/lib/bitcoin/script.rb
CHANGED
@@ -152,24 +152,25 @@ class Bitcoin::Script
|
|
152
152
|
|
153
153
|
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
154
154
|
|
155
|
-
attr_reader :raw, :chunks, :debug
|
155
|
+
attr_reader :raw, :chunks, :debug, :stack
|
156
156
|
|
157
157
|
# create a new script. +bytes+ is typically input_script + output_script
|
158
158
|
def initialize(input_script, previous_output_script=nil)
|
159
159
|
@raw_byte_sizes = [input_script.bytesize, previous_output_script ? previous_output_script.bytesize : 0]
|
160
|
+
@input_script, @previous_output_script = input_script, previous_output_script
|
160
161
|
|
161
|
-
@raw = if previous_output_script
|
162
|
-
input_script + [ Bitcoin::Script::OP_CODESEPARATOR ].pack("C") + previous_output_script
|
162
|
+
@raw = if @previous_output_script
|
163
|
+
@input_script + [ Bitcoin::Script::OP_CODESEPARATOR ].pack("C") + @previous_output_script
|
163
164
|
else
|
164
|
-
input_script
|
165
|
+
@input_script
|
165
166
|
end
|
166
167
|
|
167
|
-
@chunks = parse(input_script)
|
168
|
+
@chunks = parse(@input_script)
|
168
169
|
|
169
170
|
if previous_output_script
|
170
171
|
@script_codeseparator_index = @chunks.size
|
171
172
|
@chunks << Bitcoin::Script::OP_CODESEPARATOR
|
172
|
-
@chunks += parse(previous_output_script)
|
173
|
+
@chunks += parse(@previous_output_script)
|
173
174
|
end
|
174
175
|
|
175
176
|
@stack, @stack_alt, @exec_stack = [], [], []
|
@@ -354,8 +355,12 @@ class Bitcoin::Script
|
|
354
355
|
end
|
355
356
|
|
356
357
|
# script object of a string representation
|
357
|
-
def self.from_string(
|
358
|
-
|
358
|
+
def self.from_string(input_script, previous_output_script=nil)
|
359
|
+
if previous_output_script
|
360
|
+
new(binary_from_string(input_script), binary_from_string(previous_output_script))
|
361
|
+
else
|
362
|
+
new(binary_from_string(input_script))
|
363
|
+
end
|
359
364
|
end
|
360
365
|
|
361
366
|
class ScriptOpcodeError < StandardError; end
|
@@ -396,7 +401,7 @@ class Bitcoin::Script
|
|
396
401
|
end
|
397
402
|
|
398
403
|
# run the script. +check_callback+ is called for OP_CHECKSIG operations
|
399
|
-
def run(block_timestamp=Time.now.to_i, &check_callback)
|
404
|
+
def run(block_timestamp=Time.now.to_i, opts={}, &check_callback)
|
400
405
|
return false if @parse_invalid
|
401
406
|
|
402
407
|
#p [to_string, block_timestamp, is_p2sh?]
|
@@ -404,7 +409,7 @@ class Bitcoin::Script
|
|
404
409
|
@last_codeseparator_index = 0
|
405
410
|
|
406
411
|
if block_timestamp >= 1333238400 # Pay to Script Hash (BIP 0016)
|
407
|
-
return pay_to_script_hash(check_callback) if is_p2sh?
|
412
|
+
return pay_to_script_hash(block_timestamp, opts, check_callback) if is_p2sh?
|
408
413
|
end
|
409
414
|
|
410
415
|
@debug = []
|
@@ -430,7 +435,17 @@ class Bitcoin::Script
|
|
430
435
|
when *OPCODES_METHOD.keys
|
431
436
|
m = method( n=OPCODES_METHOD[chunk] )
|
432
437
|
@debug << n.to_s.upcase
|
433
|
-
|
438
|
+
# invoke opcode method
|
439
|
+
case m.arity
|
440
|
+
when 0
|
441
|
+
m.call
|
442
|
+
when 1
|
443
|
+
m.call(check_callback)
|
444
|
+
when -2 # One fixed parameter, one optional
|
445
|
+
m.call(check_callback, opts)
|
446
|
+
else
|
447
|
+
puts "Bitcoin::Script: opcode #{name} method parameters invalid"
|
448
|
+
end
|
434
449
|
when *OP_2_16
|
435
450
|
@stack << OP_2_16.index(chunk) + 2
|
436
451
|
@debug << "OP_#{chunk-80}"
|
@@ -473,17 +488,19 @@ class Bitcoin::Script
|
|
473
488
|
# pay_to_script_hash: https://en.bitcoin.it/wiki/BIP_0016
|
474
489
|
#
|
475
490
|
# <sig> {<pub> OP_CHECKSIG} | OP_HASH160 <script_hash> OP_EQUAL
|
476
|
-
def pay_to_script_hash(check_callback)
|
491
|
+
def pay_to_script_hash(block_timestamp, opts, check_callback)
|
477
492
|
return false if @chunks.size < 4
|
478
493
|
*rest, script, _, script_hash, _ = @chunks
|
479
494
|
script = rest.pop if script == OP_CODESEPARATOR
|
480
495
|
script, script_hash = cast_to_string(script), cast_to_string(script_hash)
|
481
496
|
|
482
497
|
return false unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
|
498
|
+
return true if check_callback == :check
|
483
499
|
|
484
500
|
script = self.class.new(to_binary(rest) + script).inner_p2sh!(script)
|
485
|
-
result = script.run(&check_callback)
|
501
|
+
result = script.run(block_timestamp, opts, &check_callback)
|
486
502
|
@debug = script.debug
|
503
|
+
@stack = script.stack # Set the execution stack to match the redeem script, so checks on stack contents at end of script execution validate correctly
|
487
504
|
result
|
488
505
|
end
|
489
506
|
|
@@ -501,11 +518,23 @@ class Bitcoin::Script
|
|
501
518
|
script
|
502
519
|
end
|
503
520
|
|
521
|
+
# is this a :script_hash (pay-to-script-hash/p2sh) script?
|
504
522
|
def is_pay_to_script_hash?
|
505
523
|
return false if @inner_p2sh
|
506
|
-
|
507
|
-
|
508
|
-
|
524
|
+
if @previous_output_script
|
525
|
+
chunks = Bitcoin::Script.new(@previous_output_script).chunks
|
526
|
+
chunks.size == 3 &&
|
527
|
+
chunks[-3] == OP_HASH160 &&
|
528
|
+
chunks[-2].is_a?(String) && chunks[-2].bytesize == 20 &&
|
529
|
+
chunks[-1] == OP_EQUAL
|
530
|
+
else
|
531
|
+
@chunks.size >= 3 &&
|
532
|
+
@chunks[-3] == OP_HASH160 &&
|
533
|
+
@chunks[-2].is_a?(String) && @chunks[-2].bytesize == 20 &&
|
534
|
+
@chunks[-1] == OP_EQUAL &&
|
535
|
+
# make sure the script_sig matches the p2sh hash from the pk_script (if there is one)
|
536
|
+
(@chunks.size > 3 ? pay_to_script_hash(nil, nil, :check) : true)
|
537
|
+
end
|
509
538
|
end
|
510
539
|
alias :is_p2sh? :is_pay_to_script_hash?
|
511
540
|
|
@@ -540,6 +569,54 @@ class Bitcoin::Script
|
|
540
569
|
@chunks[0] == OP_RETURN && @chunks.size <= 2
|
541
570
|
end
|
542
571
|
|
572
|
+
# Verify the script is only pushing data onto the stack
|
573
|
+
def is_push_only?(script_data=nil)
|
574
|
+
check_pushes(push_only=true, canonical_only=false, (script_data||@input_script))
|
575
|
+
end
|
576
|
+
|
577
|
+
# Make sure opcodes used to push data match their intended length ranges
|
578
|
+
def pushes_are_canonical?(script_data=nil)
|
579
|
+
check_pushes(push_only=false, canonical_only=true, (script_data||@raw))
|
580
|
+
end
|
581
|
+
|
582
|
+
def check_pushes(push_only=true, canonical_only=false, buf)
|
583
|
+
program = buf.unpack("C*")
|
584
|
+
until program.empty?
|
585
|
+
opcode = program.shift
|
586
|
+
if opcode > OP_16
|
587
|
+
return false if push_only
|
588
|
+
next
|
589
|
+
end
|
590
|
+
if opcode < OP_PUSHDATA1 && opcode > OP_0
|
591
|
+
# Could have used an OP_n code, rather than a 1-byte push.
|
592
|
+
return false if canonical_only && opcode == 1 && program[0] <= 16
|
593
|
+
program.shift(opcode)
|
594
|
+
end
|
595
|
+
if opcode == OP_PUSHDATA1
|
596
|
+
len = program.shift(1)[0]
|
597
|
+
# Could have used a normal n-byte push, rather than OP_PUSHDATA1.
|
598
|
+
return false if canonical_only && len < OP_PUSHDATA1
|
599
|
+
program.shift(len)
|
600
|
+
end
|
601
|
+
if opcode == OP_PUSHDATA2
|
602
|
+
len = program.shift(2).pack("C*").unpack("v")[0]
|
603
|
+
# Could have used an OP_PUSHDATA1.
|
604
|
+
return false if canonical_only && len <= 0xff
|
605
|
+
program.shift(len)
|
606
|
+
end
|
607
|
+
if opcode == OP_PUSHDATA4
|
608
|
+
len = program.shift(4).pack("C*").unpack("V")[0]
|
609
|
+
# Could have used an OP_PUSHDATA2.
|
610
|
+
return false if canonical_only && len <= 0xffff
|
611
|
+
program.shift(len)
|
612
|
+
end
|
613
|
+
end
|
614
|
+
true
|
615
|
+
rescue => ex
|
616
|
+
# catch parsing errors
|
617
|
+
false
|
618
|
+
end
|
619
|
+
|
543
620
|
# get type of this tx
|
544
621
|
def type
|
545
622
|
if is_hash160?; :hash160
|
@@ -671,9 +748,8 @@ class Bitcoin::Script
|
|
671
748
|
# generate input script sig spending a pubkey output with given +signature+ and +pubkey+.
|
672
749
|
# returns a raw binary script sig of the form:
|
673
750
|
# <signature> [<pubkey>]
|
674
|
-
def self.to_pubkey_script_sig(signature, pubkey)
|
675
|
-
|
676
|
-
buf = pack_pushdata(signature + hash_type)
|
751
|
+
def self.to_pubkey_script_sig(signature, pubkey, hash_type = SIGHASH_TYPE[:all])
|
752
|
+
buf = pack_pushdata(signature + [hash_type].pack("C"))
|
677
753
|
return buf unless pubkey
|
678
754
|
|
679
755
|
expected_size = case pubkey[0]
|
@@ -704,8 +780,9 @@ class Bitcoin::Script
|
|
704
780
|
# returns a raw binary script sig of the form:
|
705
781
|
# OP_0 <sig> [<sig> ...]
|
706
782
|
def self.to_multisig_script_sig(*sigs)
|
783
|
+
hash_type = sigs.last.is_a?(Numeric) ? sigs.pop : SIGHASH_TYPE[:all]
|
707
784
|
partial_script = [OP_0].pack("C*")
|
708
|
-
sigs.reverse_each{ |sig| partial_script = add_sig_to_multisig_script_sig(sig, partial_script) }
|
785
|
+
sigs.reverse_each{ |sig| partial_script = add_sig_to_multisig_script_sig(sig, partial_script, hash_type) }
|
709
786
|
partial_script
|
710
787
|
end
|
711
788
|
|
@@ -713,8 +790,8 @@ class Bitcoin::Script
|
|
713
790
|
# another signature to it after the OP_0. Used to sign a tx by
|
714
791
|
# multiple parties. Signatures must be in the same order as the
|
715
792
|
# pubkeys in the output script being redeemed.
|
716
|
-
def self.add_sig_to_multisig_script_sig(sig, script_sig)
|
717
|
-
signature = sig + [
|
793
|
+
def self.add_sig_to_multisig_script_sig(sig, script_sig, hash_type = SIGHASH_TYPE[:all])
|
794
|
+
signature = sig + [hash_type].pack("C*")
|
718
795
|
offset = script_sig.empty? ? 0 : 1
|
719
796
|
script_sig.insert(offset, pack_pushdata(signature))
|
720
797
|
end
|
@@ -726,6 +803,25 @@ class Bitcoin::Script
|
|
726
803
|
to_multisig_script_sig(*sigs.flatten) + pack_pushdata(redeem_script)
|
727
804
|
end
|
728
805
|
|
806
|
+
# Sort signatures in the given +script_sig+ according to the order of pubkeys in
|
807
|
+
# the redeem script. Also needs the +sig_hash+ to match signatures to pubkeys.
|
808
|
+
def self.sort_p2sh_multisig_signatures script_sig, sig_hash
|
809
|
+
script = new(script_sig)
|
810
|
+
redeem_script = new(script.chunks[-1])
|
811
|
+
pubkeys = redeem_script.get_multisig_pubkeys
|
812
|
+
|
813
|
+
# find the pubkey for each signature by trying to verify it
|
814
|
+
sigs = Hash[script.chunks[1...-1].map.with_index do |sig, idx|
|
815
|
+
pubkey = pubkeys.map {|key|
|
816
|
+
Bitcoin::Key.new(nil, key.hth).verify(sig_hash, sig) ? key : nil }.compact.first
|
817
|
+
raise "Key for signature ##{idx} not found in redeem script!" unless pubkey
|
818
|
+
[pubkey, sig]
|
819
|
+
end]
|
820
|
+
|
821
|
+
[OP_0].pack("C*") + pubkeys.map {|k| sigs[k] ? pack_pushdata(sigs[k]) : nil }.join +
|
822
|
+
pack_pushdata(redeem_script.raw)
|
823
|
+
end
|
824
|
+
|
729
825
|
def get_signatures_required
|
730
826
|
return false unless is_multisig?
|
731
827
|
@chunks[0] - 80
|
@@ -1240,14 +1336,14 @@ class Bitcoin::Script
|
|
1240
1336
|
# do a CHECKSIG operation on the current stack,
|
1241
1337
|
# asking +check_callback+ to do the actual signature verification.
|
1242
1338
|
# This is used by Protocol::Tx#verify_input_signature
|
1243
|
-
def op_checksig(check_callback)
|
1339
|
+
def op_checksig(check_callback, opts={})
|
1244
1340
|
return invalid if @stack.size < 2
|
1245
1341
|
pubkey = cast_to_string(@stack.pop)
|
1246
|
-
|
1342
|
+
return (@stack << 0) unless Bitcoin::Script.check_pubkey_encoding?(pubkey, opts)
|
1247
1343
|
drop_sigs = [ cast_to_string(@stack[-1]) ]
|
1248
1344
|
|
1249
1345
|
signature = cast_to_string(@stack.pop)
|
1250
|
-
|
1346
|
+
return invalid unless Bitcoin::Script.check_signature_encoding?(signature, opts)
|
1251
1347
|
return (@stack << 0) if signature == ""
|
1252
1348
|
|
1253
1349
|
sig, hash_type = parse_sig(signature)
|
@@ -1271,8 +1367,8 @@ class Bitcoin::Script
|
|
1271
1367
|
end
|
1272
1368
|
|
1273
1369
|
# Same as OP_CHECKSIG, but OP_VERIFY is executed afterward.
|
1274
|
-
def op_checksigverify(check_callback)
|
1275
|
-
op_checksig(check_callback)
|
1370
|
+
def op_checksigverify(check_callback, opts={})
|
1371
|
+
op_checksig(check_callback, opts)
|
1276
1372
|
op_verify
|
1277
1373
|
end
|
1278
1374
|
|
@@ -1289,7 +1385,7 @@ class Bitcoin::Script
|
|
1289
1385
|
#
|
1290
1386
|
# TODO: validate signature order
|
1291
1387
|
# TODO: take global opcode count
|
1292
|
-
def op_checkmultisig(check_callback)
|
1388
|
+
def op_checkmultisig(check_callback, opts={})
|
1293
1389
|
return invalid if @stack.size < 1
|
1294
1390
|
n_pubkeys = pop_int
|
1295
1391
|
return invalid unless (0..20).include?(n_pubkeys)
|
@@ -1312,6 +1408,8 @@ class Bitcoin::Script
|
|
1312
1408
|
success = true
|
1313
1409
|
while success && n_sigs > 0
|
1314
1410
|
sig, pub = sigs.pop, pubkeys.pop
|
1411
|
+
return (@stack << 0) unless Bitcoin::Script.check_pubkey_encoding?(pub, opts)
|
1412
|
+
return invalid unless Bitcoin::Script.check_signature_encoding?(sig, opts)
|
1315
1413
|
unless sig && sig.size > 0
|
1316
1414
|
success = false
|
1317
1415
|
break
|
@@ -1330,8 +1428,8 @@ class Bitcoin::Script
|
|
1330
1428
|
end
|
1331
1429
|
|
1332
1430
|
# Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward.
|
1333
|
-
def op_checkmultisigverify(check_callback)
|
1334
|
-
op_checkmultisig(check_callback)
|
1431
|
+
def op_checkmultisigverify(check_callback, opts={})
|
1432
|
+
op_checkmultisig(check_callback, opts)
|
1335
1433
|
op_verify
|
1336
1434
|
end
|
1337
1435
|
|
@@ -1346,7 +1444,12 @@ class Bitcoin::Script
|
|
1346
1444
|
OPCODES_METHOD[0] = :op_0
|
1347
1445
|
OPCODES_METHOD[81] = :op_1
|
1348
1446
|
|
1349
|
-
def self.
|
1447
|
+
def self.check_pubkey_encoding?(pubkey, opts={})
|
1448
|
+
return false if opts[:verify_strictenc] && !is_compressed_or_uncompressed_pub_key?(pubkey)
|
1449
|
+
true
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
def self.is_compressed_or_uncompressed_pub_key?(pubkey)
|
1350
1453
|
return false if pubkey.bytesize < 33 # "Non-canonical public key: too short"
|
1351
1454
|
case pubkey[0]
|
1352
1455
|
when "\x04"
|
@@ -1359,21 +1462,93 @@ class Bitcoin::Script
|
|
1359
1462
|
true
|
1360
1463
|
end
|
1361
1464
|
|
1465
|
+
# Loosely matches CheckSignatureEncoding()
|
1466
|
+
def self.check_signature_encoding?(sig, opts={})
|
1467
|
+
return true if sig.bytesize == 0
|
1468
|
+
return false if (opts[:verify_dersig] || opts[:verify_low_s] || opts[:verify_strictenc]) and !is_der_signature?(sig)
|
1469
|
+
return false if opts[:verify_low_s] && !is_low_der_signature?(sig)
|
1470
|
+
return false if opts[:verify_strictenc] && !is_defined_hashtype_signature?(sig)
|
1471
|
+
true
|
1472
|
+
end
|
1362
1473
|
|
1363
|
-
|
1364
|
-
def self.
|
1474
|
+
# Loosely correlates with IsDERSignature() from interpreter.cpp
|
1475
|
+
def self.is_der_signature?(sig)
|
1365
1476
|
return false if sig.bytesize < 9 # Non-canonical signature: too short
|
1366
1477
|
return false if sig.bytesize > 73 # Non-canonical signature: too long
|
1367
1478
|
|
1368
1479
|
s = sig.unpack("C*")
|
1369
1480
|
|
1370
|
-
hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay]))
|
1371
|
-
return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single] # Non-canonical signature: unknown hashtype byte
|
1372
|
-
|
1373
1481
|
return false if s[0] != 0x30 # Non-canonical signature: wrong type
|
1374
1482
|
return false if s[1] != s.size-3 # Non-canonical signature: wrong length marker
|
1375
1483
|
|
1376
|
-
|
1484
|
+
length_r = s[3]
|
1485
|
+
return false if (5 + length_r) >= s.size # Non-canonical signature: S length misplaced
|
1486
|
+
length_s = s[5+length_r]
|
1487
|
+
return false if (length_r + length_s + 7) != s.size # Non-canonical signature: R+S length mismatch
|
1488
|
+
|
1489
|
+
return false if s[2] != 0x02 # Non-canonical signature: R value type mismatch
|
1490
|
+
|
1491
|
+
return false if length_r == 0 # Non-canonical signature: R length is zero
|
1492
|
+
|
1493
|
+
r_val = s.slice(4, length_r)
|
1494
|
+
return false if r_val[0] & 0x80 != 0 # Non-canonical signature: R value negative
|
1495
|
+
|
1496
|
+
return false if length_r > 1 && (r_val[0] == 0x00) && !(r_val[1] & 0x80 != 0) # Non-canonical signature: R value excessively padded
|
1497
|
+
|
1498
|
+
s_val = s.slice(6 + length_r, length_s)
|
1499
|
+
return false if s[6 + length_r - 2] != 0x02 # Non-canonical signature: S value type mismatch
|
1500
|
+
|
1501
|
+
return false if length_s == 0 # Non-canonical signature: S length is zero
|
1502
|
+
return false if (s_val[0] & 0x80) != 0 # Non-canonical signature: S value negative
|
1503
|
+
|
1504
|
+
return false if length_s > 1 && (s_val[0] == 0x00) && !(s_val[1] & 0x80) # Non-canonical signature: S value excessively padded
|
1505
|
+
|
1506
|
+
true
|
1507
|
+
end
|
1508
|
+
|
1509
|
+
# Compares two arrays of bytes
|
1510
|
+
def self.compare_big_endian(c1, c2)
|
1511
|
+
c1, c2 = c1.dup, c2.dup # Clone the arrays
|
1512
|
+
|
1513
|
+
while c1.size > c2.size
|
1514
|
+
return 1 if c1.shift > 0
|
1515
|
+
end
|
1516
|
+
|
1517
|
+
while c2.size > c1.size
|
1518
|
+
return -1 if c2.shift > 0
|
1519
|
+
end
|
1520
|
+
|
1521
|
+
c1.size.times{|idx| return c1[idx] - c2[idx] if c1[idx] != c2[idx] }
|
1522
|
+
0
|
1523
|
+
end
|
1524
|
+
|
1525
|
+
# Loosely correlates with IsLowDERSignature() from interpreter.cpp
|
1526
|
+
def self.is_low_der_signature?(sig)
|
1527
|
+
s = sig.unpack("C*")
|
1528
|
+
|
1529
|
+
length_r = s[3]
|
1530
|
+
length_s = s[5+length_r]
|
1531
|
+
s_val = s.slice(6 + length_r, length_s)
|
1532
|
+
|
1533
|
+
# If the S value is above the order of the curve divided by two, its
|
1534
|
+
# complement modulo the order could have been used instead, which is
|
1535
|
+
# one byte shorter when encoded correctly.
|
1536
|
+
max_mod_half_order = [
|
1537
|
+
0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
1538
|
+
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
1539
|
+
0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d,
|
1540
|
+
0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa0]
|
1541
|
+
|
1542
|
+
compare_big_endian(s_val, [0]) > 0 &&
|
1543
|
+
compare_big_endian(s_val, max_mod_half_order) <= 0
|
1544
|
+
end
|
1545
|
+
|
1546
|
+
def self.is_defined_hashtype_signature?(sig)
|
1547
|
+
return false if sig.empty?
|
1548
|
+
|
1549
|
+
s = sig.unpack("C*")
|
1550
|
+
hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay]))
|
1551
|
+
return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single] # Non-canonical signature: unknown hashtype byte
|
1377
1552
|
|
1378
1553
|
true
|
1379
1554
|
end
|