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