bitcoin-ruby 0.0.1 → 0.0.2

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 (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)