bitcoin-ruby 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/.gitignore +4 -1
  2. data/Gemfile +21 -0
  3. data/README.rdoc +85 -25
  4. data/Rakefile +7 -3
  5. data/bin/bitcoin_node +39 -42
  6. data/bin/bitcoin_shell +1 -0
  7. data/bin/bitcoin_wallet +129 -53
  8. data/bitcoin-ruby.gemspec +4 -7
  9. data/concept-examples/blockchain-pow.rb +1 -1
  10. data/doc/CONFIG.rdoc +5 -5
  11. data/doc/EXAMPLES.rdoc +9 -5
  12. data/doc/NAMECOIN.rdoc +34 -0
  13. data/doc/NODE.rdoc +147 -10
  14. data/examples/balance.rb +10 -4
  15. data/examples/bbe_verify_tx.rb +7 -2
  16. data/examples/forwarder.rb +73 -0
  17. data/examples/generate_tx.rb +34 -0
  18. data/examples/simple_network_monitor_and_util.rb +187 -0
  19. data/examples/verify_tx.rb +1 -1
  20. data/lib/bitcoin.rb +308 -18
  21. data/lib/bitcoin/builder.rb +62 -36
  22. data/lib/bitcoin/config.rb +2 -0
  23. data/lib/bitcoin/connection.rb +11 -8
  24. data/lib/bitcoin/electrum/mnemonic.rb +162 -0
  25. data/lib/bitcoin/ffi/openssl.rb +187 -21
  26. data/lib/bitcoin/gui/addr_view.rb +2 -0
  27. data/lib/bitcoin/gui/conn_view.rb +2 -0
  28. data/lib/bitcoin/gui/connection.rb +2 -0
  29. data/lib/bitcoin/gui/em_gtk.rb +2 -0
  30. data/lib/bitcoin/gui/gui.rb +2 -0
  31. data/lib/bitcoin/gui/helpers.rb +2 -0
  32. data/lib/bitcoin/gui/tree_view.rb +2 -0
  33. data/lib/bitcoin/gui/tx_view.rb +2 -0
  34. data/lib/bitcoin/key.rb +77 -11
  35. data/lib/bitcoin/litecoin.rb +81 -0
  36. data/lib/bitcoin/logger.rb +20 -1
  37. data/lib/bitcoin/namecoin.rb +279 -0
  38. data/lib/bitcoin/network/command_client.rb +7 -6
  39. data/lib/bitcoin/network/command_handler.rb +229 -43
  40. data/lib/bitcoin/network/connection_handler.rb +182 -70
  41. data/lib/bitcoin/network/node.rb +231 -106
  42. data/lib/bitcoin/protocol.rb +44 -23
  43. data/lib/bitcoin/protocol/address.rb +5 -3
  44. data/lib/bitcoin/protocol/alert.rb +3 -4
  45. data/lib/bitcoin/protocol/aux_pow.rb +123 -0
  46. data/lib/bitcoin/protocol/block.rb +98 -18
  47. data/lib/bitcoin/protocol/handler.rb +6 -5
  48. data/lib/bitcoin/protocol/parser.rb +44 -19
  49. data/lib/bitcoin/protocol/tx.rb +105 -52
  50. data/lib/bitcoin/protocol/txin.rb +39 -19
  51. data/lib/bitcoin/protocol/txout.rb +28 -13
  52. data/lib/bitcoin/protocol/version.rb +16 -7
  53. data/lib/bitcoin/script.rb +579 -122
  54. data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
  55. data/lib/bitcoin/storage/models.rb +20 -7
  56. data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
  57. data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
  58. data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
  59. data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
  60. data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
  61. data/lib/bitcoin/storage/storage.rb +233 -28
  62. data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
  63. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
  64. data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
  65. data/lib/bitcoin/validation.rb +369 -0
  66. data/lib/bitcoin/version.rb +1 -1
  67. data/lib/bitcoin/wallet/coinselector.rb +3 -0
  68. data/lib/bitcoin/wallet/keygenerator.rb +3 -1
  69. data/lib/bitcoin/wallet/keystore.rb +6 -2
  70. data/lib/bitcoin/wallet/txdp.rb +6 -4
  71. data/lib/bitcoin/wallet/wallet.rb +54 -16
  72. data/spec/bitcoin/bitcoin_spec.rb +48 -3
  73. data/spec/bitcoin/builder_spec.rb +40 -17
  74. data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
  75. data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
  76. data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
  77. data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
  78. data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
  79. data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
  80. data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
  81. data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
  82. data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
  83. data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
  84. data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
  85. data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
  86. data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
  87. data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
  88. data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
  89. data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
  90. data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
  91. data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
  92. data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
  93. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
  94. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
  95. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
  96. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
  97. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
  98. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
  99. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
  100. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
  101. data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
  102. data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
  103. data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
  104. data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
  105. data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
  106. data/spec/bitcoin/key_spec.rb +128 -3
  107. data/spec/bitcoin/namecoin_spec.rb +182 -0
  108. data/spec/bitcoin/network_spec.rb +5 -3
  109. data/spec/bitcoin/node/command_api_spec.rb +376 -0
  110. data/spec/bitcoin/protocol/addr_spec.rb +2 -0
  111. data/spec/bitcoin/protocol/alert_spec.rb +2 -0
  112. data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
  113. data/spec/bitcoin/protocol/block_spec.rb +134 -39
  114. data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
  115. data/spec/bitcoin/protocol/inv_spec.rb +10 -8
  116. data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
  117. data/spec/bitcoin/protocol/ping_spec.rb +2 -0
  118. data/spec/bitcoin/protocol/tx_spec.rb +83 -17
  119. data/spec/bitcoin/protocol/version_spec.rb +7 -5
  120. data/spec/bitcoin/script/opcodes_spec.rb +412 -133
  121. data/spec/bitcoin/script/script_spec.rb +112 -13
  122. data/spec/bitcoin/spec_helper.rb +68 -0
  123. data/spec/bitcoin/storage/reorg_spec.rb +199 -0
  124. data/spec/bitcoin/storage/storage_spec.rb +337 -0
  125. data/spec/bitcoin/storage/validation_spec.rb +261 -0
  126. data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
  127. data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
  128. data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
  129. data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
  130. data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
  131. metadata +105 -51
  132. data/lib/bitcoin/storage/sequel.rb +0 -335
  133. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
  134. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
  135. data/spec/bitcoin/reorg_spec.rb +0 -129
  136. data/spec/bitcoin/storage_spec.rb +0 -229
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin
2
4
  module Protocol
3
5
 
@@ -23,26 +25,38 @@ module Bitcoin
23
25
 
24
26
  # parse raw binary data for transaction output
25
27
  def parse_data(data)
26
- idx = 0
27
- @value = data[idx...idx+=8].unpack("Q")[0]
28
- @pk_script_length, tmp = Protocol.unpack_var_int(data[idx..-1])
29
- idx += data[idx..-1].bytesize - tmp.bytesize
30
- @pk_script = data[idx...idx+=@pk_script_length]
31
- idx
28
+ buf = data.is_a?(String) ? StringIO.new(data) : data
29
+ parse_data_from_io(buf)
30
+ buf.pos
31
+ end
32
+
33
+ def self.from_io(buf)
34
+ txout = new; txout.parse_data_from_io(buf); txout
35
+ end
36
+
37
+ # parse raw binary data for transaction output
38
+ def parse_data_from_io(buf)
39
+ @value = buf.read(8).unpack("Q")[0]
40
+ @pk_script_length = Protocol.unpack_var_int_from_io(buf)
41
+ @pk_script = buf.read(@pk_script_length)
32
42
  end
33
43
 
34
44
  alias :parse_payload :parse_data
35
45
 
36
46
  def to_payload
37
- buf = [ @value ].pack("Q")
38
- buf << Protocol.pack_var_int(@pk_script_length)
39
- buf << @pk_script if @pk_script_length > 0
40
- buf
47
+ [@value].pack("Q") << Protocol.pack_var_int(@pk_script_length) << @pk_script
48
+ end
49
+
50
+ def to_null_payload
51
+ self.class.new(-1, '').to_payload
41
52
  end
42
53
 
43
- def to_hash
44
- { 'value' => "%.8f" % (@value / 100000000.0),
45
- 'scriptPubKey' => Bitcoin::Script.new(@pk_script).to_string }
54
+ def to_hash(options = {})
55
+ script = Bitcoin::Script.new(@pk_script)
56
+ h = { 'value' => "%.8f" % (@value / 100000000.0),
57
+ 'scriptPubKey' => script.to_string }
58
+ h["address"] = script.get_address if script.is_hash160? && options[:with_address]
59
+ h
46
60
  end
47
61
 
48
62
  def self.from_hash(output)
@@ -64,6 +78,7 @@ module Bitcoin
64
78
  # create output spending +value+ btc (base units) to +address+
65
79
  def self.value_to_address(value, address)
66
80
  pk_script = Bitcoin::Script.to_address_script(address)
81
+ raise "Script#pk_script nil with address #{address}" unless pk_script
67
82
  new(value, pk_script)
68
83
  end
69
84
 
@@ -1,12 +1,19 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin
2
4
  module Protocol
3
5
 
6
+ # https://en.bitcoin.it/wiki/Protocol_specification#version
4
7
  class Version
8
+ # services bit constants
9
+ NODE_NETWORK = (1 << 0)
10
+
5
11
  attr_reader :fields
12
+
6
13
  def initialize(opts={})
7
14
  @fields = {
8
- :version => Bitcoin::Protocol::VERSION,
9
- :services => 1,
15
+ :version => Bitcoin.network[:protocol_version],
16
+ :services => NODE_NETWORK,
10
17
  :time => Time.now.tv_sec,
11
18
  :from => "127.0.0.1:8333",
12
19
  :to => "127.0.0.1:8333",
@@ -18,12 +25,12 @@ module Bitcoin
18
25
 
19
26
  def to_payload
20
27
  payload = [
21
- @fields.values_at(:version, :services, :time).pack("IQQ"),
28
+ @fields.values_at(:version, :services, :time).pack("VQQ"),
22
29
  pack_address_field(@fields[:from]),
23
30
  pack_address_field(@fields[:to]),
24
31
  @fields.values_at(:nonce).pack("Q"),
25
32
  Protocol.pack_var_string(@fields[:user_agent]),
26
- @fields.values_at(:last_block).pack("I")
33
+ @fields.values_at(:last_block).pack("V")
27
34
  ].join
28
35
  end
29
36
 
@@ -32,15 +39,15 @@ module Bitcoin
32
39
  end
33
40
 
34
41
  def parse(payload)
35
- version, services, timestamp, to, from, nonce, payload = payload.unpack("Ia8Qa26a26Qa*")
42
+ version, services, timestamp, to, from, nonce, payload = payload.unpack("VQQa26a26Qa*")
36
43
  to, from = unpack_address_field(to), unpack_address_field(from)
37
44
  user_agent, payload = Protocol.unpack_var_string(payload)
38
- last_block = payload.unpack("I")[0]
45
+ last_block = payload.unpack("V")[0]
39
46
 
40
47
  @fields = {
41
48
  :version => version, :services => services, :time => timestamp,
42
49
  :from => from, :to => to, :nonce => nonce,
43
- :user_agent => user_agent, :last_block => last_block
50
+ :user_agent => user_agent.to_s, :last_block => last_block
44
51
  }
45
52
  self
46
53
  end
@@ -63,6 +70,8 @@ module Bitcoin
63
70
  @fields[:time] - Time.now.tv_sec
64
71
  end
65
72
 
73
+ def method_missing(*a); (@fields[a.first] rescue nil) or super(*a); end
74
+
66
75
  def self.parse(payload); new.parse(payload); end
67
76
  end
68
77
 
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  require 'bitcoin'
2
4
 
3
5
  class Bitcoin::Script
@@ -6,9 +8,11 @@ class Bitcoin::Script
6
8
  OP_TRUE = 81
7
9
  OP_0 = 0
8
10
  OP_FALSE = 0
11
+ OP_PUSHDATA0 = 0
9
12
  OP_PUSHDATA1 = 76
10
13
  OP_PUSHDATA2 = 77
11
14
  OP_PUSHDATA4 = 78
15
+ OP_PUSHDATA_INVALID = 238 # 0xEE
12
16
  OP_NOP = 97
13
17
  OP_DUP = 118
14
18
  OP_HASH160 = 169
@@ -32,9 +36,16 @@ class Bitcoin::Script
32
36
  OP_SHA256 = 168
33
37
  OP_SHA1 = 167
34
38
  OP_RIPEMD160 = 166
35
- OP_EVAL = 176
39
+ OP_NOP1 = 176
36
40
  OP_NOP2 = 177
37
- OP_CHECKHASHVERIFY = 177
41
+ OP_NOP3 = 178
42
+ OP_NOP4 = 179
43
+ OP_NOP5 = 180
44
+ OP_NOP6 = 181
45
+ OP_NOP7 = 182
46
+ OP_NOP8 = 183
47
+ OP_NOP9 = 184
48
+ OP_NOP10 = 185
38
49
  OP_CODESEPARATOR = 171
39
50
  OP_MIN = 163
40
51
  OP_MAX = 164
@@ -43,10 +54,52 @@ class Bitcoin::Script
43
54
  OP_IFDUP = 115
44
55
  OP_DEPTH = 116
45
56
  OP_1NEGATE = 79
46
- # OP_IF = 99
47
- # OP_NOTIF = 100
48
- # OP_ELSE = 103
49
- # OP_ENDIF = 104
57
+ OP_WITHIN = 165
58
+ OP_NUMEQUAL = 156
59
+ OP_NUMEQUALVERIFY = 157
60
+ OP_LESSTHAN = 159
61
+ OP_LESSTHANOREQUAL = 161
62
+ OP_GREATERTHAN = 160
63
+ OP_NOT = 145
64
+ OP_0NOTEQUAL = 146
65
+ OP_ABS = 144
66
+ OP_1ADD = 139
67
+ OP_1SUB = 140
68
+ OP_NEGATE = 143
69
+ OP_BOOLOR = 155
70
+ OP_NUMNOTEQUAL = 158
71
+ OP_RETURN = 106
72
+ OP_OVER = 120
73
+ OP_IF = 99
74
+ OP_NOTIF = 100
75
+ OP_ELSE = 103
76
+ OP_ENDIF = 104
77
+ OP_PICK = 121
78
+ OP_SIZE = 130
79
+ OP_VER = 98
80
+ OP_ROLL = 122
81
+ OP_ROT = 123
82
+ OP_2DROP = 109
83
+ OP_2DUP = 110
84
+ OP_3DUP = 111
85
+ OP_NIP = 119
86
+
87
+ OP_CAT = 126
88
+ OP_SUBSTR = 127
89
+ OP_LEFT = 128
90
+ OP_RIGHT = 129
91
+ OP_INVERT = 131
92
+ OP_AND = 132
93
+ OP_OR = 133
94
+ OP_XOR = 134
95
+ OP_2MUL = 141
96
+ OP_2DIV = 142
97
+ OP_MUL = 149
98
+ OP_DIV = 150
99
+ OP_MOD = 151
100
+ OP_LSHIFT = 152
101
+ OP_RSHIFT = 153
102
+
50
103
 
51
104
  OPCODES = Hash[*constants.grep(/^OP_/).map{|i| [const_get(i), i.to_s] }.flatten]
52
105
  OPCODES[0] = "0"
@@ -55,19 +108,44 @@ class Bitcoin::Script
55
108
  OPCODES_ALIAS = {
56
109
  "OP_TRUE" => OP_1,
57
110
  "OP_FALSE" => OP_0,
58
- "OP_NOP1" => OP_EVAL,
59
- "OP_NOP2" => OP_CHECKHASHVERIFY
111
+ "OP_EVAL" => OP_NOP1,
112
+ "OP_CHECKHASHVERIFY" => OP_NOP2,
60
113
  }
61
114
 
115
+ DISABLED_OPCODES = [
116
+ OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_INVERT,
117
+ OP_AND, OP_OR, OP_XOR, OP_2MUL, OP_2DIV, OP_MUL,
118
+ OP_DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT
119
+ ]
120
+
62
121
  OP_2_16 = (82..96).to_a
63
122
 
123
+
124
+ OPCODES_PARSE_BINARY = {}
125
+ OPCODES.each{|k,v| OPCODES_PARSE_BINARY[k] = v }
126
+ OP_2_16.each{|i| OPCODES_PARSE_BINARY[i] = (OP_2_16.index(i)+2).to_s }
127
+
128
+ OPCODES_PARSE_STRING = {}
129
+ OPCODES.each{|k,v| OPCODES_PARSE_STRING[v] = k }
130
+ OPCODES_ALIAS.each{|k,v| OPCODES_PARSE_STRING[k] = v }
131
+ 2.upto(16).each{|i| OPCODES_PARSE_STRING["OP_#{i}"] = OP_2_16[i-2] }
132
+ 2.upto(16).each{|i| OPCODES_PARSE_STRING["#{i}" ] = OP_2_16[i-2] }
133
+ [1,2,4].each{|i| OPCODES_PARSE_STRING.delete("OP_PUSHDATA#{i}") }
134
+
135
+
64
136
  attr_reader :raw, :chunks, :debug
65
137
 
66
138
  # create a new script. +bytes+ is typically input_script + output_script
67
139
  def initialize(bytes, offset=0)
68
140
  @raw = bytes
69
- @stack, @stack_alt = [], []
141
+ @stack, @stack_alt, @exec_stack = [], [], []
70
142
  @chunks = parse(bytes, offset)
143
+ @do_exec = true
144
+ end
145
+
146
+ class ::String
147
+ attr_accessor :bitcoin_pushdata
148
+ attr_accessor :bitcoin_pushdata_length
71
149
  end
72
150
 
73
151
  # parse raw script
@@ -76,43 +154,135 @@ class Bitcoin::Script
76
154
  chunks = []
77
155
  until program.empty?
78
156
  opcode = program.shift(1)[0]
79
- if opcode >= 0xf0
80
- opcode = (opcode << 8) | program.shift(1)[0]
81
- end
82
157
 
83
158
  if (opcode > 0) && (opcode < OP_PUSHDATA1)
84
- len = opcode
159
+ len, tmp = opcode, program[0]
85
160
  chunks << program.shift(len).pack("C*")
161
+
162
+ # 0x16 = 22 due to OP_2_16 from_string parsing
163
+ if len == 1 && tmp <= 22
164
+ chunks.last.bitcoin_pushdata = OP_PUSHDATA0
165
+ chunks.last.bitcoin_pushdata_length = len
166
+ else
167
+ raise "invalid OP_PUSHDATA0" if len != chunks.last.bytesize
168
+ end
86
169
  elsif (opcode == OP_PUSHDATA1)
87
170
  len = program.shift(1)[0]
88
171
  chunks << program.shift(len).pack("C*")
172
+
173
+ unless len > OP_PUSHDATA1 && len <= 0xff
174
+ chunks.last.bitcoin_pushdata = OP_PUSHDATA1
175
+ chunks.last.bitcoin_pushdata_length = len
176
+ else
177
+ raise "invalid OP_PUSHDATA1" if len != chunks.last.bytesize
178
+ end
89
179
  elsif (opcode == OP_PUSHDATA2)
90
- len = program.shift(2).pack("C*").unpack("n")[0]
180
+ len = program.shift(2).pack("C*").unpack("v")[0]
91
181
  chunks << program.shift(len).pack("C*")
182
+
183
+ unless len > 0xff && len <= 0xffff
184
+ chunks.last.bitcoin_pushdata = OP_PUSHDATA2
185
+ chunks.last.bitcoin_pushdata_length = len
186
+ else
187
+ raise "invalid OP_PUSHDATA2" if len != chunks.last.bytesize
188
+ end
92
189
  elsif (opcode == OP_PUSHDATA4)
93
- len = program.shift(4).pack("C*").unpack("N")[0]
190
+ len = program.shift(4).pack("C*").unpack("V")[0]
94
191
  chunks << program.shift(len).pack("C*")
192
+
193
+ unless len > 0xffff # && len <= 0xffffffff
194
+ chunks.last.bitcoin_pushdata = OP_PUSHDATA4
195
+ chunks.last.bitcoin_pushdata_length = len
196
+ else
197
+ raise "invalid OP_PUSHDATA4" if len != chunks.last.bytesize
198
+ end
95
199
  else
96
200
  chunks << opcode
97
201
  end
98
202
  end
99
203
  chunks
204
+ rescue Exception => ex
205
+ # bail out! #run returns false but serialization roundtrips still create the right payload.
206
+ @parse_invalid = true
207
+ c = bytes.unpack("C*").pack("C*")
208
+ c.bitcoin_pushdata = OP_PUSHDATA_INVALID
209
+ c.bitcoin_pushdata_length = c.bytesize
210
+ chunks = [ c ]
100
211
  end
101
212
 
102
213
  # string representation of the script
103
214
  def to_string(chunks=nil)
104
- (chunks || @chunks).map{|i|
105
- case i
215
+ string = ""
216
+ (chunks || @chunks).each.with_index{|i,idx|
217
+ string << " " unless idx == 0
218
+ string << case i
106
219
  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})"
220
+ if opcode = OPCODES_PARSE_BINARY[i]
221
+ opcode
222
+ else
223
+ "(opcode-#{i})"
111
224
  end
112
225
  when String
113
- i.unpack("H*")[0]
226
+ if i.bitcoin_pushdata
227
+ "#{i.bitcoin_pushdata}:#{i.bitcoin_pushdata_length}:".force_encoding('binary') + i.unpack("H*")[0]
228
+ else
229
+ i.unpack("H*")[0]
230
+ end
114
231
  end
115
- }.join(" ")
232
+ }
233
+ string
234
+ end
235
+
236
+ def to_binary(chunks=nil)
237
+ (chunks || @chunks).map{|chunk|
238
+ case chunk
239
+ when Fixnum; [chunk].pack("C*")
240
+ when String; self.class.pack_pushdata(chunk)
241
+ end
242
+ }.join
243
+ end
244
+ alias :to_payload :to_binary
245
+
246
+
247
+ def to_binary_without_signatures(drop_signatures, chunks=nil)
248
+ to_binary( (chunks || @chunks).select{|i| drop_signatures.none?{|e| e == i } } )
249
+ end
250
+
251
+
252
+ def self.pack_pushdata(data)
253
+ size = data.bytesize
254
+
255
+ if data.bitcoin_pushdata
256
+ size = data.bitcoin_pushdata_length
257
+ pack_pushdata_align(data.bitcoin_pushdata, size, data)
258
+ else
259
+ head = if size < OP_PUSHDATA1
260
+ [size].pack("C")
261
+ elsif size <= 0xff
262
+ [OP_PUSHDATA1, size].pack("CC")
263
+ elsif size <= 0xffff
264
+ [OP_PUSHDATA2, size].pack("Cv")
265
+ #elsif size <= 0xffffffff
266
+ else
267
+ [OP_PUSHDATA4, size].pack("CV")
268
+ end
269
+ head + data
270
+ end
271
+ end
272
+
273
+ def self.pack_pushdata_align(pushdata, len, data)
274
+ case pushdata
275
+ when OP_PUSHDATA1
276
+ [OP_PUSHDATA1, len].pack("CC") + data
277
+ when OP_PUSHDATA2
278
+ [OP_PUSHDATA2, len].pack("Cv") + data
279
+ when OP_PUSHDATA4
280
+ [OP_PUSHDATA4, len].pack("CV") + data
281
+ when OP_PUSHDATA_INVALID
282
+ data
283
+ else # OP_PUSHDATA0
284
+ [len].pack("C") + data
285
+ end
116
286
  end
117
287
 
118
288
  # script object of a string representation
@@ -124,33 +294,33 @@ class Bitcoin::Script
124
294
 
125
295
  # raw script binary of a string representation
126
296
  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
297
+ buf = ""
298
+ script_string.split(" ").each{|i|
299
+ i = if opcode = OPCODES_PARSE_STRING[i]
300
+ opcode
301
+ else
302
+ case i
303
+ when /OP_PUSHDATA/ # skip
304
+ when /OP_(.+)$/; raise ScriptOpcodeError, "#{i} not defined!"
305
+ when /\(opcode\-(\d+)\)/; $1.to_i
306
+ when "(opcode"; # skip # fix invalid opcode parsing
307
+ when /^(\d+)\)/; $1.to_i # fix invalid opcode parsing
308
+ when /(\d+):(\d+):(.+)?/
309
+ pushdata, len, data = $1.to_i, $2.to_i, $3
310
+ pack_pushdata_align(pushdata, len, [data].pack("H*"))
311
+ else
312
+ data = [i].pack("H*")
313
+ pack_pushdata(data)
314
+ end
150
315
  end
151
- }.map{|i|
152
- i.is_a?(Fixnum) ? [i].pack("C*") : i # TODO yikes, implement/pack 2 byte opcodes.
153
- }.join
316
+
317
+ buf << if i.is_a?(Fixnum)
318
+ i < 256 ? [i].pack("C") : [OpenSSL::BN.new(i.to_s,10).to_hex].pack("H*")
319
+ else
320
+ i
321
+ end if i
322
+ }
323
+ buf
154
324
  end
155
325
 
156
326
  def invalid?
@@ -158,35 +328,55 @@ class Bitcoin::Script
158
328
  end
159
329
 
160
330
  # 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?
331
+ def run(block_timestamp=Time.now.to_i, &check_callback)
332
+ return false if @parse_invalid
333
+
334
+ #p [to_string, block_timestamp, is_p2sh?]
335
+ @script_invalid = true if @raw.bytesize > 10_000
336
+
337
+ if block_timestamp >= 1333238400 # Pay to Script Hash (BIP 0016)
338
+ return pay_to_script_hash(check_callback) if is_p2sh?
339
+ end
340
+
163
341
  @debug = []
164
342
  @chunks.each{|chunk|
165
343
  break if invalid?
344
+
166
345
  @debug << @stack.map{|i| i.unpack("H*") rescue i}
167
-
346
+ @do_exec = @exec_stack.count(false) == 0 ? true : false
347
+ #p [@stack, @do_exec]
348
+
168
349
  case chunk
169
350
  when Fixnum
170
- case chunk
351
+ if DISABLED_OPCODES.include?(chunk)
352
+ @script_invalid = true
353
+ @debug << "DISABLED_#{OPCODES[chunk]}"
354
+ break
355
+ end
356
+
357
+ next unless (@do_exec || (OP_IF <= chunk && chunk <= OP_ENDIF))
171
358
 
359
+ case chunk
172
360
  when *OPCODES_METHOD.keys
173
361
  m = method( n=OPCODES_METHOD[chunk] )
174
362
  @debug << n.to_s.upcase
175
363
  (m.arity == 1) ? m.call(check_callback) : m.call # invoke opcode method
176
-
177
364
  when *OP_2_16
178
365
  @stack << OP_2_16.index(chunk) + 2
179
366
  @debug << "OP_#{chunk-80}"
180
367
  else
181
368
  name = OPCODES[chunk] || chunk
369
+ puts "Bitcoin::Script: opcode #{name} unkown or not implemented\n#{to_string.inspect}"
182
370
  raise "opcode #{name} unkown or not implemented"
183
371
  end
184
372
  when String
185
- @debug << "PUSH DATA #{chunk.unpack("H*")[0]}"
186
- @stack << chunk
373
+ if @do_exec
374
+ @debug << "PUSH DATA #{chunk.unpack("H*")[0]}"
375
+ @stack << chunk
376
+ end
187
377
  end
188
378
  }
189
- @debug << @stack.map{|i| i.unpack("H*") rescue i }
379
+ @debug << @stack.map{|i| i.unpack("H*") rescue i } #if @do_exec
190
380
 
191
381
  if @script_invalid
192
382
  @stack << 0
@@ -195,7 +385,7 @@ class Bitcoin::Script
195
385
 
196
386
  @debug << "RESULT"
197
387
  return false if @stack.empty?
198
- return false if @stack.pop == 0
388
+ return false if [0, ''].include?(@stack.pop)
199
389
  true
200
390
  end
201
391
 
@@ -203,13 +393,6 @@ class Bitcoin::Script
203
393
  @script_invalid = true; nil
204
394
  end
205
395
 
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
396
  def self.drop_signatures(script_pubkey, drop_signatures)
214
397
  script = new(script_pubkey).to_string.split(" ").delete_if{|c| drop_signatures.include?(c) }.join(" ")
215
398
  script_pubkey = binary_from_string(script)
@@ -219,17 +402,24 @@ class Bitcoin::Script
219
402
  #
220
403
  # <sig> {<pub> OP_CHECKSIG} | OP_HASH160 <script_hash> OP_EQUAL
221
404
  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
405
+ return false if @chunks.size < 4
406
+ *rest, script, _, script_hash, _ = @chunks
407
+ script, script_hash = cast_to_string(script), cast_to_string(script_hash)
226
408
 
227
409
  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)
410
+ rest.delete_at(0) if rest[0] && cast_to_bignum(rest[0]) == 0
411
+
412
+ script = self.class.new(to_binary(rest) + script).inner_p2sh!(script)
413
+ result = script.run(&check_callback)
414
+ @debug = script.debug
415
+ result
230
416
  end
231
417
 
418
+ def inner_p2sh!(script=nil); @inner_p2sh = true; @inner_script_code = script; self; end
419
+ def inner_p2sh?; @inner_p2sh; end
420
+
232
421
  def is_pay_to_script_hash?
422
+ return false unless @chunks[-2].is_a?(String)
233
423
  @chunks.size >= 3 && @chunks[-3] == OP_HASH160 &&
234
424
  @chunks[-2].bytesize == 20 && @chunks[-1] == OP_EQUAL
235
425
  end
@@ -258,15 +448,16 @@ class Bitcoin::Script
258
448
  # is this a multisig tx
259
449
  def is_multisig?
260
450
  return false if @chunks.size > 6 || @chunks.size < 4
261
- @chunks[-1] == OP_CHECKMULTISIG
451
+ @chunks[-1] == OP_CHECKMULTISIG and get_multisig_pubkeys.all?{|c| c.is_a?(String) }
262
452
  end
263
453
 
454
+ # get type of this tx
264
455
  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
456
+ if is_hash160?; :hash160
457
+ elsif is_pubkey?; :pubkey
458
+ elsif is_multisig?; :multisig
459
+ elsif is_p2sh?; :p2sh
460
+ else; :unknown
270
461
  end
271
462
  end
272
463
 
@@ -281,7 +472,7 @@ class Bitcoin::Script
281
472
  Bitcoin.pubkey_to_address(get_pubkey)
282
473
  end
283
474
 
284
- # get the hash160 for this hash160 script
475
+ # get the hash160 for this hash160 or pubkey script
285
476
  def get_hash160
286
477
  return @chunks[2..-3][0].unpack("H*")[0] if is_hash160?
287
478
  return Bitcoin.hash160(get_pubkey) if is_pubkey?
@@ -294,19 +485,25 @@ class Bitcoin::Script
294
485
 
295
486
  # get the public keys for this multisig script
296
487
  def get_multisig_pubkeys
297
- 1.upto(@chunks[-2] - 80).map {|i| @chunks[i]}
488
+ 1.upto(@chunks[-2] - 80).map{|i| @chunks[i] }
298
489
  end
299
490
 
300
491
  # get the pubkey addresses for this multisig script
301
492
  def get_multisig_addresses
302
- get_multisig_pubkeys.map {|p| Bitcoin::Key.new(nil, p.unpack("H*")[0]).addr}
493
+ get_multisig_pubkeys.map{|pub|
494
+ begin
495
+ Bitcoin::Key.new(nil, pub.unpack("H*")[0]).addr
496
+ rescue OpenSSL::PKey::ECError, OpenSSL::PKey::EC::Point::Error
497
+ end
498
+ }.compact
303
499
  end
304
500
 
305
501
  # get all addresses this script corresponds to (if possible)
306
502
  def get_addresses
307
- return [get_pubkey_address] if is_pubkey?
308
- return [get_hash160_address] if is_hash160?
503
+ return [get_pubkey_address] if is_pubkey?
504
+ return [get_hash160_address] if is_hash160?
309
505
  return get_multisig_addresses if is_multisig?
506
+ []
310
507
  end
311
508
 
312
509
  # get single address, or first for multisig script
@@ -352,7 +549,19 @@ class Bitcoin::Script
352
549
  def self.to_pubkey_script_sig(signature, pubkey)
353
550
  hash_type = "\x01"
354
551
  #pubkey = [pubkey].pack("H*") if pubkey.bytesize != 65
355
- raise "pubkey is not in binary form" unless pubkey.bytesize == 65 && pubkey[0] == "\x04"
552
+ return [ [signature.bytesize+1].pack("C"), signature, hash_type ].join unless pubkey
553
+
554
+ case pubkey[0]
555
+ when "\x04"
556
+ expected_size = 65
557
+ when "\x02", "\x03"
558
+ expected_size = 33
559
+ end
560
+
561
+ if !expected_size || pubkey.bytesize != expected_size
562
+ raise "pubkey is not in binary form"
563
+ end
564
+
356
565
  [ [signature.bytesize+1].pack("C"), signature, hash_type, [pubkey.bytesize].pack("C"), pubkey ].join
357
566
  end
358
567
 
@@ -373,8 +582,17 @@ class Bitcoin::Script
373
582
  ## OPCODES
374
583
 
375
584
  # Does nothing
376
- def op_nop
377
- end
585
+ def op_nop; end
586
+ def op_nop1; end
587
+ def op_nop2; end
588
+ def op_nop3; end
589
+ def op_nop4; end
590
+ def op_nop5; end
591
+ def op_nop6; end
592
+ def op_nop7; end
593
+ def op_nop8; end
594
+ def op_nop9; end
595
+ def op_nop10; end
378
596
 
379
597
  # Duplicates the top stack item.
380
598
  def op_dup
@@ -383,31 +601,31 @@ class Bitcoin::Script
383
601
 
384
602
  # The input is hashed using SHA-256.
385
603
  def op_sha256
386
- buf = @stack.pop
604
+ buf = pop_string
387
605
  @stack << Digest::SHA256.digest(buf)
388
606
  end
389
607
 
390
608
  # The input is hashed using SHA-1.
391
609
  def op_sha1
392
- buf = @stack.pop
610
+ buf = pop_string
393
611
  @stack << Digest::SHA1.digest(buf)
394
612
  end
395
613
 
396
614
  # The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
397
615
  def op_hash160
398
- buf = @stack.pop
616
+ buf = pop_string
399
617
  @stack << Digest::RMD160.digest(Digest::SHA256.digest(buf))
400
618
  end
401
619
 
402
620
  # The input is hashed using RIPEMD-160.
403
621
  def op_ripemd160
404
- buf = @stack.pop
622
+ buf = pop_string
405
623
  @stack << Digest::RMD160.digest(buf)
406
624
  end
407
625
 
408
626
  # The input is hashed two times with SHA-256.
409
627
  def op_hash256
410
- buf = @stack.pop
628
+ buf = pop_string
411
629
  @stack << Digest::SHA256.digest(Digest::SHA256.digest(buf))
412
630
  end
413
631
 
@@ -428,33 +646,103 @@ class Bitcoin::Script
428
646
 
429
647
  # The top two items on the stack are swapped.
430
648
  def op_swap
431
- @stack[-2..-1] = @stack[-2..-1].reverse
649
+ @stack[-2..-1] = @stack[-2..-1].reverse if @stack[-2]
432
650
  end
433
651
 
434
652
  # If both a and b are not 0, the output is 1. Otherwise 0.
435
653
  def op_booland
436
- a, b = @stack.pop(2)
654
+ a, b = pop_int(2)
437
655
  @stack << (![a,b].any?{|n| n == 0 } ? 1 : 0)
438
656
  end
439
657
 
658
+ # If a or b is not 0, the output is 1. Otherwise 0.
659
+ def op_boolor
660
+ a, b = pop_int(2)
661
+ @stack << ( (a != 0 || b != 0) ? 1 : 0 )
662
+ end
663
+
440
664
  # a is added to b.
441
665
  def op_add
442
- a, b = @stack.pop(2).reverse
666
+ a, b = pop_int(2)
443
667
  @stack << a + b
444
668
  end
445
669
 
446
670
  # b is subtracted from a.
447
671
  def op_sub
448
- a, b = @stack.pop(2).reverse
672
+ a, b = pop_int(2)
449
673
  @stack << a - b
450
674
  end
451
675
 
676
+ # Returns 1 if a is less than b, 0 otherwise.
677
+ def op_lessthan
678
+ a, b = pop_int(2)
679
+ @stack << (a < b ? 1 : 0)
680
+ end
681
+
682
+ # Returns 1 if a is less than or equal to b, 0 otherwise.
683
+ def op_lessthanorequal
684
+ a, b = pop_int(2)
685
+ @stack << (a <= b ? 1 : 0)
686
+ end
687
+
688
+ # Returns 1 if a is greater than b, 0 otherwise.
689
+ def op_greaterthan
690
+ a, b = pop_int(2)
691
+ @stack << (a > b ? 1 : 0)
692
+ end
693
+
452
694
  # Returns 1 if a is greater than or equal to b, 0 otherwise.
453
695
  def op_greaterthanorequal
454
- a, b = @stack.pop(2).reverse
696
+ a, b = pop_int(2)
455
697
  @stack << (a >= b ? 1 : 0)
456
698
  end
457
699
 
700
+ # If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
701
+ def op_not
702
+ a = pop_int
703
+ @stack << (a == 0 ? 1 : 0)
704
+ end
705
+
706
+ def op_0notequal
707
+ a = pop_int
708
+ @stack << (a != 0 ? 1 : 0)
709
+ end
710
+
711
+ # The input is made positive.
712
+ def op_abs
713
+ a = pop_int
714
+ @stack << a.abs
715
+ end
716
+
717
+ # The input is divided by 2. Currently disabled.
718
+ def op_2div
719
+ a = pop_int
720
+ @stack << (a >> 1)
721
+ end
722
+
723
+ # The input is multiplied by 2. Currently disabled.
724
+ def op_2mul
725
+ a = pop_int
726
+ @stack << (a << 1)
727
+ end
728
+
729
+ # 1 is added to the input.
730
+ def op_1add
731
+ a = pop_int
732
+ @stack << (a + 1)
733
+ end
734
+
735
+ def op_1sub
736
+ a = pop_int
737
+ @stack << (a - 1)
738
+ end
739
+
740
+ # The sign of the input is flipped.
741
+ def op_negate
742
+ a = pop_int
743
+ @stack << -a
744
+ end
745
+
458
746
  # Removes the top stack item.
459
747
  def op_drop
460
748
  @stack.pop
@@ -462,13 +750,14 @@ class Bitcoin::Script
462
750
 
463
751
  # Returns 1 if the inputs are exactly equal, 0 otherwise.
464
752
  def op_equal
465
- a, b = @stack.pop(2).reverse
753
+ #a, b = @stack.pop(2)
754
+ a, b = pop_int(2)
466
755
  @stack << (a == b ? 1 : 0)
467
756
  end
468
757
 
469
758
  # Marks transaction as invalid if top stack value is not true. True is removed, but false is not.
470
759
  def op_verify
471
- res = @stack.pop
760
+ res = pop_int
472
761
  if res == 0
473
762
  @stack << res
474
763
  @script_invalid = true # raise 'transaction invalid' ?
@@ -494,30 +783,30 @@ class Bitcoin::Script
494
783
 
495
784
  # Returns the smaller of a and b.
496
785
  def op_min
497
- @stack << @stack.pop(2).min
786
+ @stack << pop_int(2).min
498
787
  end
499
788
 
500
789
  # Returns the larger of a and b.
501
790
  def op_max
502
- @stack << @stack.pop(2).max
791
+ @stack << pop_int(2).max
503
792
  end
504
-
793
+
505
794
  # Copies the pair of items two spaces back in the stack to the front.
506
795
  def op_2over
507
796
  @stack << @stack[-4]
508
797
  @stack << @stack[-4]
509
798
  end
510
-
799
+
511
800
  # Swaps the top two pairs of items.
512
801
  def op_2swap
513
802
  p1 = @stack.pop(2)
514
803
  p2 = @stack.pop(2)
515
804
  @stack += p1 += p2
516
805
  end
517
-
806
+
518
807
  # If the input is true, duplicate it.
519
808
  def op_ifdup
520
- if @stack.last != 0
809
+ if cast_to_bignum(@stack.last) != 0
521
810
  @stack << @stack.last
522
811
  end
523
812
  end
@@ -526,38 +815,189 @@ class Bitcoin::Script
526
815
  def op_1negate
527
816
  @stack << -1
528
817
  end
529
-
818
+
530
819
  # Puts the number of stack items onto the stack.
531
820
  def op_depth
532
821
  @stack << @stack.size
533
822
  end
534
823
 
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
824
+ # Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.
825
+ def op_within
826
+ bn1, bn2, bn3 = pop_int(3)
827
+ @stack << ( (bn2 <= bn1 && bn1 < bn3) ? 1 : 0 )
828
+ end
829
+
830
+ # Returns 1 if the numbers are equal, 0 otherwise.
831
+ def op_numequal
832
+ a, b = pop_int(2)
833
+ @stack << (a == b ? 1 : 0)
834
+ end
835
+
836
+ # Returns 1 if the numbers are not equal, 0 otherwise.
837
+ def op_numnotequal
838
+ a, b = pop_int(2)
839
+ @stack << (a != b ? 1 : 0)
840
+ end
841
+
842
+ # Marks transaction as invalid.
843
+ def op_return
844
+ @script_invalid = true; nil
845
+ end
846
+
847
+ # Copies the second-to-top stack item to the top.
848
+ def op_over
849
+ item = @stack[-2]
850
+ @stack << item if item
851
+ end
852
+
853
+ # If the top stack value is not 0, the statements are executed. The top stack value is removed.
854
+ def op_if
855
+ value = false
856
+ if @do_exec
857
+ return if @stack.size < 1
858
+ value = pop_int == 1 ? true : false
859
+ end
860
+ @exec_stack << value
861
+ end
862
+
863
+ # If the top stack value is 0, the statements are executed. The top stack value is removed.
864
+ def op_notif
865
+ value = false
866
+ if @do_exec
867
+ return if @stack.size < 1
868
+ value = pop_int == 1 ? false : true
869
+ end
870
+ @exec_stack << value
871
+ end
872
+
873
+ # If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed then these statements are and if the preceding OP_IF or OP_NOTIF or OP_ELSE was executed then these statements are not.
874
+ def op_else
875
+ return if @exec_stack.empty?
876
+ @exec_stack[-1] = !@exec_stack[-1]
877
+ end
878
+
879
+ # Ends an if/else block.
880
+ def op_endif
881
+ return if @exec_stack.empty?
882
+ @exec_stack.pop
883
+ end
884
+
885
+ # The item n back in the stack is copied to the top.
886
+ def op_pick
887
+ pos = pop_int
888
+ item = @stack[-(pos+1)]
889
+ @stack << item if item
890
+ end
891
+
892
+ # The item n back in the stack is moved to the top.
893
+ def op_roll
894
+ pos = pop_int
895
+ idx = -(pos+1)
896
+ item = @stack[idx]
897
+ if item
898
+ @stack.delete_at(idx)
899
+ @stack << item if item
540
900
  end
541
901
  end
542
902
 
903
+ # The top three items on the stack are rotated to the left.
904
+ def op_rot
905
+ return if @stack.size < 3
906
+ @stack[-3..-1] = [ @stack[-2], @stack[-1], @stack[-3] ]
907
+ end
908
+
909
+ # Removes the top two stack items.
910
+ def op_2drop
911
+ @stack.pop(2)
912
+ end
913
+
914
+ # Duplicates the top two stack items.
915
+ def op_2dup
916
+ @stack.push(*@stack[-2..-1])
917
+ end
918
+
919
+ # Duplicates the top three stack items.
920
+ def op_3dup
921
+ @stack.push(*@stack[-3..-1])
922
+ end
923
+
924
+ # Removes the second-to-top stack item.
925
+ def op_nip
926
+ @stack.delete_at(-2)
927
+ end
928
+
929
+ # Returns the length of the input string.
930
+ def op_size
931
+ item = @stack[-1]
932
+ size = case item
933
+ when String; item.bytesize
934
+ when Numeric; OpenSSL::BN.new(item.to_s).to_mpi.size - 4
935
+ end
936
+ @stack << size
937
+ end
938
+
939
+ # Transaction is invalid unless occuring in an unexecuted OP_IF branch
940
+ def op_ver
941
+ invalid if @do_exec
942
+ end
943
+
944
+ def pop_int(count=nil)
945
+ return cast_to_bignum(@stack.pop) unless count
946
+ @stack.pop(count).map{|i| cast_to_bignum(i) }
947
+ end
948
+
949
+ def pop_string(count=nil)
950
+ return cast_to_string(@stack.pop) unless count
951
+ @stack.pop(count).map{|i| cast_to_string(i) }
952
+ end
953
+
954
+ def cast_to_bignum(buf)
955
+ case buf
956
+ when Numeric; buf
957
+ when String; OpenSSL::BN.new([buf.bytesize].pack("N") + buf.reverse, 0).to_i
958
+ else; raise TypeError, 'cast_to_bignum: failed to cast: %s (%s)' % [buf, buf.class]
959
+ end
960
+ end
961
+
962
+ def cast_to_string(buf)
963
+ case buf
964
+ when Numeric; OpenSSL::BN.new(buf.to_s).to_s(0)[4..-1]
965
+ when String; buf;
966
+ else; raise TypeError, 'cast_to_string: failed to cast: %s (%s)' % [buf, buf.class]
967
+ end
968
+ end
969
+
970
+ # Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
971
+ def op_numequalverify
972
+ op_numequal; op_verify
973
+ end
974
+
543
975
  # All of the signature checking words will only match signatures
544
976
  # to the data after the most recently-executed OP_CODESEPARATOR.
545
977
  def op_codeseparator
546
978
  @codehash_start = @chunks.size - @chunks.reverse.index(OP_CODESEPARATOR)
547
979
  end
548
980
 
981
+ def codehash_script(opcode)
982
+ # CScript scriptCode(pbegincodehash, pend);
983
+ script = to_string(@chunks[(@codehash_start||0)...@chunks.size-@chunks.reverse.index(opcode)])
984
+ checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
985
+ [script, checkhash]
986
+ end
987
+
988
+
549
989
  # do a CHECKSIG operation on the current stack,
550
990
  # asking +check_callback+ to do the actual signature verification.
551
991
  # This is used by Protocol::Tx#verify_input_signature
552
992
  def op_checksig(check_callback)
553
993
  return invalid if @stack.size < 2
554
994
  pubkey = @stack.pop
555
- drop_sigs = [@stack[-1].unpack("H*")[0]]
995
+ drop_sigs = [ @stack[-1] ]
556
996
  sig, hash_type = parse_sig(@stack.pop)
557
997
 
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)
998
+ if inner_p2sh?
999
+ script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
1000
+ drop_sigs = nil
561
1001
  else
562
1002
  script_code, drop_sigs = nil, nil
563
1003
  end
@@ -589,23 +1029,22 @@ class Bitcoin::Script
589
1029
  # TODO: validate signature order
590
1030
  # TODO: take global opcode count
591
1031
  def op_checkmultisig(check_callback)
592
- n_pubkeys = @stack.pop
1032
+ n_pubkeys = pop_int
593
1033
  return invalid unless (0..20).include?(n_pubkeys)
594
1034
  return invalid unless @stack.last(n_pubkeys).all?{|e| e.is_a?(String) && e != '' }
595
1035
  #return invalid if ((@op_count ||= 0) += n_pubkeys) > 201
596
- pubkeys = @stack.pop(n_pubkeys)
1036
+ pubkeys = pop_string(n_pubkeys)
597
1037
 
598
- n_sigs = @stack.pop
1038
+ n_sigs = pop_int
599
1039
  return invalid unless (0..n_pubkeys).include?(n_sigs)
600
1040
  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) }
1041
+ sigs = (drop_sigs = pop_string(n_sigs)).map{|s| parse_sig(s) }
602
1042
 
603
1043
  @stack.pop if @stack[-1] == '' # remove OP_NOP from stack
604
1044
 
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] }
1045
+ if inner_p2sh?
1046
+ script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
1047
+ drop_sigs = nil
609
1048
  else
610
1049
  script_code, drop_sigs = nil, nil
611
1050
  end
@@ -615,7 +1054,12 @@ class Bitcoin::Script
615
1054
  valid_sigs += 1 if check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code)
616
1055
  }}
617
1056
 
618
- @stack << ((valid_sigs == n_sigs) ? 1 : (invalid; 0))
1057
+ @stack << ((valid_sigs >= n_sigs) ? 1 : (invalid; 0))
1058
+ end
1059
+
1060
+ # op_eval: https://en.bitcoin.it/wiki/BIP_0012
1061
+ # the BIP was never accepted and must be handled as old OP_NOP1
1062
+ def op_nop1
619
1063
  end
620
1064
 
621
1065
  OPCODES_METHOD = Hash[*instance_methods.grep(/^op_/).map{|m|
@@ -624,6 +1068,19 @@ class Bitcoin::Script
624
1068
  OPCODES_METHOD[0] = :op_0
625
1069
  OPCODES_METHOD[81] = :op_1
626
1070
 
1071
+ def self.is_canonical_pubkey?(pubkey)
1072
+ return false if pubkey.bytesize < 33 # "Non-canonical public key: too short"
1073
+ case pubkey[0]
1074
+ when "\x04"
1075
+ return false if pubkey.bytesize != 65 # "Non-canonical public key: invalid length for uncompressed key"
1076
+ when "\x02", "\x03"
1077
+ return false if pubkey.bytesize != 33 # "Non-canonical public key: invalid length for compressed key"
1078
+ else
1079
+ return false # "Non-canonical public key: compressed nor uncompressed"
1080
+ end
1081
+ true
1082
+ end
1083
+
627
1084
  private
628
1085
 
629
1086
  def parse_sig(sig)