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
 
3
5
  # Optional DSL to help create blocks and transactions.
@@ -7,19 +9,21 @@ module Bitcoin
7
9
 
8
10
  # build a Bitcoin::Protocol::Block matching the given +target+.
9
11
  # see BlockBuilder for details.
10
- def blk(target = "00".ljust(32, 'f'))
12
+ def build_block(target = "00".ljust(64, 'f'))
11
13
  c = BlockBuilder.new
12
14
  yield c
13
15
  c.block(target)
14
16
  end
17
+ alias :blk :build_block
15
18
 
16
19
  # build a Bitcoin::Protocol::Tx.
17
20
  # see TxBuilder for details.
18
- def tx
21
+ def build_tx
19
22
  c = TxBuilder.new
20
23
  yield c
21
24
  c.tx
22
25
  end
26
+ alias :tx :build_tx
23
27
 
24
28
  # build a Bitcoin::Script.
25
29
  # see ScriptBuilder for details.
@@ -29,7 +33,7 @@ module Bitcoin
29
33
  c.script
30
34
  end
31
35
 
32
- # DSL to create a Bitcoin::Protocol::Block used by Builder#blk.
36
+ # DSL to create a Bitcoin::Protocol::Block used by Builder#create_block.
33
37
  # block = blk("00".ljust(32, 'f')) do |b|
34
38
  # b.prev_block "\x00"*32
35
39
  # b.tx do |t|
@@ -46,7 +50,7 @@ module Bitcoin
46
50
  class BlockBuilder
47
51
 
48
52
  def initialize
49
- @block = Bitcoin::P::Block.new(nil)
53
+ @block = P::Block.new(nil)
50
54
  end
51
55
 
52
56
  # specify block version. this is usually not necessary. defaults to 1.
@@ -59,6 +63,11 @@ module Bitcoin
59
63
  @prev_block = hash
60
64
  end
61
65
 
66
+ # set the block timestamp (defaults to current time).
67
+ def time time
68
+ @time = time
69
+ end
70
+
62
71
  # add transactions to the block (see TxBuilder).
63
72
  def tx
64
73
  c = TxBuilder.new
@@ -69,14 +78,15 @@ module Bitcoin
69
78
  # create the block according to values specified via DSL.
70
79
  def block target
71
80
  @block.ver = @version || 1
72
- @block.prev_block = [@prev_block].pack("H*").reverse
81
+ @block.prev_block = @prev_block.htb.reverse
73
82
  @block.mrkl_root = @mrkl_root
74
- @block.time = Time.now.to_i
83
+ @block.time = @time || Time.now.to_i
75
84
  @block.nonce = 0
76
- @block.mrkl_root = [Bitcoin.hash_mrkl_tree(@block.tx.map {|t|
77
- t.hash }).last].pack("H*")
85
+ @block.mrkl_root = Bitcoin.hash_mrkl_tree(@block.tx.map(&:hash)).last.htb.reverse
78
86
  find_hash(target)
79
- Bitcoin::P::Block.new(@block.to_payload)
87
+ block = P::Block.new(@block.to_payload)
88
+ raise "Payload Error" unless block.to_payload == @block.to_payload
89
+ block
80
90
  end
81
91
 
82
92
  private
@@ -104,22 +114,25 @@ module Bitcoin
104
114
 
105
115
  end
106
116
 
107
- # DSL to create Bitcoin::Protocol::Tx used by Builder#tx.
108
- # tx = tx do |t|
109
- # t.input do |i|
110
- # i.prev_out prev_tx # previous transaction
111
- # i.prev_out_index 0 # index of previous output
112
- # i.signature_key key # Bitcoin::Key used to sign the input
113
- # end
114
- # t.output do |o|
115
- # o.value 12345 # 0.00012345 BTC
116
- # o.script {|s| s.type :address; s.recipient key.addr }
117
- # end
118
- # end
117
+ # DSL to create Bitcoin::Protocol::Tx used by Builder#build_tx.
118
+ # tx = tx do |t|
119
+ # t.input do |i|
120
+ # i.prev_out prev_tx # previous transaction
121
+ # i.prev_out_index 0 # index of previous output
122
+ # i.signature_key key # Bitcoin::Key used to sign the input
123
+ # end
124
+ # t.output do |o|
125
+ # o.value 12345 # 0.00012345 BTC
126
+ # o.script {|s| s.type :address; s.recipient key.addr }
127
+ # end
128
+ # end
129
+ #
130
+ # signs every input that has a signature key. if the signature key is
131
+ # not specified, the input will include the #sig_hash that needs to be signed.
119
132
  class TxBuilder
120
133
 
121
134
  def initialize
122
- @tx = Bitcoin::P::Tx.new(nil)
135
+ @tx = P::Tx.new(nil)
123
136
  @tx.ver, @tx.lock_time = 1, 0
124
137
  @ins, @outs = [], []
125
138
  end
@@ -148,7 +161,10 @@ module Bitcoin
148
161
  @outs << c
149
162
  end
150
163
 
151
- # create the transaction according to values specified via DSL and sign inputs.
164
+ # create the transaction according to values specified via DSL.
165
+ # sign each input that has a signature key specified. if there is
166
+ # no key, store the sig_hash in the input, so it can easily be
167
+ # signed later.
152
168
  def tx
153
169
  @ins.each {|i| @tx.add_in(i.txin) }
154
170
  @outs.each {|o| @tx.add_out(o.txout) }
@@ -160,26 +176,36 @@ module Bitcoin
160
176
  next
161
177
  end
162
178
  prev_tx = inc.instance_variable_get(:@prev_out)
163
- sig_hash = @tx.signature_hash_for_input(i, prev_tx)
164
- sig = inc.key.sign(sig_hash)
165
- script_sig = Bitcoin::Script.to_signature_pubkey_script(sig, [inc.key.pub].pack("H*"))
166
- @tx.in[i].script_sig_length = script_sig.bytesize
167
- @tx.in[i].script_sig = script_sig
168
- raise "Signature error" unless @tx.verify_input_signature(i, prev_tx)
179
+ @sig_hash = @tx.signature_hash_for_input(i, prev_tx)
180
+ if inc.key && inc.key.priv
181
+ sig = inc.key.sign(@sig_hash)
182
+ script_sig = Script.to_signature_pubkey_script(sig, [inc.key.pub].pack("H*"))
183
+ @tx.in[i].script_sig_length = script_sig.bytesize
184
+ @tx.in[i].script_sig = script_sig
185
+ raise "Signature error" unless @tx.verify_input_signature(i, prev_tx)
186
+ else
187
+ @tx.in[i].script_sig_length = 0
188
+ @tx.in[i].script_sig = ""
189
+ @tx.in[i].sig_hash = @sig_hash
190
+ @tx.in[i].sig_address = Script.new(prev_tx.out[@tx.in[i].prev_out_index].pk_script).get_address
191
+ end
169
192
  end
170
- Bitcoin::P::Tx.new(@tx.to_payload)
193
+ data = @tx.in.map {|i| [i.sig_hash, i.sig_address] }
194
+ tx = P::Tx.new(@tx.to_payload)
195
+ data.each.with_index {|d, i| i = tx.in[i]; i.sig_hash = d[0]; i.sig_address = d[1] }
196
+ raise "Payload Error" unless tx.to_payload == @tx.to_payload
197
+ tx
171
198
  end
172
199
  end
173
200
 
174
201
  # create a Bitcoin::Protocol::TxIn used by TxBuilder#input.
175
202
  #
176
- # inputs can be either 'coinbase', in which case they only need to specify #coinbase,
177
- # or they have to define a #prev_out, #prev_out_index and #signature key.
203
+ # inputs need a #prev_out tx and #prev_out_index of the output they spend.
178
204
  class TxInBuilder
179
205
  attr_reader :key, :coinbase_data
180
206
 
181
207
  def initialize
182
- @txin = Bitcoin::P::TxIn.new
208
+ @txin = P::TxIn.new
183
209
  end
184
210
 
185
211
  # previous transaction that contains the output we want to use.
@@ -224,7 +250,7 @@ module Bitcoin
224
250
  attr_reader :script
225
251
 
226
252
  def initialize
227
- @type = nil
253
+ @type = :address
228
254
  @script = nil
229
255
  end
230
256
 
@@ -236,7 +262,7 @@ module Bitcoin
236
262
  # recipient(s) of the script.
237
263
  # depending on the #type, either an address, hash160 pubkey, etc.
238
264
  def recipient *data
239
- @script = Bitcoin::Script.send("to_#{@type}_script", *data)
265
+ @script = Script.send("to_#{@type}_script", *data)
240
266
  end
241
267
  end
242
268
 
@@ -245,7 +271,7 @@ module Bitcoin
245
271
  attr_reader :txout
246
272
 
247
273
  def initialize
248
- @txout = Bitcoin::P::TxOut.new
274
+ @txout = P::TxOut.new
249
275
  end
250
276
 
251
277
  # set output value (in base units / "satoshis")
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  require 'yaml'
2
4
  module Bitcoin
3
5
 
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  require 'socket'
2
4
  require 'eventmachine'
3
5
  require 'bitcoin'
@@ -5,27 +7,24 @@ require 'bitcoin'
5
7
  module Bitcoin
6
8
 
7
9
  module ConnectionHandler
8
- def hth(h); h.unpack("H*")[0]; end
9
- def htb(h); [h].pack("H*"); end
10
-
11
10
  def on_inv_transaction(hash)
12
- p ['inv transaction', hth(hash)]
11
+ p ['inv transaction', hash.hth]
13
12
  pkt = Protocol.getdata_pkt(:tx, [hash])
14
13
  send_data(pkt)
15
14
  end
16
15
 
17
16
  def on_inv_block(hash)
18
- p ['inv block', hth(hash)]
17
+ p ['inv block', hash.hth]
19
18
  pkt = Protocol.getdata_pkt(:block, [hash])
20
19
  send_data(pkt)
21
20
  end
22
21
 
23
22
  def on_get_transaction(hash)
24
- p ['get transaction', hth(hash)]
23
+ p ['get transaction', hash.hth]
25
24
  end
26
25
 
27
26
  def on_get_block(hash)
28
- p ['get block', hth(hash)]
27
+ p ['get block', hash.hth]
29
28
  end
30
29
 
31
30
  def on_addr(addr)
@@ -43,10 +42,14 @@ module Bitcoin
43
42
  end
44
43
 
45
44
  def on_version(version)
46
- p [@sockaddr, 'version', version, version.timestamp - Time.now.to_i]
45
+ p [@sockaddr, 'version', version, version.time - Time.now.to_i]
47
46
  send_data( Protocol.verack_pkt )
48
47
  end
49
48
 
49
+ def on_verack
50
+ on_handshake_complete
51
+ end
52
+
50
53
  def on_handshake_complete
51
54
  p [@sockaddr, 'handshake complete']
52
55
  @connected = true
@@ -0,0 +1,162 @@
1
+ # encoding: ascii-8bit
2
+
3
+ class Mnemonic
4
+ # ruby version of: https://github.com/spesmilo/electrum/blob/master/lib/mnemonic.py
5
+
6
+ # list of words from http://en.wiktionary.org/wiki/Wiktionary:Frequency_lists/Contemporary_poetry
7
+ Words = (<<-TEXT).split
8
+ like just love know never want time out there make look eye down only think
9
+ heart back then into about more away still them take thing even through long always
10
+ world too friend tell try hand thought over here other need smile again much cry
11
+ been night ever little said end some those around mind people girl leave dream left
12
+ turn myself give nothing really off before something find walk wish good once place ask
13
+ stop keep watch seem everything wait got yet made remember start alone run hope maybe
14
+ believe body hate after close talk stand own each hurt help home god soul new
15
+ many two inside should true first fear mean better play another gone change use wonder
16
+ someone hair cold open best any behind happen water dark laugh stay forever name work
17
+ show sky break came deep door put black together upon happy such great white matter
18
+ fill past please burn cause enough touch moment soon voice scream anything stare sound red
19
+ everyone hide kiss truth death beautiful mine blood broken very pass next forget tree wrong
20
+ air mother understand lip hit wall memory sleep free high realize school might skin sweet
21
+ perfect blue kill breath dance against fly between grow strong under listen bring sometimes speak
22
+ pull person become family begin ground real small father sure feet rest young finally land
23
+ across today different guy line fire reason reach second slowly write eat smell mouth step
24
+ learn three floor promise breathe darkness push earth guess save song above along both color
25
+ house almost sorry anymore brother okay dear game fade already apart warm beauty heard notice
26
+ question shine began piece whole shadow secret street within finger point morning whisper child moon
27
+ green story glass kid silence since soft yourself empty shall angel answer baby bright dad
28
+ path worry hour drop follow power war half flow heaven act chance fact least tired
29
+ children near quite afraid rise sea taste window cover nice trust lot sad cool force
30
+ peace return blind easy ready roll rose drive held music beneath hang mom paint emotion
31
+ quiet clear cloud few pretty bird outside paper picture front rock simple anyone meant reality
32
+ road sense waste bit leaf thank happiness meet men smoke truly decide self age book
33
+ form alive carry escape damn instead able ice minute throw catch leg ring course goodbye
34
+ lead poem sick corner desire known problem remind shoulder suppose toward wave drink jump woman
35
+ pretend sister week human joy crack grey pray surprise dry knee less search bleed caught
36
+ clean embrace future king son sorrow chest hug remain sat worth blow daddy final parent
37
+ tight also create lonely safe cross dress evil silent bone fate perhaps anger class scar
38
+ snow tiny tonight continue control dog edge mirror month suddenly comfort given loud quickly gaze
39
+ plan rush stone town battle ignore spirit stood stupid yours brown build dust hey kept
40
+ pay phone twist although ball beyond hidden nose taken fail float pure somehow wash wrap
41
+ angry cheek creature forgotten heat rip single space special weak whatever yell anyway blame job
42
+ choose country curse drift echo figure grew laughter neck suffer worse yeah disappear foot forward
43
+ knife mess somewhere stomach storm beg idea lift offer breeze field five often simply stuck
44
+ win allow confuse enjoy except flower seek strength calm grin gun heavy hill large ocean
45
+ shoe sigh straight summer tongue accept crazy everyday exist grass mistake sent shut surround table
46
+ ache brain destroy heal nature shout sign stain choice doubt glance glow mountain queen stranger
47
+ throat tomorrow city either fish flame rather shape spin spread ash distance finish image imagine
48
+ important nobody shatter warmth became feed flesh funny lust shirt trouble yellow attention bare bite
49
+ money protect amaze appear born choke completely daughter fresh friendship gentle probably six deserve expect
50
+ grab middle nightmare river thousand weight worst wound barely bottle cream regret relationship stick test
51
+ crush endless fault itself rule spill art circle join kick mask master passion quick raise
52
+ smooth unless wander actually broke chair deal favorite gift note number sweat box chill clothes
53
+ lady mark park poor sadness tie animal belong brush consume dawn forest innocent pen pride
54
+ stream thick clay complete count draw faith press silver struggle surface taught teach wet bless
55
+ chase climb enter letter melt metal movie stretch swing vision wife beside crash forgot guide
56
+ haunt joke knock plant pour prove reveal steal stuff trip wood wrist bother bottom crawl
57
+ crowd fix forgive frown grace loose lucky party release surely survive teacher gently grip speed
58
+ suicide travel treat vein written cage chain conversation date enemy however interest million page pink
59
+ proud sway themselves winter church cruel cup demon experience freedom pair pop purpose respect shoot
60
+ softly state strange bar birth curl dirt excuse lord lovely monster order pack pants pool
61
+ scene seven shame slide ugly among blade blonde closet creek deny drug eternity gain grade
62
+ handle key linger pale prepare swallow swim tremble wheel won cast cigarette claim college direction
63
+ dirty gather ghost hundred loss lung orange present swear swirl twice wild bitter blanket doctor
64
+ everywhere flash grown knowledge numb pressure radio repeat ruin spend unknown buy clock devil early
65
+ false fantasy pound precious refuse sheet teeth welcome add ahead block bury caress content depth
66
+ despite distant marry purple threw whenever bomb dull easily grasp hospital innocence normal receive reply
67
+ rhyme shade someday sword toe visit asleep bought center consider flat hero history ink insane
68
+ muscle mystery pocket reflection shove silently smart soldier spot stress train type view whether bus
69
+ energy explain holy hunger inch magic mix noise nowhere prayer presence shock snap spider study
70
+ thunder trail admit agree bag bang bound butterfly cute exactly explode familiar fold further pierce
71
+ reflect scent selfish sharp sink spring stumble universe weep women wonderful action ancient attempt avoid
72
+ birthday branch chocolate core depress drunk especially focus fruit honest match palm perfectly pillow pity
73
+ poison roar shift slightly thump truck tune twenty unable wipe wrote coat constant dinner drove
74
+ egg eternal flight flood frame freak gasp glad hollow motion peer plastic root screen season
75
+ sting strike team unlike victim volume warn weird attack await awake built charm crave despair
76
+ fought grant grief horse limit message ripple sanity scatter serve split string trick annoy blur
77
+ boat brave clearly cling connect fist forth imagination iron jock judge lesson milk misery nail
78
+ naked ourselves poet possible princess sail size snake society stroke torture toss trace wise bloom
79
+ bullet cell check cost darling during footstep fragile hallway hardly horizon invisible journey midnight mud
80
+ nod pause relax shiver sudden value youth abuse admire blink breast bruise constantly couple creep
81
+ curve difference dumb emptiness gotta honor plain planet recall rub ship slam soar somebody tightly
82
+ weather adore approach bond bread burst candle coffee cousin crime desert flutter frozen grand heel
83
+ hello language level movement pleasure powerful random rhythm settle silly slap sort spoken steel threaten
84
+ tumble upset aside awkward bee blank board button card carefully complain crap deeply discover drag
85
+ dread effort entire fairy giant gotten greet illusion jeans leap liquid march mend nervous nine
86
+ replace rope spine stole terror accident apple balance boom childhood collect demand depression eventually faint
87
+ glare goal group honey kitchen laid limb machine mere mold murder nerve painful poetry prince
88
+ rabbit shelter shore shower soothe stair steady sunlight tangle tease treasure uncle begun bliss canvas
89
+ cheer claw clutch commit crimson crystal delight doll existence express fog football gay goose guard
90
+ hatred illuminate mass math mourn rich rough skip stir student style support thorn tough yard
91
+ yearn yesterday advice appreciate autumn bank beam bowl capture carve collapse confusion creation dove feather
92
+ girlfriend glory government harsh hop inner loser moonlight neighbor neither peach pig praise screw shield
93
+ shimmer sneak stab subject throughout thrown tower twirl wow army arrive bathroom bump cease cookie
94
+ couch courage dim guilt howl hum husband insult led lunch mock mostly natural nearly needle
95
+ nerd peaceful perfection pile price remove roam sanctuary serious shiny shook sob stolen tap vain
96
+ void warrior wrinkle affection apologize blossom bounce bridge cheap crumble decision descend desperately dig dot
97
+ flip frighten heartbeat huge lazy lick odd opinion process puzzle quietly retreat score sentence separate
98
+ situation skill soak square stray taint task tide underneath veil whistle anywhere bedroom bid bloody
99
+ burden careful compare concern curtain decay defeat describe double dreamer driver dwell evening flare flicker
100
+ grandma guitar harm horrible hungry indeed lace melody monkey nation object obviously rainbow salt scratch
101
+ shown shy stage stun third tickle useless weakness worship worthless afternoon beard boyfriend bubble busy
102
+ certain chin concrete desk diamond doom drawn due felicity freeze frost garden glide harmony hopefully
103
+ hunt jealous lightning mama mercy peel physical position pulse punch quit rant respond salty sane
104
+ satisfy savior sheep slept social sport tuck utter valley wolf aim alas alter arrow awaken
105
+ beaten belief brand ceiling cheese clue confidence connection daily disguise eager erase essence everytime expression
106
+ fan flag flirt foul fur giggle glorious ignorance law lifeless measure mighty muse north opposite
107
+ paradise patience patient pencil petal plate ponder possibly practice slice spell stock strife strip suffocate
108
+ suit tender tool trade velvet verse waist witch aunt bench bold cap certainly click companion
109
+ creator dart delicate determine dish dragon drama drum dude everybody feast forehead former fright fully
110
+ gas hook hurl invite juice manage moral possess raw rebel royal scale scary several slight
111
+ stubborn swell talent tea terrible thread torment trickle usually vast violence weave acid agony ashamed
112
+ awe belly blend blush character cheat common company coward creak danger deadly defense define depend
113
+ desperate destination dew duck dusty embarrass engine example explore foe freely frustrate generation glove guilty
114
+ health hurry idiot impossible inhale jaw kingdom mention mist moan mumble mutter observe ode pathetic
115
+ pattern pie prefer puff rape rare revenge rude scrape spiral squeeze strain sunset suspend sympathy
116
+ thigh throne total unseen weapon weary
117
+ TEXT
118
+
119
+ def self.encode(hex, words=Words)
120
+ n = words.size
121
+ [hex].pack("H*").unpack("N*").map{|x|
122
+ w1 = x % n
123
+ w2 = ((x / n) + w1) % n
124
+ w3 = ((x / n / n) + w2) % n
125
+ [ words[w1], words[w2], words[w3] ]
126
+ }.flatten
127
+ end
128
+
129
+ def self.decode(word_list, words=Words)
130
+ n = words.size
131
+ word_list.each_slice(3).map{|three_words|
132
+ w1, w2, w3 = three_words.map{|word| words.index(word) % n }
133
+ '%08x' % ( w1 + n*((w2-w1)%n) + n*n*((w3-w2)%n) )
134
+ }.join
135
+ end
136
+
137
+ end
138
+
139
+
140
+
141
+ if $0 == __FILE__
142
+ hex = "4c7d10656aa55383a5d88e3f63300af5e169918f4058bf349d99b20239909b61"
143
+ expected_words = ['horse', 'love', 'nose', 'speak', 'diamond', 'gaze', 'wash', 'drag', 'glance',
144
+ 'money', 'cease', 'soft', 'complete', 'huge', 'aside', 'confusion', 'touch',
145
+ 'grass', 'pie', 'play', 'bread', 'exactly', 'bubble', 'great']
146
+
147
+ # from http://brainwallet.org/#chains
148
+ hex = "ff64b72c431f75799f0c5ebe438e46dd"
149
+ expected_words = %w[muscle lot sea got revenge crack wait yeah gas study embrace spend]
150
+ hex = "18cfaf96961750c7a4e4e39c861d1415"
151
+ expected_words = %w[example poor twice expect decision master blame rub forward easy jump carve]
152
+
153
+ # from https://en.bitcoin.it/wiki/Electrum#Brain_Wallet
154
+ hex = "431a62f1c86555d3c45e5c4d9e10c8c7"
155
+ expected_words = %w[constant forest adore false green weave stop guy fur freeze giggle clock]
156
+
157
+ #p Mnemonic.encode(hex)
158
+ p Mnemonic.encode(hex) == expected_words
159
+ #p Mnemonic.decode(expected_words)
160
+ p Mnemonic.decode(expected_words) == hex
161
+ end
162
+
@@ -1,8 +1,9 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  # autoload when you need to re-generate a public_key from only its private_key.
2
4
  # ported from: https://github.com/sipa/bitcoin/blob/2d40fe4da9ea82af4b652b691a4185431d6e47a8/key.h
3
5
 
4
- Bitcoin.require_dependency :ffi, exit: false, message:
5
- "Skipping FFI needed for OpenSSL_EC.regenerate_key."
6
+ Bitcoin.require_dependency :ffi, exit: false, message: "Skipping FFI needed for OpenSSL_EC methods."
6
7
 
7
8
  module Bitcoin
8
9
  module OpenSSL_EC
@@ -10,32 +11,54 @@ module OpenSSL_EC
10
11
  ffi_lib 'ssl'
11
12
 
12
13
  NID_secp256k1 = 714
14
+ POINT_CONVERSION_COMPRESSED = 2
15
+ POINT_CONVERSION_UNCOMPRESSED = 4
13
16
 
14
17
  attach_function :SSL_library_init, [], :int
15
18
  attach_function :ERR_load_crypto_strings, [], :void
16
19
  attach_function :SSL_load_error_strings, [], :void
17
20
  attach_function :RAND_poll, [], :int
18
21
 
19
- #attach_function :BN_bin2bn, [:string, :int, :pointer], :pointer
22
+ attach_function :BN_CTX_free, [:pointer], :int
23
+ attach_function :BN_CTX_new, [], :pointer
24
+ attach_function :BN_add, [:pointer, :pointer, :pointer], :int
20
25
  attach_function :BN_bin2bn, [:pointer, :int, :pointer], :pointer
21
- attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
22
- attach_function :EC_KEY_get0_group, [:pointer], :pointer
26
+ attach_function :BN_bn2bin, [:pointer, :pointer], :void
27
+ attach_function :BN_cmp, [:pointer, :pointer], :int
28
+ attach_function :BN_copy, [:pointer, :pointer], :pointer
29
+ attach_function :BN_dup, [:pointer], :pointer
30
+ attach_function :BN_free, [:pointer], :int
31
+ attach_function :BN_mod_inverse, [:pointer, :pointer, :pointer, :pointer], :pointer
32
+ attach_function :BN_mod_mul, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
33
+ attach_function :BN_mod_sub, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
34
+ attach_function :BN_mul_word, [:pointer, :int], :int
23
35
  attach_function :BN_new, [], :pointer
24
- attach_function :BN_CTX_new, [], :pointer
36
+ attach_function :BN_rshift, [:pointer, :pointer, :int], :int
37
+ attach_function :BN_set_word, [:pointer, :int], :int
38
+ attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
39
+ attach_function :EC_GROUP_get_degree, [:pointer], :int
25
40
  attach_function :EC_GROUP_get_order, [:pointer, :pointer, :pointer], :int
26
- attach_function :EC_POINT_new, [:pointer], :pointer
27
- attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
41
+ attach_function :EC_KEY_free, [:pointer], :int
42
+ attach_function :EC_KEY_get0_group, [:pointer], :pointer
43
+ attach_function :EC_KEY_get0_private_key, [:pointer], :pointer
44
+ attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
45
+ attach_function :EC_KEY_set_conv_form, [:pointer, :int], :void
28
46
  attach_function :EC_KEY_set_private_key, [:pointer, :pointer], :int
29
47
  attach_function :EC_KEY_set_public_key, [:pointer, :pointer], :int
30
- attach_function :BN_free, [:pointer], :int
31
48
  attach_function :EC_POINT_free, [:pointer], :int
32
- attach_function :BN_CTX_free, [:pointer], :int
33
- attach_function :EC_KEY_free, [:pointer], :int
34
- attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
35
- attach_function :i2d_ECPrivateKey, [:pointer, :pointer], :int
49
+ attach_function :EC_POINT_is_at_infinity, [:pointer, :pointer], :int
50
+ attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
51
+ attach_function :EC_POINT_new, [:pointer], :pointer
52
+ attach_function :EC_POINT_set_compressed_coordinates_GFp, [:pointer, :pointer, :pointer, :int, :pointer], :int
36
53
  attach_function :d2i_ECPrivateKey, [:pointer, :pointer, :long], :pointer
37
- attach_function :EC_KEY_get0_private_key, [:pointer], :pointer
38
- attach_function :BN_bn2bin, [:pointer, :pointer], :void
54
+ attach_function :i2d_ECPrivateKey, [:pointer, :pointer], :int
55
+ attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
56
+ attach_function :EC_KEY_check_key, [:pointer], :uint
57
+ attach_function :ECDSA_do_sign, [:pointer, :uint, :pointer], :pointer
58
+ attach_function :BN_num_bits, [:pointer], :int
59
+ attach_function :ECDSA_SIG_free, [:pointer], :void
60
+
61
+ def self.BN_num_bytes(ptr); (BN_num_bits(ptr) + 7) / 8; end
39
62
 
40
63
 
41
64
  # resolve public from private key, using ffi and libssl.so
@@ -48,7 +71,7 @@ module OpenSSL_EC
48
71
  #private_key = FFI::MemoryPointer.new(:uint8, private_key.bytesize)
49
72
  # .put_bytes(0, private_key, 0, private_key.bytesize)
50
73
  private_key = FFI::MemoryPointer.from_string(private_key)
51
-
74
+
52
75
  init_ffi_ssl
53
76
  eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
54
77
  #priv_key = BN_bin2bn(private_key, private_key.size, BN_new())
@@ -69,10 +92,11 @@ module OpenSSL_EC
69
92
 
70
93
 
71
94
  length = i2d_ECPrivateKey(eckey, nil)
72
- ptr = FFI::MemoryPointer.new(:pointer)
95
+ buf = FFI::MemoryPointer.new(:uint8, length)
96
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
73
97
  priv_hex = if i2d_ECPrivateKey(eckey, ptr) == length
74
- size = ptr.read_pointer.get_array_of_uint8(8, 1)[0]
75
- ptr.read_pointer.get_array_of_uint8(9, size).pack("C*").rjust(32, "\x00").unpack("H*")[0]
98
+ size = buf.get_array_of_uint8(8, 1)[0]
99
+ buf.get_array_of_uint8(9, size).pack("C*").rjust(32, "\x00").unpack("H*")[0]
76
100
  #der_to_private_key( ptr.read_pointer.read_string(length).unpack("H*")[0] )
77
101
  end
78
102
 
@@ -82,9 +106,10 @@ module OpenSSL_EC
82
106
 
83
107
 
84
108
  length = i2o_ECPublicKey(eckey, nil)
85
- ptr = FFI::MemoryPointer.new(:pointer)
109
+ buf = FFI::MemoryPointer.new(:uint8, length)
110
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
86
111
  pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
87
- ptr.read_pointer.read_string(length).unpack("H*")[0]
112
+ buf.read_string(length).unpack("H*")[0]
88
113
  end
89
114
 
90
115
  EC_KEY_free(eckey)
@@ -109,6 +134,147 @@ module OpenSSL_EC
109
134
  buf.read_string(32).unpack("H*")[0]
110
135
  end
111
136
 
137
+ # Given the components of a signature and a selector value, recover and
138
+ # return the public key that generated the signature according to the
139
+ # algorithm in SEC1v2 section 4.1.6.
140
+ #
141
+ # rec_id is an index from 0 to 3 that indicates which of the 4 possible
142
+ # keys is the correct one. Because the key recovery operation yields
143
+ # multiple potential keys, the correct key must either be stored alongside
144
+ # the signature, or you must be willing to try each rec_id in turn until
145
+ # you find one that outputs the key you are expecting.
146
+ #
147
+ # If this method returns nil, it means recovery was not possible and rec_id
148
+ # should be iterated.
149
+ #
150
+ # Given the above two points, a correct usage of this method is inside a
151
+ # for loop from 0 to 3, and if the output is nil OR a key that is not the
152
+ # one you expect, you try again with the next rec_id.
153
+ #
154
+ # message_hash = hash of the signed message.
155
+ # signature = the R and S components of the signature, wrapped.
156
+ # rec_id = which possible key to recover.
157
+ # is_compressed = whether or not the original pubkey was compressed.
158
+ def self.recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
159
+ return nil if rec_id < 0 or signature.bytesize != 65
160
+ init_ffi_ssl
161
+
162
+ signature = FFI::MemoryPointer.from_string(signature)
163
+ #signature_bn = BN_bin2bn(signature, 65, BN_new())
164
+ r = BN_bin2bn(signature[1], 32, BN_new())
165
+ s = BN_bin2bn(signature[33], 32, BN_new())
166
+
167
+ n, i = 0, rec_id / 2
168
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
169
+
170
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed
171
+
172
+ group = EC_KEY_get0_group(eckey)
173
+ order = BN_new()
174
+ EC_GROUP_get_order(group, order, nil)
175
+ x = BN_dup(order)
176
+ BN_mul_word(x, i)
177
+ BN_add(x, x, r)
178
+
179
+ field = BN_new()
180
+ EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)
181
+
182
+ if BN_cmp(x, field) >= 0
183
+ [r, s, order, x, field].each{|i| BN_free(i) }
184
+ EC_KEY_free(eckey)
185
+ return nil
186
+ end
187
+
188
+ big_r = EC_POINT_new(group)
189
+ EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)
190
+
191
+ big_q = EC_POINT_new(group)
192
+ n = EC_GROUP_get_degree(group)
193
+ e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
194
+ BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n
195
+
196
+ ctx = BN_CTX_new()
197
+ zero, rr, sor, eor = BN_new(), BN_new(), BN_new(), BN_new()
198
+ BN_set_word(zero, 0)
199
+ BN_mod_sub(e, zero, e, order, ctx)
200
+ BN_mod_inverse(rr, r, order, ctx)
201
+ BN_mod_mul(sor, s, rr, order, ctx)
202
+ BN_mod_mul(eor, e, rr, order, ctx)
203
+ EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
204
+ EC_KEY_set_public_key(eckey, big_q)
205
+ BN_CTX_free(ctx)
206
+
207
+ [r, s, order, x, field, e, zero, rr, sor, eor].each{|i| BN_free(i) }
208
+ [big_r, big_q].each{|i| EC_POINT_free(i) }
209
+
210
+ length = i2o_ECPublicKey(eckey, nil)
211
+ buf = FFI::MemoryPointer.new(:uint8, length)
212
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
213
+ pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
214
+ buf.read_string(length).unpack("H*")[0]
215
+ end
216
+
217
+ EC_KEY_free(eckey)
218
+
219
+ pub_hex
220
+ end
221
+
222
+ def self.sign_compact(hash, private_key, public_key_hex = nil, pubkey_compressed = nil)
223
+ private_key = [private_key].pack("H*") if private_key.bytesize >= 64
224
+ private_key_hex = private_key.unpack("H*")[0]
225
+
226
+ public_key_hex = regenerate_key(private_key_hex).last unless public_key_hex
227
+ pubkey_compressed = (public_key_hex[0..1] == "04" ? false : true) unless pubkey_compressed
228
+
229
+ init_ffi_ssl
230
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
231
+ priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())
232
+
233
+ group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
234
+ EC_GROUP_get_order(group, order, ctx)
235
+
236
+ pub_key = EC_POINT_new(group)
237
+ EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
238
+ EC_KEY_set_private_key(eckey, priv_key)
239
+ EC_KEY_set_public_key(eckey, pub_key)
240
+
241
+ signature = ECDSA_do_sign(hash, hash.bytesize, eckey)
242
+
243
+ BN_free(order)
244
+ BN_CTX_free(ctx)
245
+ EC_POINT_free(pub_key)
246
+ BN_free(priv_key)
247
+ EC_KEY_free(eckey)
248
+
249
+ buf, rec_id, head = FFI::MemoryPointer.new(:uint8, 32), nil, nil
250
+ r, s = signature.get_array_of_pointer(0, 2).map{|i| BN_bn2bin(i, buf); buf.read_string(BN_num_bytes(i)).rjust(32, "\x00") }
251
+
252
+ if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 }
253
+ 4.times{|i|
254
+ head = [ 27 + i + (pubkey_compressed ? 4 : 0) ].pack("C")
255
+ if public_key_hex == recover_public_key_from_signature(hash, [head, r, s].join, i, pubkey_compressed)
256
+ rec_id = i; break
257
+ end
258
+ }
259
+ end
260
+
261
+ ECDSA_SIG_free(signature)
262
+
263
+ [ head, [r,s] ].join if rec_id
264
+ end
265
+
266
+ def self.recover_compact(hash, signature)
267
+ return false if signature.bytesize != 65
268
+ #i = signature.unpack("C")[0] - 27
269
+ #pubkey = recover_public_key_from_signature(hash, signature, (i & ~4), i >= 4)
270
+
271
+ version = signature.unpack('C')[0]
272
+ return false if version < 27 or version > 34
273
+
274
+ compressed = (version >= 31) ? (version -= 4; true) : false
275
+ pubkey = recover_public_key_from_signature(hash, signature, version-27, compressed)
276
+ end
277
+
112
278
  def self.init_ffi_ssl
113
279
  return if @ssl_loaded
114
280
  SSL_library_init()