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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/lib/ethereum.rb +53 -0
- data/lib/ethereum/abi.rb +333 -0
- data/lib/ethereum/abi/contract_translator.rb +174 -0
- data/lib/ethereum/abi/type.rb +117 -0
- data/lib/ethereum/account.rb +72 -0
- data/lib/ethereum/address.rb +60 -0
- data/lib/ethereum/base_convert.rb +53 -0
- data/lib/ethereum/block.rb +1311 -0
- data/lib/ethereum/block_header.rb +211 -0
- data/lib/ethereum/bloom.rb +83 -0
- data/lib/ethereum/cached_block.rb +36 -0
- data/lib/ethereum/chain.rb +400 -0
- data/lib/ethereum/constant.rb +26 -0
- data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
- data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
- data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
- data/lib/ethereum/core_ext/object/truth.rb +47 -0
- data/lib/ethereum/db.rb +6 -0
- data/lib/ethereum/db/base_db.rb +9 -0
- data/lib/ethereum/db/ephem_db.rb +63 -0
- data/lib/ethereum/db/overlay_db.rb +72 -0
- data/lib/ethereum/db/refcount_db.rb +188 -0
- data/lib/ethereum/env.rb +64 -0
- data/lib/ethereum/ethash.rb +78 -0
- data/lib/ethereum/ethash_ruby.rb +38 -0
- data/lib/ethereum/ethash_ruby/cache.rb +47 -0
- data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
- data/lib/ethereum/ethash_ruby/utils.rb +53 -0
- data/lib/ethereum/exceptions.rb +28 -0
- data/lib/ethereum/external_call.rb +173 -0
- data/lib/ethereum/fast_rlp.rb +81 -0
- data/lib/ethereum/fast_vm.rb +625 -0
- data/lib/ethereum/fast_vm/call_data.rb +44 -0
- data/lib/ethereum/fast_vm/message.rb +29 -0
- data/lib/ethereum/fast_vm/state.rb +25 -0
- data/lib/ethereum/ffi/openssl.rb +390 -0
- data/lib/ethereum/index.rb +97 -0
- data/lib/ethereum/log.rb +43 -0
- data/lib/ethereum/miner.rb +84 -0
- data/lib/ethereum/opcodes.rb +131 -0
- data/lib/ethereum/private_key.rb +92 -0
- data/lib/ethereum/pruning_trie.rb +28 -0
- data/lib/ethereum/public_key.rb +88 -0
- data/lib/ethereum/receipt.rb +53 -0
- data/lib/ethereum/secp256k1.rb +58 -0
- data/lib/ethereum/secure_trie.rb +49 -0
- data/lib/ethereum/sedes.rb +42 -0
- data/lib/ethereum/special_contract.rb +95 -0
- data/lib/ethereum/spv.rb +79 -0
- data/lib/ethereum/spv/proof.rb +31 -0
- data/lib/ethereum/spv/proof_constructor.rb +19 -0
- data/lib/ethereum/spv/proof_verifier.rb +24 -0
- data/lib/ethereum/tester.rb +14 -0
- data/lib/ethereum/tester/abi_contract.rb +65 -0
- data/lib/ethereum/tester/fixture.rb +31 -0
- data/lib/ethereum/tester/language.rb +30 -0
- data/lib/ethereum/tester/log_recorder.rb +13 -0
- data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
- data/lib/ethereum/tester/state.rb +194 -0
- data/lib/ethereum/transaction.rb +196 -0
- data/lib/ethereum/transient_trie.rb +28 -0
- data/lib/ethereum/trie.rb +549 -0
- data/lib/ethereum/trie/nibble_key.rb +184 -0
- data/lib/ethereum/utils.rb +191 -0
- data/lib/ethereum/version.rb +5 -0
- data/lib/ethereum/vm.rb +606 -0
- data/lib/ethereum/vm/call_data.rb +40 -0
- data/lib/ethereum/vm/message.rb +30 -0
- data/lib/ethereum/vm/state.rb +25 -0
- 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
|