bitcoin-ruby 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +2 -2
- data/COPYING +1 -1
- data/Gemfile +5 -11
- data/README.rdoc +11 -5
- data/Rakefile +5 -0
- data/bin/bitcoin_node +11 -29
- data/bin/bitcoin_node_cli +81 -0
- data/bin/bitcoin_wallet +9 -6
- data/doc/NODE.rdoc +79 -26
- data/examples/bbe_verify_tx.rb +1 -1
- data/examples/index_nhash.rb +24 -0
- data/examples/reindex_p2sh_addrs.rb +44 -0
- data/lib/bitcoin.rb +135 -20
- data/lib/bitcoin/builder.rb +233 -63
- data/lib/bitcoin/key.rb +89 -16
- data/lib/bitcoin/litecoin.rb +13 -11
- data/lib/bitcoin/namecoin.rb +5 -4
- data/lib/bitcoin/network/command_client.rb +23 -13
- data/lib/bitcoin/network/command_handler.rb +336 -131
- data/lib/bitcoin/network/connection_handler.rb +14 -13
- data/lib/bitcoin/network/node.rb +61 -20
- data/lib/bitcoin/protocol.rb +5 -1
- data/lib/bitcoin/protocol/block.rb +15 -3
- data/lib/bitcoin/protocol/parser.rb +3 -3
- data/lib/bitcoin/protocol/tx.rb +82 -20
- data/lib/bitcoin/protocol/txin.rb +7 -0
- data/lib/bitcoin/protocol/txout.rb +12 -9
- data/lib/bitcoin/script.rb +329 -75
- data/lib/bitcoin/storage/dummy/dummy_store.rb +23 -4
- data/lib/bitcoin/storage/models.rb +6 -11
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +14 -0
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +16 -0
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +56 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +168 -70
- data/lib/bitcoin/storage/storage.rb +161 -97
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +1 -1
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +14 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +25 -12
- data/lib/bitcoin/validation.rb +87 -56
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +38 -0
- data/spec/bitcoin/builder_spec.rb +177 -0
- data/spec/bitcoin/fixtures/litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json +259 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-265322.bin +0 -0
- data/spec/bitcoin/fixtures/tx-0295028ef826b2a188409cb905b631faebb9bb3cdf14510571c5f4bd8591338f.json +64 -0
- data/spec/bitcoin/fixtures/tx-03339a725007a279484fb6f5361f522dd1cf4d0923d30e6b973290dba4275f92.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ce7e5238fbdb6c086cf1b384b21b827e91cc23f360417265874a5a0d86ce367.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ef34c49f630aea17df0080728b0fc67bf5f87fbda936934a4b11b4a69d7821e.json +64 -0
- data/spec/bitcoin/fixtures/tx-1129d2a8bd5bb3a81e54dc96a90f1f6b2544575748caa17243470935c5dd91b7.json +28 -0
- data/spec/bitcoin/fixtures/tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json +23 -0
- data/spec/bitcoin/fixtures/tx-1a4f3b9dc4494aeedeb39f30dd37e60541b2abe3ed4977992017cc0ad4f44956.json +64 -0
- data/spec/bitcoin/fixtures/tx-1f9191dcf2b1844ca28c6ef4b969e1d5fab70a5e3c56b7007949e55851cb0c4f.json +64 -0
- data/spec/bitcoin/fixtures/tx-22cd5fef23684d7b304e119bedffde6f54538d3d54a5bfa237e20dc2d9b4b5ad.json +64 -0
- data/spec/bitcoin/fixtures/tx-2958fb00b4fd6fe0353503b886eb9a193d502f4fd5fc042d5e03216ba918bbd6.json +64 -0
- data/spec/bitcoin/fixtures/tx-29f277145749ad6efbed3ae6ce301f8d33c585ec26b7c044ad93c2f866e9e942.json +64 -0
- data/spec/bitcoin/fixtures/tx-2c5e5376c20e9cc78d0fb771730e5d840cc2096eff0ef045b599fe92475ace1c.json +28 -0
- data/spec/bitcoin/fixtures/tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json +30 -0
- data/spec/bitcoin/fixtures/tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json +23 -0
- data/spec/bitcoin/fixtures/tx-345bed8785c3282a264ffb0dbee61cde54854f10e16f1b3e75b7f2d9f62946f2.json +64 -0
- data/spec/bitcoin/fixtures/tx-39ba7440b7103557560cc8ce258009936796485aaf8b478e66ab4cb97c66e31b.json +32 -0
- data/spec/bitcoin/fixtures/tx-3a04d57a833367f1655cc5ec3beb587888ef4977a86caa8c8ad4ba7cc717eae7.json +64 -0
- data/spec/bitcoin/fixtures/tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json +38 -0
- data/spec/bitcoin/fixtures/tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json +23 -0
- data/spec/bitcoin/fixtures/tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json +30 -0
- data/spec/bitcoin/fixtures/tx-62d9a565bd7b5344c5352e3e9e5f40fa4bbd467fa19c87357216ec8777ba1cce.json +64 -0
- data/spec/bitcoin/fixtures/tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json +23 -0
- data/spec/bitcoin/fixtures/tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json +27 -0
- data/spec/bitcoin/fixtures/tx-6aaf18b9f1283b939d8e5d40ff5f8a435229f4178372659cc3a0bce4e262bf78.json +28 -0
- data/spec/bitcoin/fixtures/tx-6b48bba6f6d2286d7ec0883c0fc3085955090813a4c94980466611c798b868cc.json +64 -0
- data/spec/bitcoin/fixtures/tx-70cfbc6690f9ab46712db44e3079ac227962b2771a9341d4233d898b521619ef.json +40 -0
- data/spec/bitcoin/fixtures/tx-7a1a9db42f065f75110fcdb1bc415549c8ef7670417ba1d35a67f1b8adc562c1.json +64 -0
- data/spec/bitcoin/fixtures/tx-9a768fc7d0c4bdc86e25154357ef7c0063ca21310e5740a2f12f90b7455184a7.json +64 -0
- data/spec/bitcoin/fixtures/tx-9cad8d523a0694f2509d092c39cebc8046adae62b4e4297102d568191d9478d8.json +64 -0
- data/spec/bitcoin/fixtures/tx-9e052eb694bd7e15906433f064dff0161a12fd325c1124537766377004023c6f.json +64 -0
- data/spec/bitcoin/fixtures/tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json +23 -0
- data/spec/bitcoin/fixtures/tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json +23 -0
- data/spec/bitcoin/fixtures/tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json +27 -0
- data/spec/bitcoin/fixtures/tx-ad4bcf3241e5d2ad140564e20db3567d41594cf4c2012433fe46a2b70e0d87b8.json +64 -0
- data/spec/bitcoin/fixtures/tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json +27 -0
- data/spec/bitcoin/fixtures/tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json +28 -0
- data/spec/bitcoin/fixtures/tx-bbca0628c42cb8bf7c3f4b2ad688fa56da5308dd2a10255da89fb1f46e6e413d.json +36 -0
- data/spec/bitcoin/fixtures/tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json +23 -0
- data/spec/bitcoin/fixtures/tx-c192b74844e4837a34c4a5a97b438f1c111405b01b99e2d12b7c96d07fc74c04.json +28 -0
- data/spec/bitcoin/fixtures/tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json +406 -0
- data/spec/bitcoin/fixtures/tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json +35 -0
- data/spec/bitcoin/fixtures/tx-fee1b9b85531c8fb6cd7831f83490c7f2aa768b6eefe29854ef5e89ce7b9ecb1.json +64 -0
- data/spec/bitcoin/fixtures/txscript-invalid-too-many-sigops-followed-by-invalid-pushdata.bin +1 -0
- data/spec/bitcoin/helpers/fake_blockchain.rb +183 -0
- data/spec/bitcoin/key_spec.rb +79 -8
- data/spec/bitcoin/namecoin_spec.rb +1 -1
- data/spec/bitcoin/node/command_api_spec.rb +373 -86
- data/spec/bitcoin/performance/storage_spec.rb +41 -0
- data/spec/bitcoin/protocol/addr_spec.rb +7 -5
- data/spec/bitcoin/protocol/aux_pow_spec.rb +1 -0
- data/spec/bitcoin/protocol/block_spec.rb +6 -0
- data/spec/bitcoin/protocol/tx_spec.rb +184 -1
- data/spec/bitcoin/protocol/txin_spec.rb +27 -0
- data/spec/bitcoin/protocol/txout_spec.rb +27 -0
- data/spec/bitcoin/script/opcodes_spec.rb +74 -3
- data/spec/bitcoin/script/script_spec.rb +271 -0
- data/spec/bitcoin/spec_helper.rb +34 -6
- data/spec/bitcoin/storage/models_spec.rb +104 -0
- data/spec/bitcoin/storage/reorg_spec.rb +42 -11
- data/spec/bitcoin/storage/storage_spec.rb +58 -15
- data/spec/bitcoin/storage/validation_spec.rb +44 -14
- data/spec/bitcoin/wallet/keygenerator_spec.rb +6 -3
- data/spec/bitcoin/wallet/keystore_spec.rb +3 -3
- data/spec/bitcoin/wallet/wallet_spec.rb +87 -89
- metadata +117 -11
|
@@ -42,6 +42,13 @@ module Bitcoin
|
|
|
42
42
|
@prev_out_index == other.prev_out_index &&
|
|
43
43
|
@script_sig == other.script_sig &&
|
|
44
44
|
@sequence == other.sequence
|
|
45
|
+
rescue
|
|
46
|
+
false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# returns true if the sequence number is final (DEFAULT_SEQUENCE)
|
|
50
|
+
def is_final?
|
|
51
|
+
self.sequence == DEFAULT_SEQUENCE
|
|
45
52
|
end
|
|
46
53
|
|
|
47
54
|
# parse raw binary data for transaction input
|
|
@@ -11,6 +11,9 @@ module Bitcoin
|
|
|
11
11
|
# pk_script output Script
|
|
12
12
|
attr_accessor :pk_script, :pk_script_length
|
|
13
13
|
|
|
14
|
+
# p2sh redeem script (optional, not included in the serialized binary format)
|
|
15
|
+
attr_accessor :redeem_script
|
|
16
|
+
|
|
14
17
|
def initialize *args
|
|
15
18
|
if args.size == 2
|
|
16
19
|
@value, @pk_script_length, @pk_script = args[0], args[1].bytesize, args[1]
|
|
@@ -20,7 +23,7 @@ module Bitcoin
|
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
def ==(other)
|
|
23
|
-
@value == other.value && @pk_script == other.pk_script
|
|
26
|
+
@value == other.value && @pk_script == other.pk_script rescue false
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
# parse raw binary data for transaction output
|
|
@@ -43,8 +46,8 @@ module Bitcoin
|
|
|
43
46
|
|
|
44
47
|
alias :parse_payload :parse_data
|
|
45
48
|
|
|
46
|
-
def
|
|
47
|
-
@
|
|
49
|
+
def parsed_script
|
|
50
|
+
@parsed_script ||= Bitcoin::Script.new(pk_script)
|
|
48
51
|
end
|
|
49
52
|
|
|
50
53
|
def to_payload
|
|
@@ -56,10 +59,9 @@ module Bitcoin
|
|
|
56
59
|
end
|
|
57
60
|
|
|
58
61
|
def to_hash(options = {})
|
|
59
|
-
script = get_script
|
|
60
62
|
h = { 'value' => "%.8f" % (@value / 100000000.0),
|
|
61
|
-
'scriptPubKey' =>
|
|
62
|
-
h["address"] =
|
|
63
|
+
'scriptPubKey' => parsed_script.to_string }
|
|
64
|
+
h["address"] = parsed_script.get_address if parsed_script.is_hash160? && options[:with_address]
|
|
63
65
|
h
|
|
64
66
|
end
|
|
65
67
|
|
|
@@ -68,17 +70,18 @@ module Bitcoin
|
|
|
68
70
|
script = Script.binary_from_string(output['scriptPubKey'])
|
|
69
71
|
new(amount, script)
|
|
70
72
|
end
|
|
73
|
+
|
|
71
74
|
# set pk_script and pk_script_length
|
|
72
|
-
def pk_script=(
|
|
73
|
-
@pk_script_length, @pk_script =
|
|
75
|
+
def pk_script=(pk_script)
|
|
76
|
+
@pk_script_length, @pk_script = pk_script.bytesize, pk_script
|
|
74
77
|
end
|
|
75
78
|
|
|
76
79
|
alias :amount :value
|
|
77
80
|
alias :amount= :value=
|
|
81
|
+
|
|
78
82
|
alias :script :pk_script
|
|
79
83
|
alias :script= :pk_script=
|
|
80
84
|
|
|
81
|
-
|
|
82
85
|
# create output spending +value+ btc (base units) to +address+
|
|
83
86
|
def self.value_to_address(value, address)
|
|
84
87
|
pk_script = Bitcoin::Script.to_address_script(address)
|
data/lib/bitcoin/script.rb
CHANGED
|
@@ -4,10 +4,26 @@ require 'bitcoin'
|
|
|
4
4
|
|
|
5
5
|
class Bitcoin::Script
|
|
6
6
|
|
|
7
|
-
OP_1 = 81
|
|
8
|
-
OP_TRUE = 81
|
|
9
7
|
OP_0 = 0
|
|
10
8
|
OP_FALSE = 0
|
|
9
|
+
OP_1 = 81
|
|
10
|
+
OP_TRUE = 81
|
|
11
|
+
OP_2 = 0x52
|
|
12
|
+
OP_3 = 0x53
|
|
13
|
+
OP_4 = 0x54
|
|
14
|
+
OP_5 = 0x55
|
|
15
|
+
OP_6 = 0x56
|
|
16
|
+
OP_7 = 0x57
|
|
17
|
+
OP_8 = 0x58
|
|
18
|
+
OP_9 = 0x59
|
|
19
|
+
OP_10 = 0x5a
|
|
20
|
+
OP_11 = 0x5b
|
|
21
|
+
OP_12 = 0x5c
|
|
22
|
+
OP_13 = 0x5d
|
|
23
|
+
OP_14 = 0x5e
|
|
24
|
+
OP_15 = 0x5f
|
|
25
|
+
OP_16 = 0x60
|
|
26
|
+
|
|
11
27
|
OP_PUSHDATA0 = 0
|
|
12
28
|
OP_PUSHDATA1 = 76
|
|
13
29
|
OP_PUSHDATA2 = 77
|
|
@@ -50,6 +66,7 @@ class Bitcoin::Script
|
|
|
50
66
|
OP_MIN = 163
|
|
51
67
|
OP_MAX = 164
|
|
52
68
|
OP_2OVER = 112
|
|
69
|
+
OP_2ROT = 113
|
|
53
70
|
OP_2SWAP = 114
|
|
54
71
|
OP_IFDUP = 115
|
|
55
72
|
OP_DEPTH = 116
|
|
@@ -133,14 +150,30 @@ class Bitcoin::Script
|
|
|
133
150
|
2.upto(16).each{|i| OPCODES_PARSE_STRING["#{i}" ] = OP_2_16[i-2] }
|
|
134
151
|
[1,2,4].each{|i| OPCODES_PARSE_STRING.delete("OP_PUSHDATA#{i}") }
|
|
135
152
|
|
|
153
|
+
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
|
136
154
|
|
|
137
155
|
attr_reader :raw, :chunks, :debug
|
|
138
156
|
|
|
139
157
|
# create a new script. +bytes+ is typically input_script + output_script
|
|
140
|
-
def initialize(
|
|
141
|
-
@
|
|
158
|
+
def initialize(input_script, previous_output_script=nil)
|
|
159
|
+
@raw_byte_sizes = [input_script.bytesize, previous_output_script ? previous_output_script.bytesize : 0]
|
|
160
|
+
|
|
161
|
+
@raw = if previous_output_script
|
|
162
|
+
input_script + [ Bitcoin::Script::OP_CODESEPARATOR ].pack("C") + previous_output_script
|
|
163
|
+
else
|
|
164
|
+
input_script
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
@chunks = parse(input_script)
|
|
168
|
+
|
|
169
|
+
if previous_output_script
|
|
170
|
+
@script_codeseparator_index = @chunks.size
|
|
171
|
+
@chunks << Bitcoin::Script::OP_CODESEPARATOR
|
|
172
|
+
@chunks += parse(previous_output_script)
|
|
173
|
+
end
|
|
174
|
+
|
|
142
175
|
@stack, @stack_alt, @exec_stack = [], [], []
|
|
143
|
-
@
|
|
176
|
+
@last_codeseparator_index = 0
|
|
144
177
|
@do_exec = true
|
|
145
178
|
end
|
|
146
179
|
|
|
@@ -154,14 +187,14 @@ class Bitcoin::Script
|
|
|
154
187
|
program = bytes.unpack("C*")
|
|
155
188
|
chunks = []
|
|
156
189
|
until program.empty?
|
|
157
|
-
opcode = program.shift
|
|
190
|
+
opcode = program.shift
|
|
158
191
|
|
|
159
192
|
if (opcode > 0) && (opcode < OP_PUSHDATA1)
|
|
160
193
|
len, tmp = opcode, program[0]
|
|
161
194
|
chunks << program.shift(len).pack("C*")
|
|
162
195
|
|
|
163
196
|
# 0x16 = 22 due to OP_2_16 from_string parsing
|
|
164
|
-
if len == 1 && tmp <= 22
|
|
197
|
+
if len == 1 && tmp && tmp <= 22
|
|
165
198
|
chunks.last.bitcoin_pushdata = OP_PUSHDATA0
|
|
166
199
|
chunks.last.bitcoin_pushdata_length = len
|
|
167
200
|
else
|
|
@@ -202,13 +235,14 @@ class Bitcoin::Script
|
|
|
202
235
|
end
|
|
203
236
|
end
|
|
204
237
|
chunks
|
|
205
|
-
rescue
|
|
238
|
+
rescue => ex
|
|
206
239
|
# bail out! #run returns false but serialization roundtrips still create the right payload.
|
|
240
|
+
chunks.pop if ex.message.include?("invalid OP_PUSHDATA")
|
|
207
241
|
@parse_invalid = true
|
|
208
242
|
c = bytes.unpack("C*").pack("C*")
|
|
209
243
|
c.bitcoin_pushdata = OP_PUSHDATA_INVALID
|
|
210
244
|
c.bitcoin_pushdata_length = c.bytesize
|
|
211
|
-
chunks
|
|
245
|
+
chunks << c
|
|
212
246
|
end
|
|
213
247
|
|
|
214
248
|
# string representation of the script
|
|
@@ -244,11 +278,44 @@ class Bitcoin::Script
|
|
|
244
278
|
end
|
|
245
279
|
alias :to_payload :to_binary
|
|
246
280
|
|
|
247
|
-
|
|
248
281
|
def to_binary_without_signatures(drop_signatures, chunks=nil)
|
|
249
|
-
|
|
282
|
+
buf = []
|
|
283
|
+
(chunks || @chunks).each.with_index{|chunk,idx|
|
|
284
|
+
if chunk == OP_CODESEPARATOR and idx <= @last_codeseparator_index
|
|
285
|
+
buf.clear
|
|
286
|
+
elsif chunk == OP_CODESEPARATOR
|
|
287
|
+
if idx == @script_codeseparator_index
|
|
288
|
+
break
|
|
289
|
+
else
|
|
290
|
+
# skip
|
|
291
|
+
end
|
|
292
|
+
elsif drop_signatures.none?{|e| e == chunk }
|
|
293
|
+
buf << chunk
|
|
294
|
+
end
|
|
295
|
+
}
|
|
296
|
+
to_binary(buf)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Adds opcode (OP_0, OP_1, ... OP_CHECKSIG etc.)
|
|
300
|
+
# Returns self.
|
|
301
|
+
def append_opcode(opcode)
|
|
302
|
+
raise "Opcode should be a Fixnum" if !opcode.is_a?(Fixnum)
|
|
303
|
+
if opcode >= OP_0 && opcode <= 0xff
|
|
304
|
+
@chunks << opcode
|
|
305
|
+
else
|
|
306
|
+
raise "Opcode should be within [0x00, 0xff]"
|
|
307
|
+
end
|
|
308
|
+
self
|
|
250
309
|
end
|
|
251
310
|
|
|
311
|
+
# Adds binary string as pushdata. Pushdata will be encoded in the most compact form
|
|
312
|
+
# (unless the string contains internal info about serialization that's added by Script class)
|
|
313
|
+
# Returns self.
|
|
314
|
+
def append_pushdata(pushdata_string)
|
|
315
|
+
raise "Pushdata should be a string" if !pushdata_string.is_a?(String)
|
|
316
|
+
@chunks << pushdata_string
|
|
317
|
+
self
|
|
318
|
+
end
|
|
252
319
|
|
|
253
320
|
def self.pack_pushdata(data)
|
|
254
321
|
size = data.bytesize
|
|
@@ -333,15 +400,17 @@ class Bitcoin::Script
|
|
|
333
400
|
return false if @parse_invalid
|
|
334
401
|
|
|
335
402
|
#p [to_string, block_timestamp, is_p2sh?]
|
|
336
|
-
@script_invalid = true if @
|
|
403
|
+
@script_invalid = true if @raw_byte_sizes.any?{|size| size > 10_000 }
|
|
404
|
+
@last_codeseparator_index = 0
|
|
337
405
|
|
|
338
406
|
if block_timestamp >= 1333238400 # Pay to Script Hash (BIP 0016)
|
|
339
407
|
return pay_to_script_hash(check_callback) if is_p2sh?
|
|
340
408
|
end
|
|
341
409
|
|
|
342
410
|
@debug = []
|
|
343
|
-
@chunks.each{|chunk|
|
|
411
|
+
@chunks.each.with_index{|chunk,idx|
|
|
344
412
|
break if invalid?
|
|
413
|
+
@chunk_last_index = idx
|
|
345
414
|
|
|
346
415
|
@debug << @stack.map{|i| i.unpack("H*") rescue i}
|
|
347
416
|
@do_exec = @exec_stack.count(false) == 0 ? true : false
|
|
@@ -355,7 +424,7 @@ class Bitcoin::Script
|
|
|
355
424
|
break
|
|
356
425
|
end
|
|
357
426
|
|
|
358
|
-
next unless (@do_exec || (OP_IF <= chunk && chunk <= OP_ENDIF))
|
|
427
|
+
next @debug.pop unless (@do_exec || (OP_IF <= chunk && chunk <= OP_ENDIF))
|
|
359
428
|
|
|
360
429
|
case chunk
|
|
361
430
|
when *OPCODES_METHOD.keys
|
|
@@ -374,6 +443,8 @@ class Bitcoin::Script
|
|
|
374
443
|
if @do_exec
|
|
375
444
|
@debug << "PUSH DATA #{chunk.unpack("H*")[0]}"
|
|
376
445
|
@stack << chunk
|
|
446
|
+
else
|
|
447
|
+
@debug.pop
|
|
377
448
|
end
|
|
378
449
|
end
|
|
379
450
|
}
|
|
@@ -386,7 +457,7 @@ class Bitcoin::Script
|
|
|
386
457
|
|
|
387
458
|
@debug << "RESULT"
|
|
388
459
|
return false if @stack.empty?
|
|
389
|
-
return false if
|
|
460
|
+
return false if cast_to_bool(@stack.pop) == false
|
|
390
461
|
true
|
|
391
462
|
end
|
|
392
463
|
|
|
@@ -405,10 +476,10 @@ class Bitcoin::Script
|
|
|
405
476
|
def pay_to_script_hash(check_callback)
|
|
406
477
|
return false if @chunks.size < 4
|
|
407
478
|
*rest, script, _, script_hash, _ = @chunks
|
|
479
|
+
script = rest.pop if script == OP_CODESEPARATOR
|
|
408
480
|
script, script_hash = cast_to_string(script), cast_to_string(script_hash)
|
|
409
481
|
|
|
410
482
|
return false unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
|
|
411
|
-
rest.delete_at(0) if rest[0] && cast_to_bignum(rest[0]) == 0
|
|
412
483
|
|
|
413
484
|
script = self.class.new(to_binary(rest) + script).inner_p2sh!(script)
|
|
414
485
|
result = script.run(&check_callback)
|
|
@@ -419,7 +490,19 @@ class Bitcoin::Script
|
|
|
419
490
|
def inner_p2sh!(script=nil); @inner_p2sh = true; @inner_script_code = script; self; end
|
|
420
491
|
def inner_p2sh?; @inner_p2sh; end
|
|
421
492
|
|
|
493
|
+
# get the inner p2sh script
|
|
494
|
+
def inner_p2sh_script
|
|
495
|
+
return nil if @chunks.size < 4
|
|
496
|
+
*rest, script, _, script_hash, _ = @chunks
|
|
497
|
+
script = rest.pop if script == OP_CODESEPARATOR
|
|
498
|
+
script, script_hash = cast_to_string(script), cast_to_string(script_hash)
|
|
499
|
+
|
|
500
|
+
return nil unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
|
|
501
|
+
script
|
|
502
|
+
end
|
|
503
|
+
|
|
422
504
|
def is_pay_to_script_hash?
|
|
505
|
+
return false if @inner_p2sh
|
|
423
506
|
return false unless @chunks[-2].is_a?(String)
|
|
424
507
|
@chunks.size >= 3 && @chunks[-3] == OP_HASH160 &&
|
|
425
508
|
@chunks[-2].bytesize == 20 && @chunks[-1] == OP_EQUAL
|
|
@@ -428,17 +511,17 @@ class Bitcoin::Script
|
|
|
428
511
|
|
|
429
512
|
# check if script is in one of the recognized standard formats
|
|
430
513
|
def is_standard?
|
|
431
|
-
is_pubkey? || is_hash160? || is_multisig? || is_p2sh?
|
|
514
|
+
is_pubkey? || is_hash160? || is_multisig? || is_p2sh? || is_op_return?
|
|
432
515
|
end
|
|
433
516
|
|
|
434
|
-
# is this a pubkey
|
|
517
|
+
# is this a pubkey script
|
|
435
518
|
def is_pubkey?
|
|
436
519
|
return false if @chunks.size != 2
|
|
437
|
-
(@chunks[1] == OP_CHECKSIG) && @chunks[0].
|
|
520
|
+
(@chunks[1] == OP_CHECKSIG) && @chunks[0] && (@chunks[0].is_a?(String)) && @chunks[0] != OP_RETURN
|
|
438
521
|
end
|
|
439
522
|
alias :is_send_to_ip? :is_pubkey?
|
|
440
523
|
|
|
441
|
-
# is this a hash160 (address)
|
|
524
|
+
# is this a hash160 (address) script
|
|
442
525
|
def is_hash160?
|
|
443
526
|
return false if @chunks.size != 5
|
|
444
527
|
(@chunks[0..1] + @chunks[-2..-1]) ==
|
|
@@ -446,18 +529,24 @@ class Bitcoin::Script
|
|
|
446
529
|
@chunks[2].is_a?(String) && @chunks[2].bytesize == 20
|
|
447
530
|
end
|
|
448
531
|
|
|
449
|
-
# is this a multisig
|
|
532
|
+
# is this a multisig script
|
|
450
533
|
def is_multisig?
|
|
451
|
-
return false if @chunks.size
|
|
534
|
+
return false if @chunks.size < 4 || !@chunks[-2].is_a?(Fixnum)
|
|
452
535
|
@chunks[-1] == OP_CHECKMULTISIG and get_multisig_pubkeys.all?{|c| c.is_a?(String) }
|
|
453
536
|
end
|
|
454
537
|
|
|
538
|
+
# is this an op_return script
|
|
539
|
+
def is_op_return?
|
|
540
|
+
@chunks[0] == OP_RETURN && @chunks.size <= 2
|
|
541
|
+
end
|
|
542
|
+
|
|
455
543
|
# get type of this tx
|
|
456
544
|
def type
|
|
457
545
|
if is_hash160?; :hash160
|
|
458
546
|
elsif is_pubkey?; :pubkey
|
|
459
547
|
elsif is_multisig?; :multisig
|
|
460
548
|
elsif is_p2sh?; :p2sh
|
|
549
|
+
elsif is_op_return?;:op_return
|
|
461
550
|
else; :unknown
|
|
462
551
|
end
|
|
463
552
|
end
|
|
@@ -504,6 +593,12 @@ class Bitcoin::Script
|
|
|
504
593
|
Bitcoin.hash160_to_p2sh_address(get_hash160)
|
|
505
594
|
end
|
|
506
595
|
|
|
596
|
+
# get the data possibly included in an OP_RETURN script
|
|
597
|
+
def get_op_return_data
|
|
598
|
+
return nil unless is_op_return?
|
|
599
|
+
cast_to_string(@chunks[1]).unpack("H*")[0] if @chunks[1]
|
|
600
|
+
end
|
|
601
|
+
|
|
507
602
|
# get all addresses this script corresponds to (if possible)
|
|
508
603
|
def get_addresses
|
|
509
604
|
return [get_pubkey_address] if is_pubkey?
|
|
@@ -519,25 +614,30 @@ class Bitcoin::Script
|
|
|
519
614
|
addrs.is_a?(Array) ? addrs[0] : addrs
|
|
520
615
|
end
|
|
521
616
|
|
|
522
|
-
# generate pubkey tx script for given +pubkey
|
|
617
|
+
# generate pubkey tx script for given +pubkey+. returns a raw binary script of the form:
|
|
618
|
+
# <pubkey> OP_CHECKSIG
|
|
523
619
|
def self.to_pubkey_script(pubkey)
|
|
524
|
-
|
|
525
|
-
[[pk.bytesize].pack("C"), pk, "\xAC"].join
|
|
620
|
+
pack_pushdata([pubkey].pack("H*")) + [ OP_CHECKSIG ].pack("C")
|
|
526
621
|
end
|
|
527
622
|
|
|
528
|
-
# generate hash160 tx for given +address
|
|
623
|
+
# generate hash160 tx for given +address+. returns a raw binary script of the form:
|
|
624
|
+
# OP_DUP OP_HASH160 <hash160> OP_EQUALVERIFY OP_CHECKSIG
|
|
529
625
|
def self.to_hash160_script(hash160)
|
|
530
626
|
return nil unless hash160
|
|
531
627
|
# DUP HASH160 length hash160 EQUALVERIFY CHECKSIG
|
|
532
628
|
[ ["76", "a9", "14", hash160, "88", "ac"].join ].pack("H*")
|
|
533
629
|
end
|
|
534
630
|
|
|
631
|
+
# generate p2sh output script for given +p2sh+ hash160. returns a raw binary script of the form:
|
|
632
|
+
# OP_HASH160 <p2sh> OP_EQUAL
|
|
535
633
|
def self.to_p2sh_script(p2sh)
|
|
536
634
|
return nil unless p2sh
|
|
537
635
|
# HASH160 length hash EQUAL
|
|
538
636
|
[ ["a9", "14", p2sh, "87"].join ].pack("H*")
|
|
539
637
|
end
|
|
540
638
|
|
|
639
|
+
# generate hash160 or p2sh output script, depending on the type of the given +address+.
|
|
640
|
+
# see #to_hash160_script and #to_p2sh_script.
|
|
541
641
|
def self.to_address_script(address)
|
|
542
642
|
hash160 = Bitcoin.hash160_from_address(address)
|
|
543
643
|
case Bitcoin.address_type(address)
|
|
@@ -546,30 +646,53 @@ class Bitcoin::Script
|
|
|
546
646
|
end
|
|
547
647
|
end
|
|
548
648
|
|
|
549
|
-
# generate multisig
|
|
649
|
+
# generate multisig output script for given +pubkeys+, expecting +m+ signatures.
|
|
650
|
+
# returns a raw binary script of the form:
|
|
651
|
+
# <m> <pubkey> [<pubkey> ...] <n_pubkeys> OP_CHECKMULTISIG
|
|
550
652
|
def self.to_multisig_script(m, *pubkeys)
|
|
551
|
-
|
|
552
|
-
|
|
653
|
+
raise "invalid m-of-n number" unless [m, pubkeys.size].all?{|i| (0..20).include?(i) }
|
|
654
|
+
raise "invalid m-of-n number" if pubkeys.size < m
|
|
655
|
+
pubs = pubkeys.map{|pk| pack_pushdata([pk].pack("H*")) }
|
|
656
|
+
|
|
657
|
+
m = m > 16 ? pack_pushdata([m].pack("C")) : [80 + m.to_i].pack("C")
|
|
658
|
+
n = pubkeys.size > 16 ? pack_pushdata([pubkeys.size].pack("C")) : [80 + pubs.size].pack("C")
|
|
659
|
+
|
|
660
|
+
[ m, *pubs, n, [OP_CHECKMULTISIG].pack("C")].join
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
# generate OP_RETURN output script with given data. returns a raw binary script of the form:
|
|
664
|
+
# OP_RETURN <data>
|
|
665
|
+
def self.to_op_return_script(data = nil)
|
|
666
|
+
buf = [ OP_RETURN ].pack("C")
|
|
667
|
+
return buf unless data
|
|
668
|
+
return buf + pack_pushdata( [data].pack("H*") )
|
|
553
669
|
end
|
|
554
670
|
|
|
555
|
-
# generate
|
|
671
|
+
# generate input script sig spending a pubkey output with given +signature+ and +pubkey+.
|
|
672
|
+
# returns a raw binary script sig of the form:
|
|
673
|
+
# <signature> [<pubkey>]
|
|
556
674
|
def self.to_pubkey_script_sig(signature, pubkey)
|
|
557
|
-
hash_type = "
|
|
558
|
-
|
|
559
|
-
return
|
|
675
|
+
hash_type = [ SIGHASH_TYPE[:all] ].pack("C")
|
|
676
|
+
buf = pack_pushdata(signature + hash_type)
|
|
677
|
+
return buf unless pubkey
|
|
560
678
|
|
|
561
|
-
case pubkey[0]
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
expected_size = 33
|
|
566
|
-
end
|
|
679
|
+
expected_size = case pubkey[0]
|
|
680
|
+
when "\x04"; 65
|
|
681
|
+
when "\x02", "\x03"; 33
|
|
682
|
+
end
|
|
567
683
|
|
|
568
|
-
if !expected_size || pubkey.bytesize != expected_size
|
|
569
|
-
|
|
570
|
-
|
|
684
|
+
raise "pubkey is not in binary form" if !expected_size || pubkey.bytesize != expected_size
|
|
685
|
+
|
|
686
|
+
return buf + pack_pushdata(pubkey)
|
|
687
|
+
end
|
|
571
688
|
|
|
572
|
-
|
|
689
|
+
# generate p2sh multisig output script for given +args+.
|
|
690
|
+
# returns the p2sh output script, and the redeem script needed to spend it.
|
|
691
|
+
# see #to_multisig_script for the redeem script, and #to_p2sh_script for the p2sh script.
|
|
692
|
+
def self.to_p2sh_multisig_script(*args)
|
|
693
|
+
redeem_script = to_multisig_script(*args)
|
|
694
|
+
p2sh_script = to_p2sh_script(Bitcoin.hash160(redeem_script.hth))
|
|
695
|
+
return p2sh_script, redeem_script
|
|
573
696
|
end
|
|
574
697
|
|
|
575
698
|
# alias for #to_pubkey_script_sig
|
|
@@ -577,8 +700,30 @@ class Bitcoin::Script
|
|
|
577
700
|
to_pubkey_script_sig(*a)
|
|
578
701
|
end
|
|
579
702
|
|
|
703
|
+
# generate input script sig spending a multisig output script.
|
|
704
|
+
# returns a raw binary script sig of the form:
|
|
705
|
+
# OP_0 <sig> [<sig> ...]
|
|
580
706
|
def self.to_multisig_script_sig(*sigs)
|
|
581
|
-
|
|
707
|
+
partial_script = [OP_0].pack("C*")
|
|
708
|
+
sigs.reverse_each{ |sig| partial_script = add_sig_to_multisig_script_sig(sig, partial_script) }
|
|
709
|
+
partial_script
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
# take a multisig script sig (or p2sh multisig script sig) and add
|
|
713
|
+
# another signature to it after the OP_0. Used to sign a tx by
|
|
714
|
+
# multiple parties. Signatures must be in the same order as the
|
|
715
|
+
# pubkeys in the output script being redeemed.
|
|
716
|
+
def self.add_sig_to_multisig_script_sig(sig, script_sig)
|
|
717
|
+
signature = sig + [SIGHASH_TYPE[:all]].pack("C*")
|
|
718
|
+
offset = script_sig.empty? ? 0 : 1
|
|
719
|
+
script_sig.insert(offset, pack_pushdata(signature))
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
# generate input script sig spending a p2sh-multisig output script.
|
|
723
|
+
# returns a raw binary script sig of the form:
|
|
724
|
+
# OP_0 <sig> [<sig> ...] <redeem_script>
|
|
725
|
+
def self.to_p2sh_multisig_script_sig(redeem_script, *sigs)
|
|
726
|
+
to_multisig_script_sig(*sigs.flatten) + pack_pushdata(redeem_script)
|
|
582
727
|
end
|
|
583
728
|
|
|
584
729
|
def get_signatures_required
|
|
@@ -586,6 +731,72 @@ class Bitcoin::Script
|
|
|
586
731
|
@chunks[0] - 80
|
|
587
732
|
end
|
|
588
733
|
|
|
734
|
+
# This matches CScript::GetSigOpCount(bool fAccurate)
|
|
735
|
+
# Note: this does not cover P2SH script which is to be unserialized
|
|
736
|
+
# and checked explicitly when validating blocks.
|
|
737
|
+
def sigops_count_accurate(is_accurate)
|
|
738
|
+
count = 0
|
|
739
|
+
last_opcode = nil
|
|
740
|
+
@chunks.each do |chunk| # pushdate or opcode
|
|
741
|
+
if chunk == OP_CHECKSIG || chunk == OP_CHECKSIGVERIFY
|
|
742
|
+
count += 1
|
|
743
|
+
elsif chunk == OP_CHECKMULTISIG || chunk == OP_CHECKMULTISIGVERIFY
|
|
744
|
+
# Accurate mode counts exact number of pubkeys required (not signatures, but pubkeys!). Only used in P2SH scripts.
|
|
745
|
+
# Inaccurate mode counts every multisig as 20 signatures.
|
|
746
|
+
if is_accurate && last_opcode && last_opcode.is_a?(Fixnum) && last_opcode >= OP_1 && last_opcode <= OP_16
|
|
747
|
+
count += ::Bitcoin::Script.decode_OP_N(last_opcode)
|
|
748
|
+
else
|
|
749
|
+
count += 20
|
|
750
|
+
end
|
|
751
|
+
end
|
|
752
|
+
last_opcode = chunk
|
|
753
|
+
end
|
|
754
|
+
count
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
# This method applies to script_sig that is an input for p2sh output.
|
|
758
|
+
# Bitcoind has somewhat special way to return count for invalid input scripts:
|
|
759
|
+
# it returns 0 when the opcode can't be parsed or when it's over OP_16.
|
|
760
|
+
# Also, if the OP_{N} is used anywhere it's treated as 0-length data.
|
|
761
|
+
# See CScript::GetSigOpCount(const CScript& scriptSig) in bitcoind.
|
|
762
|
+
def sigops_count_for_p2sh
|
|
763
|
+
# This is a pay-to-script-hash scriptPubKey;
|
|
764
|
+
# get the last item that the scriptSig
|
|
765
|
+
# pushes onto the stack:
|
|
766
|
+
|
|
767
|
+
return 0 if @chunks.size == 0
|
|
768
|
+
|
|
769
|
+
data = nil
|
|
770
|
+
@chunks.each do |chunk|
|
|
771
|
+
case chunk
|
|
772
|
+
when Fixnum
|
|
773
|
+
data = ""
|
|
774
|
+
return 0 if chunk > OP_16
|
|
775
|
+
when String
|
|
776
|
+
data = chunk
|
|
777
|
+
end
|
|
778
|
+
end
|
|
779
|
+
return 0 if data == ""
|
|
780
|
+
|
|
781
|
+
::Bitcoin::Script.new(data).sigops_count_accurate(true)
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# Converts OP_{0,1,2,...,16} into 0, 1, 2, ..., 16.
|
|
785
|
+
# Returns nil for other opcodes.
|
|
786
|
+
def self.decode_OP_N(opcode)
|
|
787
|
+
if opcode == OP_0
|
|
788
|
+
return 0
|
|
789
|
+
end
|
|
790
|
+
if opcode.is_a?(Fixnum) && opcode >= OP_1 && opcode <= OP_16
|
|
791
|
+
return opcode - (OP_1 - 1);
|
|
792
|
+
else
|
|
793
|
+
nil
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
|
|
589
800
|
## OPCODES
|
|
590
801
|
|
|
591
802
|
# Does nothing
|
|
@@ -739,6 +950,7 @@ class Bitcoin::Script
|
|
|
739
950
|
@stack << (a + 1)
|
|
740
951
|
end
|
|
741
952
|
|
|
953
|
+
# 1 is subtracted from the input.
|
|
742
954
|
def op_1sub
|
|
743
955
|
a = pop_int
|
|
744
956
|
@stack << (a - 1)
|
|
@@ -757,15 +969,14 @@ class Bitcoin::Script
|
|
|
757
969
|
|
|
758
970
|
# Returns 1 if the inputs are exactly equal, 0 otherwise.
|
|
759
971
|
def op_equal
|
|
760
|
-
|
|
761
|
-
a, b = pop_int(2)
|
|
972
|
+
a, b = pop_string(2)
|
|
762
973
|
@stack << (a == b ? 1 : 0)
|
|
763
974
|
end
|
|
764
975
|
|
|
765
976
|
# Marks transaction as invalid if top stack value is not true. True is removed, but false is not.
|
|
766
977
|
def op_verify
|
|
767
978
|
res = pop_int
|
|
768
|
-
if res ==
|
|
979
|
+
if cast_to_bool(res) == false
|
|
769
980
|
@stack << res
|
|
770
981
|
@script_invalid = true # raise 'transaction invalid' ?
|
|
771
982
|
else
|
|
@@ -813,7 +1024,7 @@ class Bitcoin::Script
|
|
|
813
1024
|
|
|
814
1025
|
# If the input is true, duplicate it.
|
|
815
1026
|
def op_ifdup
|
|
816
|
-
if
|
|
1027
|
+
if cast_to_bool(@stack.last) == true
|
|
817
1028
|
@stack << @stack.last
|
|
818
1029
|
end
|
|
819
1030
|
end
|
|
@@ -861,8 +1072,8 @@ class Bitcoin::Script
|
|
|
861
1072
|
def op_if
|
|
862
1073
|
value = false
|
|
863
1074
|
if @do_exec
|
|
864
|
-
return if @stack.size < 1
|
|
865
|
-
value =
|
|
1075
|
+
(invalid; return) if @stack.size < 1
|
|
1076
|
+
value = cast_to_bool(pop_string) == false ? false : true
|
|
866
1077
|
end
|
|
867
1078
|
@exec_stack << value
|
|
868
1079
|
end
|
|
@@ -871,8 +1082,8 @@ class Bitcoin::Script
|
|
|
871
1082
|
def op_notif
|
|
872
1083
|
value = false
|
|
873
1084
|
if @do_exec
|
|
874
|
-
return if @stack.size < 1
|
|
875
|
-
value =
|
|
1085
|
+
(invalid; return) if @stack.size < 1
|
|
1086
|
+
value = cast_to_bool(pop_string) == false ? true : false
|
|
876
1087
|
end
|
|
877
1088
|
@exec_stack << value
|
|
878
1089
|
end
|
|
@@ -891,14 +1102,24 @@ class Bitcoin::Script
|
|
|
891
1102
|
|
|
892
1103
|
# The item n back in the stack is copied to the top.
|
|
893
1104
|
def op_pick
|
|
1105
|
+
return invalid if @stack.size < 2
|
|
894
1106
|
pos = pop_int
|
|
1107
|
+
return invalid if (pos < 0) || (pos >= @stack.size)
|
|
895
1108
|
item = @stack[-(pos+1)]
|
|
896
1109
|
@stack << item if item
|
|
897
1110
|
end
|
|
898
1111
|
|
|
1112
|
+
# The fifth and sixth items back are moved to the top of the stack.
|
|
1113
|
+
def op_2rot
|
|
1114
|
+
return invalid if @stack.size < 6
|
|
1115
|
+
@stack[-6..-1] = [ *@stack[-4..-1], *@stack[-6..-5] ]
|
|
1116
|
+
end
|
|
1117
|
+
|
|
899
1118
|
# The item n back in the stack is moved to the top.
|
|
900
1119
|
def op_roll
|
|
1120
|
+
return invalid if @stack.size < 2
|
|
901
1121
|
pos = pop_int
|
|
1122
|
+
return invalid if (pos < 0) || (pos >= @stack.size)
|
|
902
1123
|
idx = -(pos+1)
|
|
903
1124
|
item = @stack[idx]
|
|
904
1125
|
if item
|
|
@@ -959,21 +1180,43 @@ class Bitcoin::Script
|
|
|
959
1180
|
end
|
|
960
1181
|
|
|
961
1182
|
def cast_to_bignum(buf)
|
|
1183
|
+
return (invalid; 0) unless buf
|
|
962
1184
|
case buf
|
|
963
|
-
when Numeric
|
|
964
|
-
|
|
1185
|
+
when Numeric
|
|
1186
|
+
invalid if OpenSSL::BN.new(buf.to_s).to_s(0).unpack("N")[0] > 4
|
|
1187
|
+
buf
|
|
1188
|
+
when String
|
|
1189
|
+
invalid if buf.bytesize > 4
|
|
1190
|
+
OpenSSL::BN.new([buf.bytesize].pack("N") + buf.reverse, 0).to_i
|
|
965
1191
|
else; raise TypeError, 'cast_to_bignum: failed to cast: %s (%s)' % [buf, buf.class]
|
|
966
1192
|
end
|
|
967
1193
|
end
|
|
968
1194
|
|
|
969
1195
|
def cast_to_string(buf)
|
|
1196
|
+
return (invalid; "") unless buf
|
|
970
1197
|
case buf
|
|
971
|
-
when Numeric; OpenSSL::BN.new(buf.to_s).to_s(0)[4..-1]
|
|
1198
|
+
when Numeric; OpenSSL::BN.new(buf.to_s).to_s(0)[4..-1].reverse
|
|
972
1199
|
when String; buf;
|
|
973
1200
|
else; raise TypeError, 'cast_to_string: failed to cast: %s (%s)' % [buf, buf.class]
|
|
974
1201
|
end
|
|
975
1202
|
end
|
|
976
1203
|
|
|
1204
|
+
def cast_to_bool(buf)
|
|
1205
|
+
buf = cast_to_string(buf).unpack("C*")
|
|
1206
|
+
size = buf.size
|
|
1207
|
+
buf.each.with_index{|byte,index|
|
|
1208
|
+
if byte != 0
|
|
1209
|
+
# Can be negative zero
|
|
1210
|
+
if (index == (size-1)) && byte == 0x80
|
|
1211
|
+
return false
|
|
1212
|
+
else
|
|
1213
|
+
return true
|
|
1214
|
+
end
|
|
1215
|
+
end
|
|
1216
|
+
}
|
|
1217
|
+
return false
|
|
1218
|
+
end
|
|
1219
|
+
|
|
977
1220
|
# Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
|
|
978
1221
|
def op_numequalverify
|
|
979
1222
|
op_numequal; op_verify
|
|
@@ -983,6 +1226,7 @@ class Bitcoin::Script
|
|
|
983
1226
|
# to the data after the most recently-executed OP_CODESEPARATOR.
|
|
984
1227
|
def op_codeseparator
|
|
985
1228
|
@codehash_start = @chunks.size - @chunks.reverse.index(OP_CODESEPARATOR)
|
|
1229
|
+
@last_codeseparator_index = @chunk_last_index
|
|
986
1230
|
end
|
|
987
1231
|
|
|
988
1232
|
def codehash_script(opcode)
|
|
@@ -998,9 +1242,9 @@ class Bitcoin::Script
|
|
|
998
1242
|
# This is used by Protocol::Tx#verify_input_signature
|
|
999
1243
|
def op_checksig(check_callback)
|
|
1000
1244
|
return invalid if @stack.size < 2
|
|
1001
|
-
pubkey = @stack.pop
|
|
1245
|
+
pubkey = cast_to_string(@stack.pop)
|
|
1002
1246
|
#return (@stack << 0) unless Bitcoin::Script.is_canonical_pubkey?(pubkey) # only for isStandard
|
|
1003
|
-
drop_sigs = [ @stack[-1] ]
|
|
1247
|
+
drop_sigs = [ cast_to_string(@stack[-1]) ]
|
|
1004
1248
|
|
|
1005
1249
|
signature = cast_to_string(@stack.pop)
|
|
1006
1250
|
#return (@stack << 0) unless Bitcoin::Script.is_canonical_signature?(signature) # only for isStandard
|
|
@@ -1008,21 +1252,25 @@ class Bitcoin::Script
|
|
|
1008
1252
|
|
|
1009
1253
|
sig, hash_type = parse_sig(signature)
|
|
1010
1254
|
|
|
1011
|
-
|
|
1012
|
-
script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
|
|
1013
|
-
drop_sigs = nil
|
|
1014
|
-
else
|
|
1015
|
-
script_code, drop_sigs = nil, nil
|
|
1016
|
-
end
|
|
1255
|
+
subscript = sighash_subscript(drop_sigs)
|
|
1017
1256
|
|
|
1018
1257
|
if check_callback == nil # for tests
|
|
1019
1258
|
@stack << 1
|
|
1020
1259
|
else # real signature check callback
|
|
1021
1260
|
@stack <<
|
|
1022
|
-
((check_callback.call(pubkey, sig, hash_type,
|
|
1261
|
+
((check_callback.call(pubkey, sig, hash_type, subscript) == true) ? 1 : 0)
|
|
1023
1262
|
end
|
|
1024
1263
|
end
|
|
1025
1264
|
|
|
1265
|
+
def sighash_subscript(drop_sigs)
|
|
1266
|
+
if inner_p2sh? && @inner_script_code
|
|
1267
|
+
::Bitcoin::Script.new(@inner_script_code).to_binary_without_signatures(drop_sigs)
|
|
1268
|
+
else
|
|
1269
|
+
to_binary_without_signatures(drop_sigs)
|
|
1270
|
+
end
|
|
1271
|
+
end
|
|
1272
|
+
|
|
1273
|
+
# Same as OP_CHECKSIG, but OP_VERIFY is executed afterward.
|
|
1026
1274
|
def op_checksigverify(check_callback)
|
|
1027
1275
|
op_checksig(check_callback)
|
|
1028
1276
|
op_verify
|
|
@@ -1053,22 +1301,23 @@ class Bitcoin::Script
|
|
|
1053
1301
|
n_sigs = pop_int
|
|
1054
1302
|
return invalid if n_sigs < 0 || n_sigs > n_pubkeys
|
|
1055
1303
|
return invalid if @stack.size < n_sigs
|
|
1056
|
-
sigs =
|
|
1304
|
+
sigs = pop_string(n_sigs)
|
|
1305
|
+
drop_sigs = sigs.dup
|
|
1057
1306
|
|
|
1058
|
-
|
|
1307
|
+
# Bitcoin-core removes an extra item from the stack
|
|
1308
|
+
@stack.pop
|
|
1059
1309
|
|
|
1060
|
-
|
|
1061
|
-
script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
|
|
1062
|
-
drop_sigs = nil
|
|
1063
|
-
else
|
|
1064
|
-
script_code, drop_sigs = nil, nil
|
|
1065
|
-
end
|
|
1310
|
+
subscript = sighash_subscript(drop_sigs)
|
|
1066
1311
|
|
|
1067
1312
|
success = true
|
|
1068
1313
|
while success && n_sigs > 0
|
|
1069
1314
|
sig, pub = sigs.pop, pubkeys.pop
|
|
1315
|
+
unless sig && sig.size > 0
|
|
1316
|
+
success = false
|
|
1317
|
+
break
|
|
1318
|
+
end
|
|
1070
1319
|
signature, hash_type = parse_sig(sig)
|
|
1071
|
-
if check_callback.call(pub, signature, hash_type,
|
|
1320
|
+
if pub.size > 0 && check_callback.call(pub, signature, hash_type, subscript)
|
|
1072
1321
|
n_sigs -= 1
|
|
1073
1322
|
else
|
|
1074
1323
|
sigs << sig
|
|
@@ -1077,7 +1326,13 @@ class Bitcoin::Script
|
|
|
1077
1326
|
success = false if n_sigs > n_pubkeys
|
|
1078
1327
|
end
|
|
1079
1328
|
|
|
1080
|
-
@stack << (success ? 1 :
|
|
1329
|
+
@stack << (success ? 1 : 0)
|
|
1330
|
+
end
|
|
1331
|
+
|
|
1332
|
+
# Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward.
|
|
1333
|
+
def op_checkmultisigverify(check_callback)
|
|
1334
|
+
op_checkmultisig(check_callback)
|
|
1335
|
+
op_verify
|
|
1081
1336
|
end
|
|
1082
1337
|
|
|
1083
1338
|
# op_eval: https://en.bitcoin.it/wiki/BIP_0012
|
|
@@ -1105,7 +1360,6 @@ class Bitcoin::Script
|
|
|
1105
1360
|
end
|
|
1106
1361
|
|
|
1107
1362
|
|
|
1108
|
-
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
|
1109
1363
|
|
|
1110
1364
|
def self.is_canonical_signature?(sig)
|
|
1111
1365
|
return false if sig.bytesize < 9 # Non-canonical signature: too short
|