ruby-ethereum 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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