bitcoin-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. data/.gitignore +12 -0
  2. data/COPYING +18 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +189 -0
  5. data/Rakefile +104 -0
  6. data/bin/bitcoin_dns_seed +130 -0
  7. data/bin/bitcoin_gui +80 -0
  8. data/bin/bitcoin_node +174 -0
  9. data/bin/bitcoin_shell +12 -0
  10. data/bin/bitcoin_wallet +323 -0
  11. data/bitcoin-ruby.gemspec +27 -0
  12. data/concept-examples/blockchain-pow.rb +151 -0
  13. data/doc/CONFIG.rdoc +66 -0
  14. data/doc/EXAMPLES.rdoc +9 -0
  15. data/doc/NODE.rdoc +35 -0
  16. data/doc/STORAGE.rdoc +21 -0
  17. data/doc/WALLET.rdoc +102 -0
  18. data/examples/balance.rb +60 -0
  19. data/examples/bbe_verify_tx.rb +55 -0
  20. data/examples/connect.rb +36 -0
  21. data/examples/relay_tx.rb +22 -0
  22. data/examples/verify_tx.rb +57 -0
  23. data/lib/bitcoin.rb +370 -0
  24. data/lib/bitcoin/builder.rb +266 -0
  25. data/lib/bitcoin/config.rb +56 -0
  26. data/lib/bitcoin/connection.rb +126 -0
  27. data/lib/bitcoin/ffi/openssl.rb +121 -0
  28. data/lib/bitcoin/gui/addr_view.rb +42 -0
  29. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  30. data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
  31. data/lib/bitcoin/gui/conn_view.rb +36 -0
  32. data/lib/bitcoin/gui/connection.rb +68 -0
  33. data/lib/bitcoin/gui/em_gtk.rb +28 -0
  34. data/lib/bitcoin/gui/gui.builder +1643 -0
  35. data/lib/bitcoin/gui/gui.rb +290 -0
  36. data/lib/bitcoin/gui/helpers.rb +113 -0
  37. data/lib/bitcoin/gui/tree_view.rb +82 -0
  38. data/lib/bitcoin/gui/tx_view.rb +67 -0
  39. data/lib/bitcoin/key.rb +125 -0
  40. data/lib/bitcoin/logger.rb +65 -0
  41. data/lib/bitcoin/network/command_client.rb +93 -0
  42. data/lib/bitcoin/network/command_handler.rb +179 -0
  43. data/lib/bitcoin/network/connection_handler.rb +274 -0
  44. data/lib/bitcoin/network/node.rb +399 -0
  45. data/lib/bitcoin/protocol.rb +140 -0
  46. data/lib/bitcoin/protocol/address.rb +48 -0
  47. data/lib/bitcoin/protocol/alert.rb +47 -0
  48. data/lib/bitcoin/protocol/block.rb +154 -0
  49. data/lib/bitcoin/protocol/handler.rb +38 -0
  50. data/lib/bitcoin/protocol/parser.rb +148 -0
  51. data/lib/bitcoin/protocol/tx.rb +205 -0
  52. data/lib/bitcoin/protocol/txin.rb +97 -0
  53. data/lib/bitcoin/protocol/txout.rb +73 -0
  54. data/lib/bitcoin/protocol/version.rb +70 -0
  55. data/lib/bitcoin/script.rb +634 -0
  56. data/lib/bitcoin/storage/dummy.rb +164 -0
  57. data/lib/bitcoin/storage/models.rb +133 -0
  58. data/lib/bitcoin/storage/sequel.rb +335 -0
  59. data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
  60. data/lib/bitcoin/storage/storage.rb +243 -0
  61. data/lib/bitcoin/version.rb +3 -0
  62. data/lib/bitcoin/wallet/coinselector.rb +30 -0
  63. data/lib/bitcoin/wallet/keygenerator.rb +75 -0
  64. data/lib/bitcoin/wallet/keystore.rb +203 -0
  65. data/lib/bitcoin/wallet/txdp.rb +116 -0
  66. data/lib/bitcoin/wallet/wallet.rb +243 -0
  67. data/spec/bitcoin/bitcoin_spec.rb +472 -0
  68. data/spec/bitcoin/builder_spec.rb +90 -0
  69. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
  70. data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
  71. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
  72. data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
  73. data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
  74. data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
  75. data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
  76. data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
  77. data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
  78. data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
  79. data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
  80. data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
  81. data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
  82. data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
  83. data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
  84. data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
  85. data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
  86. data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
  87. data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
  88. data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
  89. data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
  90. data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
  91. data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
  92. data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
  93. data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
  94. data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
  95. data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
  96. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
  97. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
  98. data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
  99. data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
  100. data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
  101. data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
  102. data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
  103. data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
  104. data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
  105. data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
  106. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
  107. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
  108. data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
  109. data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
  110. data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
  111. data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
  112. data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
  113. data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
  114. data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
  115. data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
  116. data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
  117. data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
  118. data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
  119. data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
  120. data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
  121. data/spec/bitcoin/key_spec.rb +123 -0
  122. data/spec/bitcoin/network_spec.rb +48 -0
  123. data/spec/bitcoin/protocol/addr_spec.rb +68 -0
  124. data/spec/bitcoin/protocol/alert_spec.rb +20 -0
  125. data/spec/bitcoin/protocol/block_spec.rb +101 -0
  126. data/spec/bitcoin/protocol/inv_spec.rb +124 -0
  127. data/spec/bitcoin/protocol/ping_spec.rb +49 -0
  128. data/spec/bitcoin/protocol/tx_spec.rb +226 -0
  129. data/spec/bitcoin/protocol/version_spec.rb +77 -0
  130. data/spec/bitcoin/reorg_spec.rb +129 -0
  131. data/spec/bitcoin/script/opcodes_spec.rb +417 -0
  132. data/spec/bitcoin/script/script_spec.rb +246 -0
  133. data/spec/bitcoin/spec_helper.rb +36 -0
  134. data/spec/bitcoin/storage_spec.rb +229 -0
  135. data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
  136. data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
  137. data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
  138. data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
  139. data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
  140. metadata +295 -0
@@ -0,0 +1,634 @@
1
+ require 'bitcoin'
2
+
3
+ class Bitcoin::Script
4
+
5
+ OP_1 = 81
6
+ OP_TRUE = 81
7
+ OP_0 = 0
8
+ OP_FALSE = 0
9
+ OP_PUSHDATA1 = 76
10
+ OP_PUSHDATA2 = 77
11
+ OP_PUSHDATA4 = 78
12
+ OP_NOP = 97
13
+ OP_DUP = 118
14
+ OP_HASH160 = 169
15
+ OP_EQUAL = 135
16
+ OP_VERIFY = 105
17
+ OP_EQUALVERIFY = 136
18
+ OP_CHECKSIG = 172
19
+ OP_CHECKSIGVERIFY = 173
20
+ OP_CHECKMULTISIG = 174
21
+ OP_CHECKMULTISIGVERIFY = 175
22
+ OP_TOALTSTACK = 107
23
+ OP_FROMALTSTACK = 108
24
+ OP_TUCK = 125
25
+ OP_SWAP = 124
26
+ OP_BOOLAND = 154
27
+ OP_ADD = 147
28
+ OP_SUB = 148
29
+ OP_GREATERTHANOREQUAL = 162
30
+ OP_DROP = 117
31
+ OP_HASH256 = 170
32
+ OP_SHA256 = 168
33
+ OP_SHA1 = 167
34
+ OP_RIPEMD160 = 166
35
+ OP_EVAL = 176
36
+ OP_NOP2 = 177
37
+ OP_CHECKHASHVERIFY = 177
38
+ OP_CODESEPARATOR = 171
39
+ OP_MIN = 163
40
+ OP_MAX = 164
41
+ OP_2OVER = 112
42
+ OP_2SWAP = 114
43
+ OP_IFDUP = 115
44
+ OP_DEPTH = 116
45
+ OP_1NEGATE = 79
46
+ # OP_IF = 99
47
+ # OP_NOTIF = 100
48
+ # OP_ELSE = 103
49
+ # OP_ENDIF = 104
50
+
51
+ OPCODES = Hash[*constants.grep(/^OP_/).map{|i| [const_get(i), i.to_s] }.flatten]
52
+ OPCODES[0] = "0"
53
+ OPCODES[81] = "1"
54
+
55
+ OPCODES_ALIAS = {
56
+ "OP_TRUE" => OP_1,
57
+ "OP_FALSE" => OP_0,
58
+ "OP_NOP1" => OP_EVAL,
59
+ "OP_NOP2" => OP_CHECKHASHVERIFY
60
+ }
61
+
62
+ OP_2_16 = (82..96).to_a
63
+
64
+ attr_reader :raw, :chunks, :debug
65
+
66
+ # create a new script. +bytes+ is typically input_script + output_script
67
+ def initialize(bytes, offset=0)
68
+ @raw = bytes
69
+ @stack, @stack_alt = [], []
70
+ @chunks = parse(bytes, offset)
71
+ end
72
+
73
+ # parse raw script
74
+ def parse(bytes, offset=0)
75
+ program = bytes.unpack("C*")
76
+ chunks = []
77
+ until program.empty?
78
+ opcode = program.shift(1)[0]
79
+ if opcode >= 0xf0
80
+ opcode = (opcode << 8) | program.shift(1)[0]
81
+ end
82
+
83
+ if (opcode > 0) && (opcode < OP_PUSHDATA1)
84
+ len = opcode
85
+ chunks << program.shift(len).pack("C*")
86
+ elsif (opcode == OP_PUSHDATA1)
87
+ len = program.shift(1)[0]
88
+ chunks << program.shift(len).pack("C*")
89
+ elsif (opcode == OP_PUSHDATA2)
90
+ len = program.shift(2).pack("C*").unpack("n")[0]
91
+ chunks << program.shift(len).pack("C*")
92
+ elsif (opcode == OP_PUSHDATA4)
93
+ len = program.shift(4).pack("C*").unpack("N")[0]
94
+ chunks << program.shift(len).pack("C*")
95
+ else
96
+ chunks << opcode
97
+ end
98
+ end
99
+ chunks
100
+ end
101
+
102
+ # string representation of the script
103
+ def to_string(chunks=nil)
104
+ (chunks || @chunks).map{|i|
105
+ case i
106
+ when Fixnum
107
+ case i
108
+ when *OPCODES.keys; OPCODES[i]
109
+ when *OP_2_16; (OP_2_16.index(i)+2).to_s
110
+ else "(opcode #{i})"
111
+ end
112
+ when String
113
+ i.unpack("H*")[0]
114
+ end
115
+ }.join(" ")
116
+ end
117
+
118
+ # script object of a string representation
119
+ def self.from_string(script_string)
120
+ new(binary_from_string(script_string))
121
+ end
122
+
123
+ class ScriptOpcodeError < StandardError; end
124
+
125
+ # raw script binary of a string representation
126
+ def self.binary_from_string(script_string)
127
+ script_string.split(" ").map{|i|
128
+ case i
129
+ when /^OP_PUSHDATA[124]$/; # skip
130
+ when *OPCODES.values; OPCODES.find{|k,v| v == i }.first
131
+ when *OPCODES_ALIAS.keys; OPCODES_ALIAS.find{|k,v| k == i }.last
132
+ when /^([2-9]|1[0-6])$/; OP_2_16[$1.to_i-2]
133
+ when /\(opcode (\d+)\)/; $1.to_i
134
+ when /OP_(.+)$/; raise ScriptOpcodeError, "#{i} not defined!"
135
+ else
136
+ data = [i].pack("H*")
137
+ size = data.bytesize
138
+
139
+ head = if size < OP_PUSHDATA1
140
+ [size].pack("C")
141
+ elsif size > OP_PUSHDATA1 && size <= 0xff
142
+ [OP_PUSHDATA1, size].pack("CC")
143
+ elsif size > 0xff && size <= 0xffff
144
+ [OP_PUSHDATA2, size].pack("Cv")
145
+ elsif size > 0xffff && size <= 0xffffffff
146
+ [OP_PUSHDATA4, size].pack("CV")
147
+ end
148
+
149
+ head + data
150
+ end
151
+ }.map{|i|
152
+ i.is_a?(Fixnum) ? [i].pack("C*") : i # TODO yikes, implement/pack 2 byte opcodes.
153
+ }.join
154
+ end
155
+
156
+ def invalid?
157
+ @script_invalid ||= false
158
+ end
159
+
160
+ # run the script. +check_callback+ is called for OP_CHECKSIG operations
161
+ def run(&check_callback)
162
+ return pay_to_script_hash(check_callback) if is_p2sh?
163
+ @debug = []
164
+ @chunks.each{|chunk|
165
+ break if invalid?
166
+ @debug << @stack.map{|i| i.unpack("H*") rescue i}
167
+
168
+ case chunk
169
+ when Fixnum
170
+ case chunk
171
+
172
+ when *OPCODES_METHOD.keys
173
+ m = method( n=OPCODES_METHOD[chunk] )
174
+ @debug << n.to_s.upcase
175
+ (m.arity == 1) ? m.call(check_callback) : m.call # invoke opcode method
176
+
177
+ when *OP_2_16
178
+ @stack << OP_2_16.index(chunk) + 2
179
+ @debug << "OP_#{chunk-80}"
180
+ else
181
+ name = OPCODES[chunk] || chunk
182
+ raise "opcode #{name} unkown or not implemented"
183
+ end
184
+ when String
185
+ @debug << "PUSH DATA #{chunk.unpack("H*")[0]}"
186
+ @stack << chunk
187
+ end
188
+ }
189
+ @debug << @stack.map{|i| i.unpack("H*") rescue i }
190
+
191
+ if @script_invalid
192
+ @stack << 0
193
+ @debug << "INVALID TRANSACTION"
194
+ end
195
+
196
+ @debug << "RESULT"
197
+ return false if @stack.empty?
198
+ return false if @stack.pop == 0
199
+ true
200
+ end
201
+
202
+ def invalid
203
+ @script_invalid = true; nil
204
+ end
205
+
206
+ def codehash_script(opcode)
207
+ # CScript scriptCode(pbegincodehash, pend);
208
+ script = to_string(@chunks[(@codehash_start||0)...@chunks.size-@chunks.reverse.index(opcode)])
209
+ checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
210
+ [script, checkhash]
211
+ end
212
+
213
+ def self.drop_signatures(script_pubkey, drop_signatures)
214
+ script = new(script_pubkey).to_string.split(" ").delete_if{|c| drop_signatures.include?(c) }.join(" ")
215
+ script_pubkey = binary_from_string(script)
216
+ end
217
+
218
+ # pay_to_script_hash: https://en.bitcoin.it/wiki/BIP_0016
219
+ #
220
+ # <sig> {<pub> OP_CHECKSIG} | OP_HASH160 <script_hash> OP_EQUAL
221
+ def pay_to_script_hash(check_callback)
222
+ return false unless @chunks.size == 5
223
+ script_hash = @chunks[-2]
224
+ script = @chunks[-4]
225
+ sig = self.class.from_string(@chunks[0].unpack("H*")[0]).raw
226
+
227
+ return false unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
228
+ script = self.class.new(sig + script)
229
+ script.run(&check_callback)
230
+ end
231
+
232
+ def is_pay_to_script_hash?
233
+ @chunks.size >= 3 && @chunks[-3] == OP_HASH160 &&
234
+ @chunks[-2].bytesize == 20 && @chunks[-1] == OP_EQUAL
235
+ end
236
+ alias :is_p2sh? :is_pay_to_script_hash?
237
+
238
+ # check if script is in one of the recognized standard formats
239
+ def is_standard?
240
+ is_pubkey? || is_hash160? || is_multisig? || is_p2sh?
241
+ end
242
+
243
+ # is this a pubkey tx
244
+ def is_pubkey?
245
+ return false if @chunks.size != 2
246
+ (@chunks[1] == OP_CHECKSIG) && @chunks[0].size > 1
247
+ end
248
+ alias :is_send_to_ip? :is_pubkey?
249
+
250
+ # is this a hash160 (address) tx
251
+ def is_hash160?
252
+ return false if @chunks.size != 5
253
+ (@chunks[0..1] + @chunks[-2..-1]) ==
254
+ [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] &&
255
+ @chunks[2].is_a?(String) && @chunks[2].bytesize == 20
256
+ end
257
+
258
+ # is this a multisig tx
259
+ def is_multisig?
260
+ return false if @chunks.size > 6 || @chunks.size < 4
261
+ @chunks[-1] == OP_CHECKMULTISIG
262
+ end
263
+
264
+ def type
265
+ if is_hash160?; :hash160
266
+ elsif is_pubkey?; :pubkey
267
+ elsif is_multisig?; :multisig
268
+ elsif is_p2sh?; :p2sh
269
+ else; :unknown
270
+ end
271
+ end
272
+
273
+ # get the public key for this pubkey script
274
+ def get_pubkey
275
+ return @chunks[0].unpack("H*")[0] if @chunks.size == 1
276
+ is_pubkey? ? @chunks[0].unpack("H*")[0] : nil
277
+ end
278
+
279
+ # get the pubkey address for this pubkey script
280
+ def get_pubkey_address
281
+ Bitcoin.pubkey_to_address(get_pubkey)
282
+ end
283
+
284
+ # get the hash160 for this hash160 script
285
+ def get_hash160
286
+ return @chunks[2..-3][0].unpack("H*")[0] if is_hash160?
287
+ return Bitcoin.hash160(get_pubkey) if is_pubkey?
288
+ end
289
+
290
+ # get the hash160 address for this hash160 script
291
+ def get_hash160_address
292
+ Bitcoin.hash160_to_address(get_hash160)
293
+ end
294
+
295
+ # get the public keys for this multisig script
296
+ def get_multisig_pubkeys
297
+ 1.upto(@chunks[-2] - 80).map {|i| @chunks[i]}
298
+ end
299
+
300
+ # get the pubkey addresses for this multisig script
301
+ def get_multisig_addresses
302
+ get_multisig_pubkeys.map {|p| Bitcoin::Key.new(nil, p.unpack("H*")[0]).addr}
303
+ end
304
+
305
+ # get all addresses this script corresponds to (if possible)
306
+ def get_addresses
307
+ return [get_pubkey_address] if is_pubkey?
308
+ return [get_hash160_address] if is_hash160?
309
+ return get_multisig_addresses if is_multisig?
310
+ end
311
+
312
+ # get single address, or first for multisig script
313
+ def get_address
314
+ addrs = get_addresses
315
+ addrs.is_a?(Array) ? addrs[0] : addrs
316
+ end
317
+
318
+ # generate pubkey tx script for given +pubkey+
319
+ def self.to_pubkey_script(pubkey)
320
+ pk = [pubkey].pack("H*")
321
+ [[pk.bytesize].pack("C"), pk, "\xAC"].join
322
+ end
323
+
324
+ # generate hash160 tx for given +address+
325
+ def self.to_hash160_script(hash160)
326
+ return nil unless hash160
327
+ # DUP HASH160 length hash160 EQUALVERIFY CHECKSIG
328
+ [ ["76", "a9", "14", hash160, "88", "ac"].join ].pack("H*")
329
+ end
330
+
331
+ def self.to_p2sh_script(p2sh)
332
+ return nil unless p2sh
333
+ # HASH160 length hash EQUAL
334
+ [ ["a9", "14", p2sh, "87"].join ].pack("H*")
335
+ end
336
+
337
+ def self.to_address_script(address)
338
+ hash160 = Bitcoin.hash160_from_address(address)
339
+ case Bitcoin.address_type(address)
340
+ when :hash160; to_hash160_script(hash160)
341
+ when :p2sh; to_p2sh_script(hash160)
342
+ end
343
+ end
344
+
345
+ # generate multisig tx for given +pubkeys+, expecting +m+ signatures
346
+ def self.to_multisig_script(m, *pubkeys)
347
+ pubs = pubkeys.map{|pk|p=[pk].pack("H*"); [p.bytesize].pack("C") + p}
348
+ [ [80 + m.to_i].pack("C"), *pubs, [80 + pubs.size].pack("C"), "\xAE"].join
349
+ end
350
+
351
+ # generate pubkey script sig for given +signature+ and +pubkey+
352
+ def self.to_pubkey_script_sig(signature, pubkey)
353
+ hash_type = "\x01"
354
+ #pubkey = [pubkey].pack("H*") if pubkey.bytesize != 65
355
+ raise "pubkey is not in binary form" unless pubkey.bytesize == 65 && pubkey[0] == "\x04"
356
+ [ [signature.bytesize+1].pack("C"), signature, hash_type, [pubkey.bytesize].pack("C"), pubkey ].join
357
+ end
358
+
359
+ # alias for #to_pubkey_script_sig
360
+ def self.to_signature_pubkey_script(*a)
361
+ to_pubkey_script_sig(*a)
362
+ end
363
+
364
+ def self.to_multisig_script_sig(*sigs)
365
+ from_string("0 #{sigs.map{|s|s.unpack('H*')[0]}.join(' ')}").raw
366
+ end
367
+
368
+ def get_signatures_required
369
+ return false unless is_multisig?
370
+ @chunks[0] - 80
371
+ end
372
+
373
+ ## OPCODES
374
+
375
+ # Does nothing
376
+ def op_nop
377
+ end
378
+
379
+ # Duplicates the top stack item.
380
+ def op_dup
381
+ @stack << (@stack[-1].dup rescue @stack[-1])
382
+ end
383
+
384
+ # The input is hashed using SHA-256.
385
+ def op_sha256
386
+ buf = @stack.pop
387
+ @stack << Digest::SHA256.digest(buf)
388
+ end
389
+
390
+ # The input is hashed using SHA-1.
391
+ def op_sha1
392
+ buf = @stack.pop
393
+ @stack << Digest::SHA1.digest(buf)
394
+ end
395
+
396
+ # The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
397
+ def op_hash160
398
+ buf = @stack.pop
399
+ @stack << Digest::RMD160.digest(Digest::SHA256.digest(buf))
400
+ end
401
+
402
+ # The input is hashed using RIPEMD-160.
403
+ def op_ripemd160
404
+ buf = @stack.pop
405
+ @stack << Digest::RMD160.digest(buf)
406
+ end
407
+
408
+ # The input is hashed two times with SHA-256.
409
+ def op_hash256
410
+ buf = @stack.pop
411
+ @stack << Digest::SHA256.digest(Digest::SHA256.digest(buf))
412
+ end
413
+
414
+ # Puts the input onto the top of the alt stack. Removes it from the main stack.
415
+ def op_toaltstack
416
+ @stack_alt << @stack.pop
417
+ end
418
+
419
+ # Puts the input onto the top of the main stack. Removes it from the alt stack.
420
+ def op_fromaltstack
421
+ @stack << @stack_alt.pop
422
+ end
423
+
424
+ # The item at the top of the stack is copied and inserted before the second-to-top item.
425
+ def op_tuck
426
+ @stack[-2..-1] = [ @stack[-1], *@stack[-2..-1] ]
427
+ end
428
+
429
+ # The top two items on the stack are swapped.
430
+ def op_swap
431
+ @stack[-2..-1] = @stack[-2..-1].reverse
432
+ end
433
+
434
+ # If both a and b are not 0, the output is 1. Otherwise 0.
435
+ def op_booland
436
+ a, b = @stack.pop(2)
437
+ @stack << (![a,b].any?{|n| n == 0 } ? 1 : 0)
438
+ end
439
+
440
+ # a is added to b.
441
+ def op_add
442
+ a, b = @stack.pop(2).reverse
443
+ @stack << a + b
444
+ end
445
+
446
+ # b is subtracted from a.
447
+ def op_sub
448
+ a, b = @stack.pop(2).reverse
449
+ @stack << a - b
450
+ end
451
+
452
+ # Returns 1 if a is greater than or equal to b, 0 otherwise.
453
+ def op_greaterthanorequal
454
+ a, b = @stack.pop(2).reverse
455
+ @stack << (a >= b ? 1 : 0)
456
+ end
457
+
458
+ # Removes the top stack item.
459
+ def op_drop
460
+ @stack.pop
461
+ end
462
+
463
+ # Returns 1 if the inputs are exactly equal, 0 otherwise.
464
+ def op_equal
465
+ a, b = @stack.pop(2).reverse
466
+ @stack << (a == b ? 1 : 0)
467
+ end
468
+
469
+ # Marks transaction as invalid if top stack value is not true. True is removed, but false is not.
470
+ def op_verify
471
+ res = @stack.pop
472
+ if res == 0
473
+ @stack << res
474
+ @script_invalid = true # raise 'transaction invalid' ?
475
+ else
476
+ @script_invalid = false
477
+ end
478
+ end
479
+
480
+ # Same as OP_EQUAL, but runs OP_VERIFY afterward.
481
+ def op_equalverify
482
+ op_equal; op_verify
483
+ end
484
+
485
+ # An empty array of bytes is pushed onto the stack.
486
+ def op_0
487
+ @stack << "" # []
488
+ end
489
+
490
+ # The number 1 is pushed onto the stack. Same as OP_TRUE
491
+ def op_1
492
+ @stack << 1
493
+ end
494
+
495
+ # Returns the smaller of a and b.
496
+ def op_min
497
+ @stack << @stack.pop(2).min
498
+ end
499
+
500
+ # Returns the larger of a and b.
501
+ def op_max
502
+ @stack << @stack.pop(2).max
503
+ end
504
+
505
+ # Copies the pair of items two spaces back in the stack to the front.
506
+ def op_2over
507
+ @stack << @stack[-4]
508
+ @stack << @stack[-4]
509
+ end
510
+
511
+ # Swaps the top two pairs of items.
512
+ def op_2swap
513
+ p1 = @stack.pop(2)
514
+ p2 = @stack.pop(2)
515
+ @stack += p1 += p2
516
+ end
517
+
518
+ # If the input is true, duplicate it.
519
+ def op_ifdup
520
+ if @stack.last != 0
521
+ @stack << @stack.last
522
+ end
523
+ end
524
+
525
+ # The number -1 is pushed onto the stack.
526
+ def op_1negate
527
+ @stack << -1
528
+ end
529
+
530
+ # Puts the number of stack items onto the stack.
531
+ def op_depth
532
+ @stack << @stack.size
533
+ end
534
+
535
+ # https://en.bitcoin.it/wiki/BIP_0017 (old OP_NOP2)
536
+ # TODO: don't rely on it yet. add guards from wikipage too.
537
+ def op_checkhashverify
538
+ unless @checkhash && (@checkhash == @stack[-1].unpack("H*")[0])
539
+ @script_invalid = true
540
+ end
541
+ end
542
+
543
+ # All of the signature checking words will only match signatures
544
+ # to the data after the most recently-executed OP_CODESEPARATOR.
545
+ def op_codeseparator
546
+ @codehash_start = @chunks.size - @chunks.reverse.index(OP_CODESEPARATOR)
547
+ end
548
+
549
+ # do a CHECKSIG operation on the current stack,
550
+ # asking +check_callback+ to do the actual signature verification.
551
+ # This is used by Protocol::Tx#verify_input_signature
552
+ def op_checksig(check_callback)
553
+ return invalid if @stack.size < 2
554
+ pubkey = @stack.pop
555
+ drop_sigs = [@stack[-1].unpack("H*")[0]]
556
+ sig, hash_type = parse_sig(@stack.pop)
557
+
558
+ if @chunks.include?(OP_CHECKHASHVERIFY)
559
+ # Subset of script starting at the most recent codeseparator to OP_CHECKSIG
560
+ script_code, @checkhash = codehash_script(OP_CHECKSIG)
561
+ else
562
+ script_code, drop_sigs = nil, nil
563
+ end
564
+
565
+ if check_callback == nil # for tests
566
+ @stack << 1
567
+ else # real signature check callback
568
+ @stack <<
569
+ ((check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code) == true) ? 1 : 0)
570
+ end
571
+ end
572
+
573
+ def op_checksigverify(check_callback)
574
+ op_checksig(check_callback)
575
+ op_verify
576
+ end
577
+
578
+ # do a CHECKMULTISIG operation on the current stack,
579
+ # asking +check_callback+ to do the actual signature verification.
580
+ #
581
+ # CHECKMULTISIG does a m-of-n signatures verification on scripts of the form:
582
+ # 0 <sig1> <sig2> | 2 <pub1> <pub2> 2 OP_CHECKMULTISIG
583
+ # 0 <sig1> <sig2> | 2 <pub1> <pub2> <pub3> 3 OP_CHECKMULTISIG
584
+ # 0 <sig1> <sig2> <sig3> | 3 <pub1> <pub2> <pub3> 3 OP_CHECKMULTISIG
585
+ #
586
+ # see https://en.bitcoin.it/wiki/BIP_0011 for details.
587
+ # see https://github.com/bitcoin/bitcoin/blob/master/src/script.cpp#L931
588
+ #
589
+ # TODO: validate signature order
590
+ # TODO: take global opcode count
591
+ def op_checkmultisig(check_callback)
592
+ n_pubkeys = @stack.pop
593
+ return invalid unless (0..20).include?(n_pubkeys)
594
+ return invalid unless @stack.last(n_pubkeys).all?{|e| e.is_a?(String) && e != '' }
595
+ #return invalid if ((@op_count ||= 0) += n_pubkeys) > 201
596
+ pubkeys = @stack.pop(n_pubkeys)
597
+
598
+ n_sigs = @stack.pop
599
+ return invalid unless (0..n_pubkeys).include?(n_sigs)
600
+ return invalid unless @stack.last(n_sigs).all?{|e| e.is_a?(String) && e != '' }
601
+ sigs = (drop_sigs = @stack.pop(n_sigs)).map{|s| parse_sig(s) }
602
+
603
+ @stack.pop if @stack[-1] == '' # remove OP_NOP from stack
604
+
605
+ if @chunks.include?(OP_CHECKHASHVERIFY)
606
+ # Subset of script starting at the most recent codeseparator to OP_CHECKMULTISIG
607
+ script_code, @checkhash = codehash_script(OP_CHECKMULTISIG)
608
+ drop_sigs.map!{|i| i.unpack("H*")[0] }
609
+ else
610
+ script_code, drop_sigs = nil, nil
611
+ end
612
+
613
+ valid_sigs = 0
614
+ sigs.each{|sig, hash_type| pubkeys.each{|pubkey|
615
+ valid_sigs += 1 if check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code)
616
+ }}
617
+
618
+ @stack << ((valid_sigs == n_sigs) ? 1 : (invalid; 0))
619
+ end
620
+
621
+ OPCODES_METHOD = Hash[*instance_methods.grep(/^op_/).map{|m|
622
+ [ (OPCODES.find{|k,v| v == m.to_s.upcase }.first rescue nil), m ]
623
+ }.flatten]
624
+ OPCODES_METHOD[0] = :op_0
625
+ OPCODES_METHOD[81] = :op_1
626
+
627
+ private
628
+
629
+ def parse_sig(sig)
630
+ hash_type = sig[-1].unpack("C")[0]
631
+ sig = sig[0...-1]
632
+ return sig, hash_type
633
+ end
634
+ end