bitcoin-ruby 0.0.1

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