ruby-ethereum 0.9.0

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 (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +40 -0
  4. data/lib/ethereum.rb +53 -0
  5. data/lib/ethereum/abi.rb +333 -0
  6. data/lib/ethereum/abi/contract_translator.rb +174 -0
  7. data/lib/ethereum/abi/type.rb +117 -0
  8. data/lib/ethereum/account.rb +72 -0
  9. data/lib/ethereum/address.rb +60 -0
  10. data/lib/ethereum/base_convert.rb +53 -0
  11. data/lib/ethereum/block.rb +1311 -0
  12. data/lib/ethereum/block_header.rb +211 -0
  13. data/lib/ethereum/bloom.rb +83 -0
  14. data/lib/ethereum/cached_block.rb +36 -0
  15. data/lib/ethereum/chain.rb +400 -0
  16. data/lib/ethereum/constant.rb +26 -0
  17. data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
  18. data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
  19. data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
  20. data/lib/ethereum/core_ext/object/truth.rb +47 -0
  21. data/lib/ethereum/db.rb +6 -0
  22. data/lib/ethereum/db/base_db.rb +9 -0
  23. data/lib/ethereum/db/ephem_db.rb +63 -0
  24. data/lib/ethereum/db/overlay_db.rb +72 -0
  25. data/lib/ethereum/db/refcount_db.rb +188 -0
  26. data/lib/ethereum/env.rb +64 -0
  27. data/lib/ethereum/ethash.rb +78 -0
  28. data/lib/ethereum/ethash_ruby.rb +38 -0
  29. data/lib/ethereum/ethash_ruby/cache.rb +47 -0
  30. data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
  31. data/lib/ethereum/ethash_ruby/utils.rb +53 -0
  32. data/lib/ethereum/exceptions.rb +28 -0
  33. data/lib/ethereum/external_call.rb +173 -0
  34. data/lib/ethereum/fast_rlp.rb +81 -0
  35. data/lib/ethereum/fast_vm.rb +625 -0
  36. data/lib/ethereum/fast_vm/call_data.rb +44 -0
  37. data/lib/ethereum/fast_vm/message.rb +29 -0
  38. data/lib/ethereum/fast_vm/state.rb +25 -0
  39. data/lib/ethereum/ffi/openssl.rb +390 -0
  40. data/lib/ethereum/index.rb +97 -0
  41. data/lib/ethereum/log.rb +43 -0
  42. data/lib/ethereum/miner.rb +84 -0
  43. data/lib/ethereum/opcodes.rb +131 -0
  44. data/lib/ethereum/private_key.rb +92 -0
  45. data/lib/ethereum/pruning_trie.rb +28 -0
  46. data/lib/ethereum/public_key.rb +88 -0
  47. data/lib/ethereum/receipt.rb +53 -0
  48. data/lib/ethereum/secp256k1.rb +58 -0
  49. data/lib/ethereum/secure_trie.rb +49 -0
  50. data/lib/ethereum/sedes.rb +42 -0
  51. data/lib/ethereum/special_contract.rb +95 -0
  52. data/lib/ethereum/spv.rb +79 -0
  53. data/lib/ethereum/spv/proof.rb +31 -0
  54. data/lib/ethereum/spv/proof_constructor.rb +19 -0
  55. data/lib/ethereum/spv/proof_verifier.rb +24 -0
  56. data/lib/ethereum/tester.rb +14 -0
  57. data/lib/ethereum/tester/abi_contract.rb +65 -0
  58. data/lib/ethereum/tester/fixture.rb +31 -0
  59. data/lib/ethereum/tester/language.rb +30 -0
  60. data/lib/ethereum/tester/log_recorder.rb +13 -0
  61. data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
  62. data/lib/ethereum/tester/state.rb +194 -0
  63. data/lib/ethereum/transaction.rb +196 -0
  64. data/lib/ethereum/transient_trie.rb +28 -0
  65. data/lib/ethereum/trie.rb +549 -0
  66. data/lib/ethereum/trie/nibble_key.rb +184 -0
  67. data/lib/ethereum/utils.rb +191 -0
  68. data/lib/ethereum/version.rb +5 -0
  69. data/lib/ethereum/vm.rb +606 -0
  70. data/lib/ethereum/vm/call_data.rb +40 -0
  71. data/lib/ethereum/vm/message.rb +30 -0
  72. data/lib/ethereum/vm/state.rb +25 -0
  73. metadata +284 -0
@@ -0,0 +1,44 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class FastVM
5
+
6
+ class CallData
7
+
8
+ attr :size
9
+
10
+ def initialize(parent_memory, offset=0, size=nil)
11
+ @data = parent_memory
12
+ @offset = offset
13
+ @size = size || @data.size
14
+ @rlimit = @offset + @size
15
+ end
16
+
17
+ def extract_all
18
+ d = @data.safe_slice(@offset, @size)
19
+ d += [0] * (@size - d.size)
20
+ Utils.int_array_to_bytes(d)
21
+ end
22
+
23
+ def extract32(i)
24
+ return 0 if i >= @size
25
+
26
+ right = [@offset+i+32, @rlimit].min
27
+ o = @data.safe_slice(@offset+i...right)
28
+ Utils.bytearray_to_int(o + [0]*(32-o.size))
29
+ end
30
+
31
+ def extract_copy(mem, memstart, datastart, size)
32
+ [size, @size-datastart].min.times do |i|
33
+ mem[memstart+i] = @data[@offset + datastart + i]
34
+ end
35
+
36
+ ([0, [size, @size-datastart].min].max...size).each do |i|
37
+ mem[memstart+i] = 0
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class FastVM
5
+
6
+ class Message
7
+
8
+ attr_accessor :sender, :to, :value, :gas, :data, :depth, :logs, :code_address, :is_create
9
+
10
+ def initialize(sender, to, value, gas, data, depth:0, code_address:nil, is_create:false)
11
+ @sender = sender
12
+ @to = to
13
+ @value = value
14
+ @gas = gas
15
+ @data = data
16
+ @depth = depth
17
+ @logs = []
18
+ @code_address = code_address
19
+ @is_create = is_create
20
+ end
21
+
22
+ def to_s
23
+ "#<#{self.class.name}:#{object_id} to=#{@to[0,8]}>"
24
+ end
25
+ alias :inspect :to_s
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class FastVM
5
+ class State
6
+
7
+ attr_accessor :memory, :stack, :pc, :gas
8
+
9
+ def initialize(**kwargs)
10
+ @memory = []
11
+ @stack = []
12
+ @pc = 0
13
+ @gas = 0
14
+
15
+ kwargs.each do |k,v|
16
+ class <<self
17
+ self
18
+ end.class_eval("attr_accessor :#{k}")
19
+ send :"#{k}=", v
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,390 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'ffi'
4
+
5
+ module Ethereum
6
+
7
+ ##
8
+ # bindings for elliptic curve inside OpenSSL
9
+ #
10
+ # @see # https://github.com/lian/bitcoin-ruby/blob/master/lib/bitcoin/ffi/openssl.rb
11
+ #
12
+ module OpenSSL_EC
13
+ extend FFI::Library
14
+ if FFI::Platform.windows?
15
+ ffi_lib 'libeay32', 'ssleay32'
16
+ else
17
+ ffi_lib 'ssl'
18
+ end
19
+
20
+ NID_secp256k1 = 714
21
+ POINT_CONVERSION_COMPRESSED = 2
22
+ POINT_CONVERSION_UNCOMPRESSED = 4
23
+
24
+ attach_function :SSL_library_init, [], :int
25
+ attach_function :ERR_load_crypto_strings, [], :void
26
+ attach_function :SSL_load_error_strings, [], :void
27
+ attach_function :RAND_poll, [], :int
28
+
29
+ attach_function :BN_CTX_free, [:pointer], :int
30
+ attach_function :BN_CTX_new, [], :pointer
31
+ attach_function :BN_add, [:pointer, :pointer, :pointer], :int
32
+ attach_function :BN_bin2bn, [:pointer, :int, :pointer], :pointer
33
+ attach_function :BN_bn2bin, [:pointer, :pointer], :int
34
+ attach_function :BN_cmp, [:pointer, :pointer], :int
35
+ attach_function :BN_copy, [:pointer, :pointer], :pointer
36
+ attach_function :BN_dup, [:pointer], :pointer
37
+ attach_function :BN_free, [:pointer], :int
38
+ attach_function :BN_mod_inverse, [:pointer, :pointer, :pointer, :pointer], :pointer
39
+ attach_function :BN_mod_mul, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
40
+ attach_function :BN_mod_sub, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
41
+ attach_function :BN_mul_word, [:pointer, :int], :int
42
+ attach_function :BN_new, [], :pointer
43
+ attach_function :BN_rshift, [:pointer, :pointer, :int], :int
44
+ attach_function :BN_rshift1, [:pointer, :pointer], :int
45
+ attach_function :BN_set_word, [:pointer, :int], :int
46
+ attach_function :BN_sub, [:pointer, :pointer, :pointer], :int
47
+ attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
48
+ attach_function :EC_GROUP_get_degree, [:pointer], :int
49
+ attach_function :EC_GROUP_get_order, [:pointer, :pointer, :pointer], :int
50
+ attach_function :EC_KEY_free, [:pointer], :int
51
+ attach_function :EC_KEY_get0_group, [:pointer], :pointer
52
+ attach_function :EC_KEY_get0_private_key, [:pointer], :pointer
53
+ attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
54
+ attach_function :EC_KEY_set_conv_form, [:pointer, :int], :void
55
+ attach_function :EC_KEY_set_private_key, [:pointer, :pointer], :int
56
+ attach_function :EC_KEY_set_public_key, [:pointer, :pointer], :int
57
+ attach_function :EC_POINT_free, [:pointer], :int
58
+ attach_function :EC_POINT_is_at_infinity, [:pointer, :pointer], :int
59
+ attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
60
+ attach_function :EC_POINT_new, [:pointer], :pointer
61
+ attach_function :EC_POINT_set_compressed_coordinates_GFp, [:pointer, :pointer, :pointer, :int, :pointer], :int
62
+ attach_function :d2i_ECPrivateKey, [:pointer, :pointer, :long], :pointer
63
+ attach_function :i2d_ECPrivateKey, [:pointer, :pointer], :int
64
+ attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
65
+ attach_function :EC_KEY_check_key, [:pointer], :uint
66
+ attach_function :ECDSA_do_sign, [:pointer, :uint, :pointer], :pointer
67
+ attach_function :BN_num_bits, [:pointer], :int
68
+ attach_function :ECDSA_SIG_free, [:pointer], :void
69
+ attach_function :EC_POINT_add, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
70
+ attach_function :EC_POINT_point2hex, [:pointer, :pointer, :int, :pointer], :string
71
+ attach_function :EC_POINT_hex2point, [:pointer, :string, :pointer, :pointer], :pointer
72
+ attach_function :ECDSA_SIG_new, [], :pointer
73
+ attach_function :d2i_ECDSA_SIG, [:pointer, :pointer, :long], :pointer
74
+ attach_function :i2d_ECDSA_SIG, [:pointer, :pointer], :int
75
+ attach_function :OPENSSL_free, :CRYPTO_free, [:pointer], :void
76
+
77
+ def self.BN_num_bytes(ptr); (BN_num_bits(ptr) + 7) / 8; end
78
+
79
+
80
+ # resolve public from private key, using ffi and libssl.so
81
+ # example:
82
+ # keypair = Bitcoin.generate_key; Bitcoin::OpenSSL_EC.regenerate_key(keypair.first) == keypair
83
+ def self.regenerate_key(private_key)
84
+ private_key = [private_key].pack("H*") if private_key.bytesize >= (32*2)
85
+ private_key_hex = private_key.unpack("H*")[0]
86
+
87
+ #private_key = FFI::MemoryPointer.new(:uint8, private_key.bytesize)
88
+ # .put_bytes(0, private_key, 0, private_key.bytesize)
89
+ private_key = FFI::MemoryPointer.from_string(private_key)
90
+
91
+ init_ffi_ssl
92
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
93
+ #priv_key = BN_bin2bn(private_key, private_key.size, BN_new())
94
+ priv_key = BN_bin2bn(private_key, private_key.size-1, BN_new())
95
+
96
+ group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
97
+ EC_GROUP_get_order(group, order, ctx)
98
+
99
+ pub_key = EC_POINT_new(group)
100
+ EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
101
+ EC_KEY_set_private_key(eckey, priv_key)
102
+ EC_KEY_set_public_key(eckey, pub_key)
103
+
104
+ BN_free(order)
105
+ BN_CTX_free(ctx)
106
+ EC_POINT_free(pub_key)
107
+ BN_free(priv_key)
108
+
109
+
110
+ length = i2d_ECPrivateKey(eckey, nil)
111
+ buf = FFI::MemoryPointer.new(:uint8, length)
112
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
113
+ priv_hex = if i2d_ECPrivateKey(eckey, ptr) == length
114
+ size = buf.get_array_of_uint8(8, 1)[0]
115
+ buf.get_array_of_uint8(9, size).pack("C*").rjust(32, "\x00").unpack("H*")[0]
116
+ #der_to_private_key( ptr.read_pointer.read_string(length).unpack("H*")[0] )
117
+ end
118
+
119
+ if priv_hex != private_key_hex
120
+ raise "regenerated wrong private_key, raise here before generating a faulty public_key too!"
121
+ end
122
+
123
+
124
+ length = i2o_ECPublicKey(eckey, nil)
125
+ buf = FFI::MemoryPointer.new(:uint8, length)
126
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
127
+ pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
128
+ buf.read_string(length).unpack("H*")[0]
129
+ end
130
+
131
+ EC_KEY_free(eckey)
132
+
133
+ [ priv_hex, pub_hex ]
134
+ end
135
+
136
+ # extract private key from uncompressed DER format
137
+ def self.der_to_private_key(der_hex)
138
+ init_ffi_ssl
139
+ #k = EC_KEY_new_by_curve_name(NID_secp256k1)
140
+ #kp = FFI::MemoryPointer.new(:pointer).put_pointer(0, eckey)
141
+
142
+ buf = FFI::MemoryPointer.from_string([der_hex].pack("H*"))
143
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
144
+
145
+ #ec_key = d2i_ECPrivateKey(kp, ptr, buf.size-1)
146
+ ec_key = d2i_ECPrivateKey(nil, ptr, buf.size-1)
147
+ return nil if ec_key.null?
148
+ bn = EC_KEY_get0_private_key(ec_key)
149
+ BN_bn2bin(bn, buf)
150
+ buf.read_string(32).unpack("H*")[0]
151
+ end
152
+
153
+ # Given the components of a signature and a selector value, recover and
154
+ # return the public key that generated the signature according to the
155
+ # algorithm in SEC1v2 section 4.1.6.
156
+ #
157
+ # rec_id is an index from 0 to 3 that indicates which of the 4 possible
158
+ # keys is the correct one. Because the key recovery operation yields
159
+ # multiple potential keys, the correct key must either be stored alongside
160
+ # the signature, or you must be willing to try each rec_id in turn until
161
+ # you find one that outputs the key you are expecting.
162
+ #
163
+ # If this method returns nil, it means recovery was not possible and rec_id
164
+ # should be iterated.
165
+ #
166
+ # Given the above two points, a correct usage of this method is inside a
167
+ # for loop from 0 to 3, and if the output is nil OR a key that is not the
168
+ # one you expect, you try again with the next rec_id.
169
+ #
170
+ # message_hash = hash of the signed message.
171
+ # signature = the R and S components of the signature, wrapped.
172
+ # rec_id = which possible key to recover.
173
+ # is_compressed = whether or not the original pubkey was compressed.
174
+ def self.recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
175
+ return nil if rec_id < 0 or signature.bytesize != 65
176
+ init_ffi_ssl
177
+
178
+ signature = FFI::MemoryPointer.from_string(signature)
179
+ #signature_bn = BN_bin2bn(signature, 65, BN_new())
180
+ r = BN_bin2bn(signature[1], 32, BN_new())
181
+ s = BN_bin2bn(signature[33], 32, BN_new())
182
+
183
+ n, i = 0, rec_id / 2
184
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
185
+
186
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed
187
+
188
+ group = EC_KEY_get0_group(eckey)
189
+ order = BN_new()
190
+ EC_GROUP_get_order(group, order, nil)
191
+ x = BN_dup(order)
192
+ BN_mul_word(x, i)
193
+ BN_add(x, x, r)
194
+
195
+ field = BN_new()
196
+ EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)
197
+
198
+ if BN_cmp(x, field) >= 0
199
+ [r, s, order, x, field].each{|i| BN_free(i) }
200
+ EC_KEY_free(eckey)
201
+ return nil
202
+ end
203
+
204
+ big_r = EC_POINT_new(group)
205
+ EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)
206
+
207
+ big_q = EC_POINT_new(group)
208
+ n = EC_GROUP_get_degree(group)
209
+ e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
210
+ BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n
211
+
212
+ ctx = BN_CTX_new()
213
+ zero, rr, sor, eor = BN_new(), BN_new(), BN_new(), BN_new()
214
+ BN_set_word(zero, 0)
215
+ BN_mod_sub(e, zero, e, order, ctx)
216
+ BN_mod_inverse(rr, r, order, ctx)
217
+ BN_mod_mul(sor, s, rr, order, ctx)
218
+ BN_mod_mul(eor, e, rr, order, ctx)
219
+ EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
220
+ EC_KEY_set_public_key(eckey, big_q)
221
+ BN_CTX_free(ctx)
222
+
223
+ [r, s, order, x, field, e, zero, rr, sor, eor].each{|i| BN_free(i) }
224
+ [big_r, big_q].each{|i| EC_POINT_free(i) }
225
+
226
+ length = i2o_ECPublicKey(eckey, nil)
227
+ buf = FFI::MemoryPointer.new(:uint8, length)
228
+ ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
229
+ pub_hex = if i2o_ECPublicKey(eckey, ptr) == length
230
+ buf.read_string(length).unpack("H*")[0]
231
+ end
232
+
233
+ EC_KEY_free(eckey)
234
+
235
+ pub_hex
236
+ end
237
+
238
+ # Regenerate a DER-encoded signature such that the S-value complies with the BIP62
239
+ # specification.
240
+ #
241
+ def self.signature_to_low_s(signature)
242
+ init_ffi_ssl
243
+
244
+ buf = FFI::MemoryPointer.new(:uint8, 34)
245
+ temp = signature.unpack("C*")
246
+ length_r = temp[3]
247
+ length_s = temp[5+length_r]
248
+ sig = FFI::MemoryPointer.from_string(signature)
249
+
250
+ # Calculate the lower s value
251
+ s = BN_bin2bn(sig[6 + length_r], length_s, BN_new())
252
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
253
+ group, order, halforder, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_new(), BN_CTX_new()
254
+
255
+ EC_GROUP_get_order(group, order, ctx)
256
+ BN_rshift1(halforder, order)
257
+ if BN_cmp(s, halforder) > 0
258
+ BN_sub(s, order, s)
259
+ end
260
+
261
+ BN_free(halforder)
262
+ BN_free(order)
263
+ BN_CTX_free(ctx)
264
+
265
+ length_s = BN_bn2bin(s, buf)
266
+ # p buf.read_string(length_s).unpack("H*")
267
+
268
+ # Re-encode the signature in DER format
269
+ sig = [0x30, 0, 0x02, length_r]
270
+ sig.concat(temp.slice(4, length_r))
271
+ sig << 0x02
272
+ sig << length_s
273
+ sig.concat(buf.read_string(length_s).unpack("C*"))
274
+ sig[1] = sig.size - 2
275
+
276
+ BN_free(s)
277
+ EC_KEY_free(eckey)
278
+
279
+ sig.pack("C*")
280
+ end
281
+
282
+ def self.sign_compact(hash, private_key, public_key_hex = nil, pubkey_compressed = nil)
283
+ private_key = [private_key].pack("H*") if private_key.bytesize >= 64
284
+ private_key_hex = private_key.unpack("H*")[0]
285
+
286
+ public_key_hex = regenerate_key(private_key_hex).last unless public_key_hex
287
+ pubkey_compressed = (public_key_hex[0..1] == "04" ? false : true) unless pubkey_compressed
288
+
289
+ init_ffi_ssl
290
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
291
+ priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())
292
+
293
+ group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
294
+ EC_GROUP_get_order(group, order, ctx)
295
+
296
+ pub_key = EC_POINT_new(group)
297
+ EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
298
+ EC_KEY_set_private_key(eckey, priv_key)
299
+ EC_KEY_set_public_key(eckey, pub_key)
300
+
301
+ signature = ECDSA_do_sign(hash, hash.bytesize, eckey)
302
+
303
+ BN_free(order)
304
+ BN_CTX_free(ctx)
305
+ EC_POINT_free(pub_key)
306
+ BN_free(priv_key)
307
+ EC_KEY_free(eckey)
308
+
309
+ buf, rec_id, head = FFI::MemoryPointer.new(:uint8, 32), nil, nil
310
+ 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") }
311
+
312
+ if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 }
313
+ 4.times{|i|
314
+ head = [ 27 + i + (pubkey_compressed ? 4 : 0) ].pack("C")
315
+ if public_key_hex == recover_public_key_from_signature(hash, [head, r, s].join, i, pubkey_compressed)
316
+ rec_id = i; break
317
+ end
318
+ }
319
+ end
320
+
321
+ ECDSA_SIG_free(signature)
322
+
323
+ [ head, [r,s] ].join if rec_id
324
+ end
325
+
326
+ def self.recover_compact(hash, signature)
327
+ return false if signature.bytesize != 65
328
+ #i = signature.unpack("C")[0] - 27
329
+ #pubkey = recover_public_key_from_signature(hash, signature, (i & ~4), i >= 4)
330
+
331
+ version = signature.unpack('C')[0]
332
+ return false if version < 27 or version > 34
333
+
334
+ compressed = (version >= 31) ? (version -= 4; true) : false
335
+ pubkey = recover_public_key_from_signature(hash, signature, version-27, compressed)
336
+ end
337
+
338
+ # lifted from https://github.com/GemHQ/money-tree
339
+ def self.ec_add(point_0, point_1)
340
+ init_ffi_ssl
341
+
342
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
343
+ group = EC_KEY_get0_group(eckey)
344
+
345
+ point_0_hex = point_0.to_bn.to_s(16)
346
+ point_0_pt = EC_POINT_hex2point(group, point_0_hex, nil, nil)
347
+ point_1_hex = point_1.to_bn.to_s(16)
348
+ point_1_pt = EC_POINT_hex2point(group, point_1_hex, nil, nil)
349
+
350
+ sum_point = EC_POINT_new(group)
351
+ success = EC_POINT_add(group, sum_point, point_0_pt, point_1_pt, nil)
352
+ hex = EC_POINT_point2hex(group, sum_point, POINT_CONVERSION_UNCOMPRESSED, nil)
353
+ EC_KEY_free(eckey)
354
+ EC_POINT_free(sum_point)
355
+ hex
356
+ end
357
+
358
+ # repack signature for OpenSSL 1.0.1k handling of DER signatures
359
+ # https://github.com/bitcoin/bitcoin/pull/5634/files
360
+ def self.repack_der_signature(signature)
361
+ init_ffi_ssl
362
+
363
+ return false if signature.empty?
364
+
365
+ # New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first.
366
+ norm_der = FFI::MemoryPointer.new(:pointer)
367
+ sig_ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, FFI::MemoryPointer.from_string(signature))
368
+
369
+ norm_sig = d2i_ECDSA_SIG(nil, sig_ptr, signature.bytesize)
370
+
371
+ derlen = i2d_ECDSA_SIG(norm_sig, norm_der)
372
+ ECDSA_SIG_free(norm_sig)
373
+ return false if derlen <= 0
374
+
375
+ ret = norm_der.read_pointer.read_string(derlen)
376
+ OPENSSL_free(norm_der.read_pointer)
377
+
378
+ ret
379
+ end
380
+
381
+ def self.init_ffi_ssl
382
+ return if @ssl_loaded
383
+ SSL_library_init()
384
+ ERR_load_crypto_strings()
385
+ SSL_load_error_strings()
386
+ RAND_poll()
387
+ @ssl_loaded = true
388
+ end
389
+ end
390
+ end